Internationalization
How runtime translation works via data-i18n attributes and JSON locale files.
The kit translates text at runtime — no rebuild needed. You mark elements with
data-i18n attributes, and the engine in app.js swaps in the right string for the current
language.
Marking text
<!-- Element text (default target) -->
<h5 data-i18n="roleMaster.mainPanelTitle">Role Master</h5>
<!-- Attribute targets via data-i18n-target -->
<input data-i18n="header.searchPlaceholder" data-i18n-target="placeholder">
<button data-i18n="panelCard.refresh" data-i18n-target="title" title="Refresh">…</button>The text inside the element is what shows before the engine runs (and stands in if the
script is blocked). At runtime the lookup order is: the current locale → English →
the raw key string itself (logged as a Translation not found warning), so always ship the
key in English (en/) too.
data-i18n-target values
| Target | Effect |
|---|---|
| (omitted) | Sets the element's text content. |
html | Sets innerHTML (use for strings with markup). |
placeholder | Sets the placeholder attribute. |
title | Sets the title attribute (tooltips). |
aria-label | Sets the aria-label attribute. |
value | Sets a form control's value. |
Parameters
<span data-i18n="greeting" data-i18n-params='{"name":"John"}'></span>{ "greeting": "Hello, {{name}}!" }Placeholders use double curly braces — {{param}}. The engine's interpolate()
replaces each {{name}} with the matching value from data-i18n-params (any placeholder
without a matching param is left untouched).
Key naming: file.key
A data-i18n value is {file}.{key}:
- file = the JSON file name (which matches the page name).
- key = a flat, camelCase key inside that file.
So roleMaster.mainPanelTitle reads the mainPanelTitle key from roleMaster.json.
Where the strings live
assets/locales/
├── en/ English
│ ├── manifest.json lists which JSON files to load
│ ├── header.json
│ ├── roleMaster.json
│ └── … (one file per page/section)
├── fr/ de/ es/ pt/ zh/ ja/ nl/ other languages (same file names)
Each JSON file is flat key/value pairs in camelCase:
{
"mainPanelTitle": "Role Master",
"mainPanelSubtitle": "Manage user roles and permissions",
"columnRole": "Role",
"columnStatus": "Status"
}Bundled languages
en, fr, de, es, pt, zh, ja, nl — eight languages. Each has its own folder under
assets/locales/ with the same set of JSON files. The in-page language switcher in the
header lets users change language; you can also switch in code:
window.i18n.setLocale('fr');The switch happens without a page reload — setLocale() loads the locale's files (if
not already cached) and re-runs updatePageTranslations() over every data-i18n element.
How the locale is chosen and remembered
On first load the engine reads localStorage['taxi_crm_locale']; if that's unset it falls
back to the browser's navigator.languages (matched against the eight supported codes), and
finally to English. Every setLocale() call writes the choice back to
localStorage['taxi_crm_locale'], so it persists across visits, and sets <html lang> to
the active code.
Editing or adding text
- Change wording: edit the value in the relevant JSON file (in each language folder you support). The page picks it up on next load.
- Add a new string: add the key to the JSON file, then reference it with
data-i18n="file.key"in the HTML. - Add a new page's strings: create
assets/locales/en/<page>.json, then add its file name toassets/locales/en/manifest.jsonso the engine loads it. (Themanifest.jsonin each language folder lists the files to fetch.)
Translations are loaded with
fetch(). When opening pages fromfile://, some browsers block those requests and you'll see raw keys instead of text — serve the folder over HTTP (see 01 — Getting started).
Reference
data-i18n="file.key"— translate text.data-i18n-target="placeholder|title|html|…"— translate an attribute.data-i18n-params='{"name":"…"}'— interpolate values.window.i18n.setLocale(code)/getLocale()/t(key)— runtime control (see 08 — JavaScript).
Was this page helpful?
