`in` was a broken duplicate of `id`: config called it "Indonesian (Legacy)",
the README labelled it Hindi behind an Indian flag, and its dashboard catalog
was 45% English. Retiring it makes the supported-locale count an honest 42.
Saved preferences keep working: `id` now declares `"aliases": ["in"]`, so
`NEXT_LOCALE=in` (resolveRequestedLocale) and `OMNIROUTE_LANG=in`
(bin/cli/i18n.mjs detectLocale) both resolve to `id`.
Removed from every surface: config/i18n.json, the dashboard and CLI catalogs,
the docs mirror (docs/i18n/in/), the README flag block and headline, the
docs indexes, llm.txt (+ the 41 llm.txt mirrors), the i18n-flow diagram,
the Google-Translate generator specs, the CLI scaffold list, the Python
lang map, the translation-ratio baseline and .i18n-state.json.
The 🌐 language bars of the seven English docs that linked ../i18n/in/ lost
that entry too, otherwise check:doc-links would resolve to a deleted tree.
One command brings a new locale to every surface, in order: config/i18n.json
entry, flag SVG (lipis/flag-icons), dashboard catalog (scaffold + sync-ui-keys
--translate-markers), docs mirrors (run-translation over the core set every
existing locale carries, then the llm.txt / CHANGELOG.md stubs), CLI catalog
(generate-locales --code + batched common/program translation), README flag
link / docs index row / I18N guide row / locale counts (+ sync-llm-mirrors),
language bars, and the marketing site (lang JSON, SUPPORTED_LANGS, dropdowns).
Every phase checks presence first, so a re-run is a no-op apart from filling in
what is still missing; --dry-run prints the whole plan and touches nothing (no
files, network or child processes); --only / --skip select phases.
- scripts/i18n/lib/docs-core-set.mjs: computeDocsCoreSet — intersection of the
existing non-docsExcluded mirrors minus llm.txt / CHANGELOG.md / I18N.md and
paths without an English source (22 files today).
- scripts/i18n/lib/site-scaffold.mjs: addSupportedLang (re-flows the array in
the file's own fill style) and addDropdownOption (code order, every menu).
- bin/cli/scripts/generate-locales.mjs: --code=<locale> filter.
- translate-backend parseBatchResponse: Object.hasOwn + trimmed values.
- package.json: i18n:add-locale script.
feat(cli): suporte i18n completo — 42 locales, --lang flag, config lang get/set/list
- 42 locale files in bin/cli/locales/ (en + pt-BR fully translated, 29 with common/program, 11 scaffolds)
- --lang <code> global flag for per-execution override
- config lang get/set/list subcommands
- Locale persistence via ~/.omniroute/.env
- Path traversal protection via regex validation in normalize()
- Script generate-locales.mjs for scaffolding new locales
- Unit tests for lang commands + normalization security
Integrated into release/v3.8.0