Skip to content

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, placeholder or aria-label. Use $t('Namespace.key') in templates and t('Namespace.key') in script.
  • Add the key to frontend/src/locales/en.js under 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:

vue
<a
    v-if="resolvedHelpHref"
    :href="resolvedHelpHref"
    target="_blank"
    rel="noopener noreferrer"
    class="empty-state__help"
>{{ $t('EmptyState.learn_more') }}</a>
js
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>V2 twin. tests/conventions/i18n-namespaces.test.js fails on a *V2 namespace and on any static t('A.b') key missing from en.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.js reads the English catalogue for words that must not reach the screen; keys that still break a rule are listed in scripts/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_KEY set (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, ko and ptBr have no pending list.
  • The locale file is rewritten in en.js key order, and keys en.js no 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-run prints what it would do, including the count of keys "pending review"; --only=fr,ge limits 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.js has, or
  • a .vue file has more bare text, title, placeholder or aria-label outside $t() than its baseline in scripts/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:

  1. 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.
  2. 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.
  3. Run npm run i18n:backfill (it puts the file back in order) and npm run i18n:check, and commit both files together.

Released under the AGPL-3.0 licence.