Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Web app

Explanation

Internationalisation and languages

How i18n.js loads the language manifest and dictionaries, resolves keys and plurals, switches language at run time, and how metadata labels follow the language.

Every text the web app shows comes either from a language file in webapp/i18n/ or from a label dictionary in server metadata. This page explains the language manifest, how webapp/js/i18n.js picks and loads a language, how keys, parameters, and plurals resolve, and what happens on a language switch. Adding a language is a task on add a language to the web app.

In short

  • The available languages are data: webapp/i18n/languages.json lists tr (Türkçe) and en (English) (webapp/i18n/languages.json:1-4).
  • en.json and tr.json each hold 2,853 keys under 68 top-level groups, and both files have the same key set (counted by flattening both files with a JSON parser and comparing the key sets).
  • t(key, params) resolves a dotted key and fills {name} placeholders; a missing key returns the key itself (webapp/js/i18n.js:101-109).
  • tp(key, n) picks the plural form from Intl.PluralRules (webapp/js/i18n.js:112-127).
  • Metadata labels resolve through resolveLabel, which falls back to English (webapp/js/meta.js:284-288).

How it works

Choosing the language

initI18n() loads the manifest, then calls setLang(pick()) (webapp/js/i18n.js:54-72). pick() uses, in order (webapp/js/i18n.js:46-52):

  1. the code stored in local storage under cluster_lang, if it is in the manifest;
  2. the first two letters of the browser language, if they are in the manifest;
  3. tr (DEFAULT_LANG), if it is in the manifest;
  4. the first manifest entry.

If the manifest cannot be loaded, the list falls back to tr alone so the app still boots (webapp/js/i18n.js:14-16, webapp/js/i18n.js:32-44). If the dictionary cannot be loaded, the app continues with the markup's own text (webapp/js/i18n.js:56-62).

Switching language

setLang(code) loads i18n/<code>.json, stores the code, sets lang on the root element, translates the visible markup with apply(), and dispatches cluster:lang-changed (webapp/js/i18n.js:74-87). Three places change the language:

Place Behaviour Source
Sign-in screen language button Cycles through every manifest entry webapp/js/main.js:152-159, webapp/js/i18n.js:91-95
Home status line language chip A menu of every manifest entry webapp/js/home.js:167-215
Settings › Language (common.language) A list with a check mark on the active entry webapp/js/home.js:3670-3712

Listeners of cluster:lang-changed:

  • gen-api.js clears every cached option list and label, because server-resolved names depend on the language (webapp/js/gen-api.js:366-371).
  • home.js marks every running app as stale; a stale app is redrawn when the user returns to it (webapp/js/home.js:2334-2342, webapp/js/home.js:2501-2511).

Duplicated home tiles follow a change made in another tile through the storage event (webapp/js/i18n.js:63-71).

Applying text

apply(root) fills four attribute families from the dictionary (webapp/js/i18n.js:129-144):

Attribute Target
data-i18n Text content
data-i18n-ph placeholder
data-i18n-aria aria-label
data-i18n-title title

Screens built in code call t() directly. apiErrorText translates server error codes through keys of the form error.<code> and uses the server's English message when no translation exists (webapp/js/gen-api.js:11-35).

Language sent to the server

Every API call carries Accept-Language with the current code, so names the server resolves (units, categories, reference labels) come back in the same language (webapp/js/api.js:18-22). See reference data and core i18n.

Metadata labels and reference rows

Labels in metadata are dictionaries such as {"en": "...", "tr": "..."}. resolveLabel returns the current language, then English (webapp/js/meta.js:283-288). Reference rows that carry one column per language use langLabel, which reads label_<lang>, then label_en, then code (webapp/js/meta.js:416-421). App manifest labels follow the same rule (webapp/js/home.js:520).

Dates and numbers

Dates in lists use a fixed YYYY-MM-DD pattern in the user's time zone (webapp/js/prefs.js:150-154). Short date and time on widgets use Intl.DateTimeFormat with the page language, so order and separators come from the browser's locale data (webapp/js/prefs.js:156-170). The status line clock uses toLocaleTimeString and toLocaleDateString with the current language (webapp/js/home.js:110-116).

Why it is built this way

  • The project's written engineering rules state that language selection is data-driven: a new language is a new JSON file and one line in languages.json, with no code change. The comment in webapp/js/i18n.js:4-7 states the same rule.
  • The comment in webapp/js/api.js:18-21 states that the language header was added because a Turkish screen received English unit names from the server.
  • The comment in webapp/js/i18n.js:112-115 states that plural selection must not test n === 1 and takes the category from CLDR data.

Rules and boundaries

  • The project's written engineering rules require that every user-visible text comes from an i18n key, that both language files are updated together, and that Turkish text uses correct Turkish characters.
  • Generic web files must not read per-language columns or branch on a language code. tests/test_metadata_driven_rules.py:85 enforces this for eleven files.
  • Browser tests: web-client/_e2e_lang.mjs, web-client/_e2e_lang_background.mjs, web-client/_e2e_intranet_notif_i18n.mjs, web-client/_e2e_ui_a11y.mjs (plurals).

Interaction with other parts

Limits and open points

  • The language choice is stored in the browser only (webapp/js/i18n.js:80). It is not part of the user account; see user UI state.
  • Metadata label dictionaries are written in each module's Python metadata. A language that has a web dictionary but no metadata entries shows the English metadata labels (webapp/js/meta.js:287).

Terms introduced

Term Code identifier Definition Turkish term Source
language manifest languages.json The list of language codes and native names the web app offers. dil manifestosu webapp/i18n/languages.json:1
translation key t A dotted path into the language file that names one text. çeviri anahtarı webapp/js/i18n.js:104
plural form tp The text variant chosen by the CLDR plural category of a number. çoğul biçimi webapp/js/i18n.js:116

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13