type
Reference
title
i18n — English and Arabic
description
The en/ar locale files, flat key naming, fallback order, RTL layout, and where every new user-facing string must land.
tags
i18n, arabic, rtl, locales, translation
status
stable
stale_after
Fri Apr 30 2027 02:00:00 GMT+0200 (Central European Summer Time)
generated
techwriter/mimo-v2.6-flash · Sat Oct 31 2026 11:02:38 GMT+0100 (Central European Standard Time)
sources
translator → internal/i18n/translator.go; config → config/config.go; langmw → internal/infrastructure/web/language.go; renderer → internal/infrastructure/web/localized_render.go; locales → web/locales/en.json

i18n

Two locales ship — en and ar, 542 keys each, identical key sets — and the UI is genuinely bilingual: Arabic gets RTL layout, not just translated words.

Piece Where Behaviour
Locale files web/locales/en.json, ar.json flat key → string; nested JSON is flattened to dotted keys at load, so either shape works
Loader internal/i18n one Translator at boot; a missing or unparseable locale file is a fatal boot error
Lookup T(lang, key) everywhere requested lang → default lang (prefers en) → the key itself as the last resort
Negotiation web.LanguageMiddleware runs globally before every route (the login page included); ?lang= or the lang cookie, resolved into the request context
Rendering web.NewLocalizedHTMLRender one compiled template set per language, with t/dir/lang/date funcs bound to it — this removed an older request-shared atomic "current language" that could leak one request's locale into a concurrent render
RTL config.DirectionFor ar, he, fa, ur → rtl; everything else ltr. The layout template sets dir from it; the Cairo font stack (web/assets/css/cairo-fonts.css, vendored woff2) covers Arabic glyphs
Enabled languages LANGS env comma-separated, first is default; deployed tenants run en,ar

Key naming

Where new strings must land

Every user-visible literal added to a template or a handler needs a key in both web/locales/en.json and web/locales/ar.json — the key sets are kept identical (this pass verified both files at 542 keys with no diff either way), and a missing key falls back to English and then to the raw key, which renders as a bug. Templates reach strings through the t func; handlers and view-model code through h.i18n.T(lang, key).

Plugin strings follow the same rule from the outside: the host translates a plugin field under plugin_<id>_<key> and falls back to the plugin's own Field.Label — see settings. Labels on the catalogue itself (the roles matrix) are dual-written in code: En/Ar columns on every rbac.Resource.

Not determined

Rendered from simple-lms/i18n.md · OKF bundle · regenerate with npm run render