Translations
Page text for the 23 non-English languages lives here. English stays in the HTML source; the build swaps in translations per language.
Files
source.json: every English text block found on the built pages. The key is a hash of the text, so changing an English sentence makes its old translations stop matching and that sentence shows in English until it is translated again.pages.json: which texts appear on which page. A page counts as translated in a language when at least 80% of its texts are translated (ortools.jsonhas an entry for it). Translated pages get their own canonical URL and hreflang links, and are listed as language alternates of the English page in the sitemap.<lang>/content.json: hash to translation.ui-source.json: English interface messages used by the JavaScript (trText,trHtml). Translations are inpublic/locales/<lang>/ui.json, keyed by the English text.
Terms, Privacy and Licensing stay English; only their navbar and footer are translated. The blog stays English.
Markup in texts
Links and formatting are replaced by numbered markers: Read <1>the docs</1> <2/>. A translation must keep every marker once. Text inside <1>...</1> is translated; <2/> is an icon or code and is copied. Interface messages use {{name}} variables instead.
Refreshing after English changes
- Build the site (
npm run build, and once withSIMPLE_MODE=trueinto another folder). node scripts/extract-content-segments.mjs dist /path/to/simple-distrewritessource.jsonandpages.json.node scripts/extract-ui-strings.mjsrewritesui-source.json.node scripts/translations.mjs statusandnode scripts/ui-translations.mjs statusshow what is missing per language.node scripts/translations.mjs chunk <workDir> --lang <lang>writes chunks with only the texts still missing in that language (without--langit writes every text). After translating,node scripts/translations.mjs merge <lang> <workDir>/out/<lang>validates and adds the new translations; it never overwrites an existing translation unless you pass--replace, and never deletes anything.validate <lang>re-checks a language at any time. Translations of texts that no longer exist insource.jsonare kept and reported as stale;prune <lang>removes them once you are sure the extraction was complete (both builds).
Writing UI text in code
- Text shown by JavaScript goes through
trText('English text {{name}}', { name })(plain text) ortrHtml(insideinnerHTMLtemplates).showAlert,showLoader,showAlertModalandshowModaltranslate their arguments themselves, so pass them plain string literals. For templates with values, usetrTextwith{{name}}variables. - Do not keep translated text in module-level constants: they are evaluated before the language loads. Build them inside a function that runs when the UI is drawn.
- Page setup code runs inside
onPageReady(...)(fromsrc/js/i18n), which waits for both the DOM and the translations. - Code that runs in a Web Worker cannot use
trText. Pass the translated templates in from the main thread (seediffStringsinsrc/js/compare/worker-api.ts). src/tests/ui-text-i18n.test.tsfails on raw English in the common UI sinks and whenui-source.jsonis out of date. Runnode scripts/extract-ui-strings.mjsafter changing UI text.node scripts/raw-ui-text-scan.mjslists the offending lines.
Docs
Docs translations are Markdown files in docs/<lang>/, with menu labels in docs/.vitepress/i18n/<lang>.json. See scripts/docs-translations.mjs (check, localize, labels). A language is only built into the docs when every English page has a translation. Set DOCS_LANGUAGES=de,fr (or en alone) to limit which languages are built.