🔤 Arabic localization
Updated
Every number here is measured against the tree.
Where things live#
Localization consists of two independent halves. Confusing them names both the wrong file and the wrong number.
| Half | Where | What it is |
|---|---|---|
| The interface | frontend/src/i18n/locales/{en,ru,uk,ar}.ts |
the captions of buttons, columns, dialogs |
| Business captions | the caption of workspace objects, inline |
the names of dictionaries, documents, fields |
frontend/src/i18n/messages.ts is not a dictionary: 44 lines of a loader
registry, with zero keys in it. Look for a translation in locales/.
Coverage#
en, ru and uk are complete; ar is 41 keys short. There is
deliberately no absolute key count here — it changes with every commit to the
front end and goes stale silently. Count the gap, not the total: a new UI key
must land in all four locale files at once or ar falls behind.
Which means what you look at is the GAP, not the total. To recount:
for l in en ru uk ar; do
printf "%s %s\n" "$l" "$(grep -cE "^\s*'[^']+':" frontend/src/i18n/locales/$l.ts)"
done
There are 41 untranslated keys, and they are clustered rather than spread out:
| Group | Keys | What it is |
|---|---|---|
logs.* |
34 | the system log screen |
grid.* |
2 | "Open data", "Open event handler…" |
menu.* |
2 | the model menu and its hint |
obj.* |
2 | copying an object's code |
nav.logs |
1 | a navigation item |
So what is untranslated is essentially one screen — the log, an administrator's tool. There is not a single business caption among the gaps.
Business captions: Arabic is there on 277 of the 328 objects that carry a
caption. The rest are service and test models.
RTL#
The writing direction is a property of the language, not a separate "Arabic
mode": I18nContext holds dir: 'ltr' | 'rtl' and sets rtl for the languages
in RTL_LANGS.
RTL is worth checking on mixed content — Arabic text with Latin identifiers and digits inside it (an invoice number, a VAT number, an item code). That is where it breaks most often, and in our data almost every record looks like that.
Compliance and country models#
Do not assume "Arabic locale" implies "no ZATCA code". Saudi compliance lives
in LocalizationSaudiArabia, not in the ar.ts file:
- ZATCA / Fatoora — onboarding web services, outbound channels, feedback job, QR, CSR and signing profile. Implementation — ZATCA / Fatoora; open defects are listed there.
- VAT at 15% — shared tax contour (
Tax), not a hardcoded rate in the locale files.
Not part of Arabic UI work today:
- Hijri calendar — dates stay Gregorian. Empty date fields show a
Gregorian format mask from
DATE_INPUTinfrontend/src/i18n/DatesLocaleProvider.tsx(ArabicDD/MM/YYYY). Do not hardcodevalueFormatorplaceholderon a Mantine date control unless the field is display-only (LL). - Zakat — no dedicated calculation module.
- Localized exception text — user-facing errors are still English; in RTL that mismatch is especially visible.
How to finish the translation#
- Grep the file first. The same notions are almost certainly translated already, and a divergence in wording is more noticeable to the reader than a missing translation. Latin script inside a translated string is almost always a miss.
- An interface key goes into
locales/ar.ts, next to its neighbours in the group. - An object's caption goes inline into its
caption:{ "en": "Customer", "ar": "عميل", "ru": "Клиент" }. English is the base and the fallback; a language key removed from the file is deleted. - A language that is not registered on the stand is ignored on import — a
model does not create languages for a tenant. Registering it later (the Languages page,
POST /api/metadata/languages) brings its captions from the installed model tree right away — without it a tenant shows English metadata under an Arabic interface.
What this page does not replace#
A review by a tax consultant. The machine translation
ran over the whole tree, and Arabic grammar catches its mistakes better than
any diff: in an iḍāfa the first noun takes no article, so two words in a row
carrying ال — or "article + bare" — is almost always an inverted word order.
What is left of the machine pass and has not been verified by a human:
ar_final_untranslated.json
— 53 strings.