The language config
The Language config page is your personal copy of the JSON file the app is built around. It controls which languages appear in the options menu, how the teacher behaves in each, and your own flash-card decks. The Meta Quest downloads it when the app launches (restart the app to apply changes). Every save is validated first — invalid edits are rejected with a message pointing at the problem, nothing broken ever reaches the Meta Quest, and Reset to default always restores the app's built-in config.
The file has three top-level sections:
{
"Prompts": { ... the prompts, written once and shared by every language ... },
"languages": [ ... one entry per language in the picker ... ],
"flashDecks": [ ... optional hand-made decks ... ]
}
Prompts
The prompts live here once, not per language. Anything that genuinely differs between languages is a placeholder the app fills in — so one well-written teacher prompt serves all of them, and adding a language does not mean rewriting it.
| Field | What it does |
|---|---|
teacherDesc required | The teacher's system prompt — her personality, how much target language vs. English she uses, and any house rules. She answers as JSON with an emotion and her line; keep that part intact, and keep it strict JSON with double quotes. |
suggestionDesc / suggestionDesc2 | Prompts for the two reply suggestions offered after each teacher line (typically one safe, one adventurous). |
analysisDesc / analysisDesc2 | Prompts for the analysis panel — how sentences get broken down and explained. |
quizSystemPrompt required | Generates multiple-choice quiz questions. Keep the format rules intact — the app parses the output. |
wordOrderQuizPrompt | Generates word-order questions, the other test type on the quiz's start page. Leave it out and that choice falls back to multiple choice. |
quizExplainPrompt required | Prompt for the explanation shown when you miss a question. |
flashCardsPrompt required | Prompt that generates Auto and Standard flash decks. |
Languages
Each entry in languages defines one tile in the language picker. Note there
are no prompts here — only the things that really are per-language:
| Field | What it does |
|---|---|
index | Its position in the array (0, 1, 2, …). Must match — decks refer to languages by this number. |
languageCode | Two-letter ISO code ("es", "zh", "ko"…). Tells speech synthesis and recognition which language to expect. |
languageName | The name shown in the dropdown. |
elevenLabsID | The ElevenLabs voice id for the teacher (8–64 letters/digits — copy it from your ElevenLabs voice library). |
fontIndex | Which of the app's bundled fonts renders this language (0–31). Needed for scripts like Chinese, Korean, or Devanagari. For a language you add yourself, use -1 (or any number outside the bundled range): the app then uses its universal font, which renders Latin, Cyrillic, Greek, Chinese, Japanese, Korean, and Devanagari scripts on demand. Right-to-left scripts (Arabic, Hebrew) are not supported yet. |
aiOption | Which chat provider powers this language: chatgpt, deepseek, claude, grok, gemini, or mistral. You need that provider's key saved on the API keys page. |
transliteration | How this language writes pronunciation — "pinyin", "romaji", "romanization", or for a Latin-alphabet language something like "a simple English-style phonetic respelling like \"may-ZOHN\" — never IPA". This is what {TRANSLITERATION} becomes. |
quizCorrect / quizWrong | The short phrases shown for right and wrong answers, in this language. |
levels | The Difficulty dropdown for this language, e.g. ["N5", "N4", "N3"]. At least one required. |
Inside any prompt you can use placeholders that the app fills in at runtime — this is what lets one shared prompt serve every language:
{LANGUAGE} | The language being taught. Use this rather than naming a language — a prompt that says "You are a Korean teacher" stays Korean whichever tile you pick. |
{LEVEL} | The selected difficulty, from that language's levels. |
{TOPIC} | The current conversation topic. |
{TRANSLITERATION} | That language's transliteration — pinyin, romaji, a phonetic respelling. Use it anywhere you would otherwise write "pinyin". |
{WORDS} | The words on the active flash deck, so tests cover what you are studying. Quiz and flash-card prompts. |
{COUNT} | How many questions the test asked for. Quiz prompts — the app steps through exactly this many. |
{EXPLANATION_LANGUAGE} | The language explanations are written in. quizExplainPrompt only. |
Flash decks
Entries in flashDecks appear in the Test & Cards dropdown after
(None), Auto (AI), and Standard:
{
"name": "Directions",
"languageIndex": 1,
"cards": [
{ "character": "北", "transliteration": "běi", "translation": "north" },
{ "character": "南", "transliteration": "nán", "translation": "south" }
]
}
name— the dropdown label.languageIndex— theindexof the language this deck belongs to. The deck only shows while that language is selected, and it inherits the language's font.cards— 1 to 500 cards, dealt in full and in order.character(the word being studied) is required;transliteration(the pronunciation) andtranslationare optional and shown on the back of the card.
Optional: a hand-written test
Normally the quiz generates questions from the deck's words. Add a quiz
list to a deck to use your own fixed questions instead. Each entry is one question:
the question line, then the options, each wrapped in a link tag that marks it right or
wrong (that tag is also what makes the option tappable in VR):
"quiz": [
"Which direction is 'north'?\n<link=\"correct\">A. 北</link>\n<link=\"wrong\">B. 南</link>\n<link=\"wrong\">C. 西</link>\n<link=\"wrong\">D. 东</link>"
]
For put-these-in-order questions, number the links by each option's correct position
instead: <link="1"> through <link="4">. Up to
100 questions per deck; the test runs through all of them.
Saving and applying
- Validate & save checks the whole file first: JSON syntax
(including misspelled field names, which are rejected rather than silently
ignored), required fields, and cross-references like
languageIndex. Errors list the exact entry and field. - Each save bumps your config's version number. The Meta Quest checks the version at launch and only re-downloads when it changed — so restart the app to pick up an edit.
- If the Meta Quest is unlinked (or your config can't be read), the app falls back to its built-in config — you can't lock yourself out.