.translations.json Reference
.translations.json declares one or more translation projects in a repo, each with its own locales directory and source language.
The file is optional. By default the wizard saves your project config to local app storage (see Repo Onboarding). .translations.json only appears at the repo root if you opt in to the Export to repo flow to share defaults with teammates. The schema below is the source of truth for both shapes.
Minimal example
Section titled “Minimal example”The shape produced by Export to repo (and the shape an imported .translations.json must conform to):
{ "projects": [ { "name": "web", "localesDir": "apps/web/locales", "sourceLanguage": "en" } ]}Top-level fields
Section titled “Top-level fields”| Field | Required | Type | Description |
|---|---|---|---|
$schema | no | string | Optional advisory schema URL. Validation is by Zod, not the JSON Schema. |
projects | yes | Project[] (≥ 1) | Every translation project the repo ships. |
aiSystemPrompt | no | string (≤ 2000 chars) | Repo-scoped guidance prepended to every AI translation request from this repo. Use it for tone, glossary, brand-name handling. |
Unknown top-level fields are preserved silently (forward compatibility — the schema uses passthrough() at every level).
Project fields
Section titled “Project fields”| Field | Required | Default | Notes |
|---|---|---|---|
name | yes | — | Human label, unique within the file. Shown in the UI. |
localesDir | yes | — | Path relative to the repo root (e.g. apps/web/locales). |
sourceLanguage | yes | — | The language used by developers in code (e.g. en). |
fileStructure | no | {lang}/{namespace}.json | MVP supports only this value. |
format.type | no | json | Serialization format. Only json is supported in MVP. |
format.nested | no | false | If true, keys nest into objects. If false, flat with keySeparator. |
format.keySeparator | no | . | Separator for flat keys (matches i18next default). |
plurals.enabled | no | true | Toggle plural awareness in the editor. |
plurals.suffixes | no | i18next CLDR (see below) | The set of plural suffixes the editor recognizes. |
validation.placeholders | no | ["{{name}}", "{name}"] | Placeholder formats checked for round-trip presence. |
validation.allowHtml | no | true | If false, target translations with HTML tags are flagged. |
branchPrefix | no | translations/ | Prefix for new translation branches created by the app. |
languageNames | no | {} | Human labels per language code (e.g. "uk": "Українська"). |
languageBcp47 | no | — | Maps folder names to BCP 47 tags (absent unless set). Use when your folder names aren’t BCP 47 (e.g. ua → uk-UA, cn → zh-CN). The mapped tag is used as the lang attribute on cell textareas to drive correct spell-check. Each value is validated against /^[A-Za-z][A-Za-z0-9]{1,7}(?:-[A-Za-z0-9]{2,8})*$/. |
Unknown per-project fields are preserved silently.
Language autodetection
Section titled “Language autodetection”You do not declare languages in .translations.json. The app scans <localesDir>/* and treats every subfolder matching the BCP 47 pattern as a language:
apps/web/locales/ en/ ← detected uk/ ← detected pt-BR/ ← detected _archive/ ← ignored (doesn't match pattern) README.md ← ignored (not a directory)To add a new language, create the folder. No config change needed.
Default plural suffixes
Section titled “Default plural suffixes”The defaults match i18next CLDR conventions:
["_zero", "_one", "_two", "_few", "_many", "_other"]Override with plurals.suffixes if your codebase uses different conventions.
Multi-project example
Section titled “Multi-project example”{ "$schema": "https://stape.io/schemas/translations-v1.json", "projects": [ { "name": "web", "localesDir": "apps/web/locales", "sourceLanguage": "en", "branchPrefix": "translations/web/" }, { "name": "marketing-site", "localesDir": "apps/marketing/i18n", "sourceLanguage": "en", "languageNames": { "uk": "Українська", "pt-BR": "Português (Brasil)" } } ]}AI prompt and BCP 47 overrides
Section titled “AI prompt and BCP 47 overrides”Combine the top-level aiSystemPrompt with per-project languageBcp47 overrides when your folder names don’t follow BCP 47:
{ "aiSystemPrompt": "Use formal Ukrainian (вживайте «Ви»). Brand name 'Stape' stays in English — never transliterate.", "projects": [ { "name": "web", "localesDir": "apps/web/locales", "sourceLanguage": "en", "languageBcp47": { "ua": "uk-UA", "cn": "zh-CN" } } ]}The bcp47For(project, folder) helper returns the mapped tag, or the folder name itself if no mapping exists. The result drives the lang attribute on cell textareas so the OS picks the right spell-check dictionary.
Validation
Section titled “Validation”The schema is enforced with Zod on app load. Field-level errors surface with JSON-pointer paths (e.g. projects[0].localesDir) so you can jump straight to the offending key. Unknown fields pass through silently.
Bootstrap wizard
Section titled “Bootstrap wizard”The first time you open a repo, the in-app wizard collects the required fields and saves them to local app storage, not to the working tree. Adding a repo never produces stray files in git status.
If your team wants to share project defaults — so a teammate’s first run can skip the wizard — the wizard exposes an opt-in Export to repo as .translations.json action. It writes the file into the working tree as an unstaged change; commit and push it through your normal git flow. See Repo onboarding for the full export story.