Translations (i18n)
Every string a person sees in the web app comes from a locale file, so AlianHub can be shown in other languages. English in frontend/src/locales/en.js is the source; every other locale is filled from it by a script and checked by a test. This page covers how to add text, how other locales are kept complete, and how translators clear the backlog.
The rule
- Templates never ship bare text, a bare
title,placeholderoraria-label. Use$t('Namespace.key')in templates andt('Namespace.key')in script. - Add the key to
frontend/src/locales/en.jsunder the feature's namespace (Projects,Settings,Inbox, …). - After adding keys, run
npm run i18n:backfill.
A real pair, from frontend/src/components/atom/EmptyState/EmptyState.vue and en.js:
<a
v-if="resolvedHelpHref"
:href="resolvedHelpHref"
target="_blank"
rel="noopener noreferrer"
class="empty-state__help"
>{{ $t('EmptyState.learn_more') }}</a>EmptyState: {
learn_more: "Learn more",
say_label: "Or say it:",
// ...
},Writing keys
- One namespace per area. Add keys to the existing block; never create a
<Name>V2twin.tests/conventions/i18n-namespaces.test.jsfails on a*V2namespace and on any statict('A.b')key missing fromen.js. - Call
t()with a literal key. When the key is built at run time, end the literal with_or.so the check can resolve the prefix:t('Inbox.tab_' + kind). - Keep
{placeholder}names as they are in every translation. node scripts/i18n-rename-namespace.js <From> <To>moves a namespace and rewrites every reference.- Screen text uses the words a project manager uses.
scripts/plain-words.jsreads the English catalogue for words that must not reach the screen; keys that still break a rule are listed inscripts/plain-words-baseline.json, which may only shrink.
How other locales stay complete
The locales are en plus ar, ch, fr, ge, gr, gu, hi, it, ja, ko, ptBr, ru and spa. Only English ships loaded; another language is loaded when a person picks it, and English fills any key it lacks.
npm run i18n:backfill (scripts/i18n-backfill.js) fills every locale with the keys en.js has:
- With
TRANSLATE_API_KEYset (Google Translate v2), a missing value is machine-translated. Without it, the English text is copied. - Either way the key is recorded in
<locale>.pending.json, so a reviewer can find it.ja,koandptBrhave no pending list. - The locale file is rewritten in
en.jskey order, and keysen.jsno longer has are dropped. - Pending files are sorted and carry no timestamp, so two pull requests that add different keys touch different lines. On a merge conflict keep both sides' keys and re-run the backfill.
--dry-runprints what it would do, including the count of keys "pending review";--only=fr,gelimits it to some locales.
The check
npm run i18n:check (scripts/i18n-check.js, also run by tests/conventions/i18n-check.test.js) fails when:
- a locale lacks a key that
en.jshas, or - a
.vuefile has more bare text,title,placeholderoraria-labeloutside$t()than its baseline inscripts/i18n-allowlist.json.
The baseline may only shrink. When you remove bare strings from a file, node scripts/i18n-check.js --write-allowlist lowers it.
Translating
A translator clears a key in three steps:
- Open
frontend/src/locales/<locale>.pending.json. Every key in it is listed with its English text and is still English (or a machine translation) in<locale>.js. - Write the translation at that key in
frontend/src/locales/<locale>.js, keeping every{placeholder}as it is, then delete the key's line from<locale>.pending.json. Nothing removes it for you. - Run
npm run i18n:backfill(it puts the file back in order) andnpm run i18n:check, and commit both files together.
