Skip to content

Internationalization

Setup

  • Library: @nuxtjs/i18n v10.2.x
  • Strategy: no_prefix — no locale code in URL path
  • Default locale: en_GB
  • Browser detection: disabled — locale is always set manually

Supported Locales

CodeLanguageFile
en_GBEnglish (UK)en_GB.json
en_USEnglish (US)en_US.json
es_ESSpanishes_ES.json
ar_SAArabicar_SA.json
de_DEGermande_DE.json
fr_FRFrenchfr_FR.json
ru_RURussianru_RU.json
pt_PTPortuguesept_PT.json
pl_PLPolishpl_PL.json
sv_SESwedishsv_SE.json
en_PSPseudo English (testing)en_PS.json

Translation files live in i18n/locales/ and are ~130–145 KB each.

Translation Key Convention

Keys use an @ prefix by project convention (not enforced by the library):

@common.somethingWentWrong
@common.languages.en.label
@common.prereleaseTitle
@toasts.questionnaire.failedToFetch
@toasts.questionnaire.created
@stores.permission.toast.folderPermissionCreated.title
@confirms.deleteItem.title
@components.designer.addSection

Nested keys follow a domain.subdomain.key pattern. The @ prefix is purely a project-level naming convention.

Usage in Components and Stores

typescript
const { t } = useI18n();

t("@common.somethingWentWrong");
t("@common.languages.en.label");

Auto-imported by Nuxt — no import statement needed.

Locale Initialization

app/plugins/i18n.client.ts runs once on client mount:

1. Read localStorage["user-language"] (stored as JSON: {"value": "en_GB"})
2. If found and different from current locale → $i18n.setLocale(savedLocale)
3. If not found → read navigator.language, convert "en-GB" → "en_GB"
4. Fall back to en_GB if no match found

Locale Persistence

The user's chosen locale is stored in localStorage["user-language"] as:

json
{ "value": "en_GB" }

useLocalStorage("user-language", { value: "en_GB" }) provides a reactive ref bound to this key. useUserStore reads this at initialization and sets appConfig.language_code, calendar_code, and date_locale.

RTL Support

app/layouts/default.vue computes isRTL based on the current locale and sets the HTML dir attribute:

typescript
const rtlLanguages = [
  "ar_SA",
  "ar",
  "he_IL",
  "he",
  "fa_IR",
  "fa",
  "ur_PK",
  "ur",
];
const isRTL = computed(() => rtlLanguages.includes(currentLocale.value));

useHead({
  htmlAttrs: { lang: currentLocale, dir: isRTL.value ? "rtl" : "ltr" },
});

HTML in Translations

strictMessage: false in nuxt.config.ts allows HTML markup inside translation strings. Use v-html-safe (from vue-html-secure) when rendering these to prevent XSS.

Pseudo-locale

en_PS.json is generated by running:

bash
npm run pseudo-locale

The pseudo-locale wraps all strings with decorators (e.g. [Ëñglïsh]) to visually identify:

  • Hardcoded strings that bypass translation
  • Layout issues with expanded text

Questionnaire Content vs UI Language

These are separate concerns:

ConcernControllerScope
UI language@nuxtjs/i18nAll labels, buttons, toasts, error messages
Questionnaire content languageuseDesignerStore.current_languageQuestion text, section titles in the designer

Questionnaire content language codes come from useProviderStore (fetched from the backend) and are stored in payload.definitions.translations. They are not the same as the @nuxtjs/i18n locale codes.