Multilingual interfaces and localization
1) Principles
1. Language is not skin. Texts, formats, direction of writing, illustrations, legal blocks and even navigation are changing.
2. First the keys, then the texts. Structure semantic keys and parameters - translations come later.
3. Pseudolocalization - before exit. Catch overflows, "hard-sewn" lines and hidden anglicisms.
4. Folbacks are predictable. 'ru-UA → ru → en'. No "quiet" failures.
5. Security. No HTML from translation to DOM without sanitization; placeholders are positional/named only.
6. A11y-equivalence. Alt texts, aria labels, abbreviations - everything is localized.
2) Locale strategy
System codes: 'language-REGION' (for example, 'pt-BR', 'en-GB').
Locale selection: user profile → setting; spare - auto-detection by browser/geo (with confirmation).
Multi-region: distinguish language from law: 'es-ES' ≠ 'es-MX' (different laws/payments/limits).
Folback chain: UI is the nearest language; legal texts - strictly regional version, otherwise block and confirmation request.
3) Information architecture and content
Key areas: navigation, CTA, bugs, forms, prompts, notifications, letters, PDF/banners.
Text extensions: + 30-40% width reserve (German/Finnish). Layout - elastic (flex/grid).
Tone/style: brand dictionary (terms, without "slang translation" in critical places).
Images/icons: avoid text in pictures; if needed, keep local versions.
4) i18n architecture
Keys: 'domain. section. intent` → `payments. withdraw. error. insufficient_funds`.
Placeholders: named ('{amount}', '{minutes}'), formatted out of line.
ICU MessageFormat: multiplicity, gender, concordance.
Files: by locale and domains ('/i18n/{ locale }/{ domain} .json '), chunks are loaded lazily.
Server/client: universal render, locale in cookie + HTTP-Vary.
json
{
"betslip": {
"placed": "Ставка на сумму {amount} {currency} принята",
"timeout": "Ожидаем подтверждение… ~{seconds, plural, one {# сек} few {# сек} many {# сек} other {# сек}}"
}
}
5) Formatting: numbers, dates, currencies, units
Use'Intl. ':js const nf = new Intl.NumberFormat('uk-UA', { style:'currency', currency:'UAH' });
nf.format(2000); // 2 000,00 ₴
const df = new Intl.DateTimeFormat('tr-TR', { dateStyle:'medium', timeStyle:'short' });
df.format(new Date());
const pl = new Intl.PluralRules('ru-RU');
Minor units: Keep amounts in cents/kopecks; Format on the client.
Relative time: 'Intl. RelativeTimeFormat`.
Units: 'Intl. NumberFormat({ style:'unit', unit:'meter' })`.
Calendar/week: 1st day of the week and date format - by locale.
6) RTL and writing direction
Support 'dir = "rtl"' for 'ar', 'he', 'fa'; use'dir = "auto" 'for custom content.
Invert icons/chevrons; carousel and stepper mirrors.
Currency numbers/symbols - LTR windows (avoid mixed BiDi chaos).
CSS Boolean properties ('inline-start/end') instead of left/right.
7) Forms and input
Names/addresses: allow apostrophes/diacritics/double surnames.
Telephones: E.164 storage; masks - soft, with insert support.
Address formats: field order by country; index/state may be missing.
Keyboards: 'inputmode', 'autocomplete' correct for locale.
Placeholders: examples in local language/format.
8) Pseudolocalization and testing
Automatically replace the lines with '【 ěļő 】' + extension '+ + +' (~ 35%).
Include the pseudolocal in the dev assembly as' qps-ploc '.
Screenshots with context for translators: highlighting placeholders and long texts.
Test: trimmings, hyphenations, overflows, "hard-stitched" strings, RTL.
9) Notifications, letters, templates
Template of letter and subject - for each locale; separate texts and layout.
Dates/amounts in the subject - formatted by locale.
The Configure Notifications links are always in the language of the letter.
SMS: short, without multiline quotes; UTM - no localization.
10) Safety and reliability
Never interpret the translation as HTML, use safe inserts.
Placeholders - only data, not markup.
Logs/metrics are unclassified, but with a locale for tracing problems.
Folback when the translation file is not available - "quiet" (show English + telemetry).
11) Performance
Chunks of transfers by routes/domains; preload for frequent.
Кеш CDN с `ETag`/`Cache-Control`.
Avoid rerenders when changing locales - i18n context with memoization.
12) The specifics of iGaming
Disclaimers and responsible play: the wording depends on the country (18 +/21 +, regulatory authorities, help lines).
KYC/AML: legally correct terms (for example, "Source of Funds," "Beneficial Owner"), case/birth options.
Payment methods: local names (PIX, Papara, SEPA) and conditions (ETA/commissions) - strictly by region.
Coefficients and format: 'decimal/fractional/american' - local explanations and example.
Legal texts: unchanged regional versions; banning folbeck from other jurisdiction.
13) Design system tokens (example)
json
{
"i18n": {
"fallback": ["en"],
"rtl": ["ar", "he", "fa"],
"textExpansionPct": 0.35,
"screenshotHints": true
},
"typography": {
"lineHeight": { "ui": 1.4, "dense": 1.3 },
"hyphens": "auto",
"tabularNums": true
},
"layout": {
"minLabelWidth": 96,
"gap": { "sm": 8, "md": 12, "lg": 16 }
},
"a11y": {
"ariaMirroring": true,
"altTranslate": true,
"contrastAA": true
}
}
14) Snippets
React + i18next (lazy boot, ICU):ts import i18n from 'i18next';
import ICU from 'i18next-icu';
import { initReactI18next } from 'react-i18next';
i18n.use(ICU).use(initReactI18next).init({
lng: 'uk-UA',
fallbackLng: ['ru', 'en'],
load: 'languageOnly',
interpolation: { escapeValue: false },
resources: {} // пусто — грузим лениво
});
export async function loadNamespace(ns: string, lng = i18n.language){
const mod = await import(`/i18n/${lng}/${ns}.json`);
i18n.addResourceBundle(lng, ns, mod.default, true, true);
}
ICU pluralization (rus/ukr):
json
{
"notifications": "{count, plural, one {# уведомление} few {# уведомления} many {# уведомлений} other {# уведомления}}"
}
Intl for currencies/dates:
js const money = (v, c, l) => new Intl.NumberFormat(l, {style:'currency', currency:c}).format(v/100);
const rel = (v, unit, l) => new Intl.RelativeTimeFormat(l, {numeric:'auto'}).format(v, unit);
// money(250000,'EUR','de-DE') → 2.500,00 €
RTL class at the root:
js const rtl = new Set(['ar','he','fa']);
document.documentElement.dir = rtl.has(locale.split('-')[0])? 'rtl': 'ltr';
Pseudolocal (dev):
js const pseudo = s => s.replace(/[aAeEiIoOuU]/g, m => ({a:'à',e:'ê',i:'ï',o:'ô',u:'û'}[m.toLowerCase()] m)).replace(/([^\s])/g,'$1\u0301');
15) Empty/error/graysful degradation
There is no key translation: we show English + log 'missing _ key'.
No locale file: folback and banner "Part of the interface in English."
Text too long: multi-line, 'line-clamp' in place, tooltip with full text.
16) Metrics and quality control
Coverage% by key/location (target ≥ 98%).
Time-to-Translate (TTT) for new releases.
Bug rate L10n: visual clippings, RTL defects, erroneous formats.
Reading ease (subjective survey) and NPS per locale.
Legal validation by region (compliance checklist).
17) Antipatterns
Concatenation of strings in code ("You won" + amount + "!") - breaks grammar.
Text in images/icons without local versions.
Hard widths for English.
Substitution of country law with language (use 'es-ES' for Mexico).
HTML translation from CMS without sanitation.
The same key with different meanings in different places.
18) QA checklist
Lines and keys
- Named placeholders; no concatenation.
- ICU-pluralization/genus where needed.
- The folback chain works.
Layout and availability
- Margin of width + 30-40%; 'line-clamp', word wrapping.
- Alt/aria labels are localized.
- RTL mirrors icons/navigation; numbers are readable.
Formats
- Dates/Currencies via'Intl. '; amounts from minor units.
- Address/Phone/Name - Flexible Country Rules.
Safety/performance
- Translations do not execute HTML; XSS excluded.
- Lazy chunks, CDN cache, without unnecessary rerenders.
iGaming-specifics
- Disclaimers/18 +/help lines - by jurisdiction.
- KYC/AML texts are legally reconciled.
- Payment names/ETAs/fees - local.
19) Documentation in the design system
Разделы: i18n Tokens, Guides (ICU/Plural/RTL), Patterns (Emails/SMS/Toasts), Legal Strings per Region.
Tools: pseudolocal, screenshot of screens, coverage report, key linter.
Process: glossary, Translation Memory, contextual screenshots, review by a native speaker.
Brief summary
Multilingual UI is a systematic work at the level of architecture, design, content and law. Organize keys and folbacks, use ICU and 'Intl', support RTL, run a pseudo-locale in advance and ensure the legal correctness of regional wording. Then the product will feel native - from coefficients and payments to letters and help - in each country and for each user.