Internationalisation starts easy. One JSON file, a t() function, done. Then the product grows, three developers add strings in different styles, marketing rewrites the onboarding copy, and nobody knows which Arabic strings are stale or whether that key is still used anywhere.
The short answer: the i18n framework is the easy part. What breaks at scale is the workflow — how strings get added, translated, reviewed, and retired. Decide that early, because retrofitting it means auditing every key you ever wrote.
This guide covers structure and workflow. For the wider scope of Arabic support, see the complete Arabic localisation guide.
Build the layer even if you launch in one language
The cheapest localisation decision available: set up i18n in phase one, even when Arabic ships later.
What it costs now: a few days. Install a library, externalise strings as you write them, use logical CSS properties.
What it costs later: weeks. Every hardcoded string has to be found across the codebase — and they hide in validation messages, email templates, error handlers, chart labels, and PDF generators. Teams consistently find twice as many as they estimated.
The asymmetry is large enough that building the layer is worth it even if Arabic never ships. If it does ship, you saved weeks; if it does not, you lost days.
Structuring translation files
Namespace by feature, not by type. The instinct is buttons.json, labels.json, errors.json. This scales badly, because working on checkout means touching every file.
locales/
en/
common.json
auth.json
checkout.json
ar/
common.json
auth.json
checkout.json
Key naming should describe meaning, not content. auth.login.submitButton survives a copy change; auth.login.signInNow becomes a lie the moment marketing rewrites it to "Get started."
Never use the English string as the key. It seems convenient and it means every copy edit is a key rename across the codebase and every translation file.
Keep the same key set in both languages. A key present in English and absent in Arabic should fail a check, not silently render a fallback in production.
Plurals: Arabic has six forms
This is where naive i18n breaks, and it breaks silently.
English has two plural categories. Arabic has six: zero, one, two, few, many, and other. A library that only handles singular and plural will produce grammatically wrong Arabic for most quantities.
{
"items": {
"zero": "لا توجد عناصر",
"one": "عنصر واحد",
"two": "عنصران",
"few": "{{count}} عناصر",
"many": "{{count}} عنصراً",
"other": "{{count}} عنصر"
}
}
Verify your library supports CLDR plural rules before committing to it. i18next, FormatJS, and Flutter's intl all do. A hand-rolled solution almost certainly does not, and the output is wrong in a way English-speaking reviewers cannot see.
The dual form matters. Arabic has a specific form for exactly two, and getting it wrong is immediately noticeable to native readers in a way that reads as carelessness.
Interpolation and word order
Never concatenate translated fragments. Word order differs between languages, so assembled sentences produce nonsense.
// Wrong — assumes English word order
t("youHave") + " " + count + " " + t("newMessages")
// Right — one string, one meaning
t("newMessages", { count })
Give variables meaningful names. {{name}} and {{count}} tell a translator what they are working with; {{0}} and {{1}} do not, and a translator who cannot see the context will guess.
Watch bidirectional interpolation. A Latin-script value inside an Arabic sentence needs isolation, or punctuation lands wrong — see Arabic numerals and formats.
Provide context for ambiguous keys. "Post" is a verb and a noun; a translator seeing only the word cannot know which. Most libraries support a context or description field — use it, because the alternative is a translator asking or, worse, guessing.
Workflow: the part that actually breaks
The technical setup is a day. The workflow is what determines whether Arabic is still accurate in a year.
The three common approaches:
| Approach | Suits | Weakness |
|---|---|---|
| Files in the repository | Small teams, developer-authored copy | Non-developers cannot contribute |
| Translation management platform | Growing products, external translators | Cost, sync complexity |
| Spreadsheet with import script | Small budgets, occasional updates | Manual, error-prone at scale |
For most Gulf products, files in the repository work well initially and a translation platform becomes worthwhile once non-developers need to edit copy regularly.
Whatever you choose, three rules hold:
Fail the build on missing keys. A key present in English and missing in Arabic should break CI, not fall back silently in production where nobody notices.
Detect unused keys. Files accumulate strings for features deleted two years ago. A periodic scan for keys absent from the codebase keeps this from growing indefinitely.
Never machine-translate into production without review. A first draft from machine translation is reasonable for volume content. Shipping it unreviewed produces text fluent readers immediately identify as machine-produced, which costs more credibility than the translation saved in time.
Framework notes
React / Next.js — next-intl and react-i18next both handle Arabic plurals correctly. Next.js App Router uses a [lang] route segment, which gives you a real URL per language and therefore proper hreflang handling for search.
Flutter — the intl package with ARB files. Generated typed accessors mean a missing key is a compile error rather than a runtime fallback, which is the strongest possible version of the "fail on missing keys" rule.
Backend — do not forget it. Emails, SMS, PDF invoices, and API error messages all need translation, and they are the most commonly overlooked surface. An Arabic app sending English transactional emails is a common and avoidable inconsistency.
Checklist
- i18n layer built in phase one, regardless of launch languages
- Files namespaced by feature, not by string type
- Keys describe meaning, not content
- No English strings used as keys
- Library verified to support CLDR plural rules with all six Arabic forms
- No string concatenation for sentences
- Named interpolation variables with context notes
- Bidirectional isolation for interpolated Latin values
- CI fails on missing keys
- Periodic unused-key scan
- Backend surfaces translated: emails, SMS, PDFs, API errors
- Native speaker review before release, always
Related reading
- Complete Arabic localisation guide — the six layers of localisation.
- RTL in CSS — the layout half of the problem.
- Arabic numerals and formats — interpolating numbers and currency.
- Arabic and RTL in React Native — mobile specifics.
- Common localisation mistakes — what goes wrong in practice.
Frequently asked questions
How many plural forms does Arabic have?
Six: zero, one, two, few, many, and other. English has two, so any library or hand-rolled solution handling only singular and plural will produce grammatically wrong Arabic for most quantities. Verify CLDR plural rule support before choosing a library — i18next, FormatJS, and Flutter's intl all handle it.
Should I set up i18n if I am only launching in English?
Yes, if Arabic is anywhere in your roadmap. Building the layer costs a few days now; retrofitting costs weeks, because hardcoded strings hide in validation messages, email templates, error handlers, and PDF generators. The asymmetry is large enough that it is worth doing even if Arabic never ships.
Should I use the English text as my translation key?
No. It seems convenient but it means every copy edit becomes a key rename across the codebase and every translation file. Use keys that describe meaning — auth.login.submitButton — so that rewriting the button text does not touch code.
Can I use machine translation for my Arabic strings?
As a first draft for volume content, yes. Shipping it without native review produces text that fluent readers immediately identify as machine-produced, and it cannot make register decisions or adapt examples. The credibility cost usually exceeds the time saved.
Why should I not concatenate translated strings?
Word order differs between languages, so a sentence assembled from fragments in English order produces nonsense in Arabic. Use a single string per sentence with named interpolation variables instead, and give translators context notes for ambiguous keys.
What is the most common i18n oversight?
Backend surfaces. Emails, SMS messages, PDF invoices, and API error strings are routinely left in English while the interface is fully translated. An Arabic app that sends English transactional email is a common and entirely avoidable inconsistency.
How do I stop translation files rotting?
Fail CI when a key exists in one language and not another, and run a periodic scan for keys no longer referenced in the codebase. Without both, files accumulate stale strings from deleted features and silently fall back to English in production where nobody notices.
Conclusion
Build the i18n layer in phase one. It costs days now and weeks later, and the calculation holds even if Arabic never ships.
Check plural support before choosing a library. Arabic's six forms are the specific thing naive implementations get wrong, and the error is invisible to reviewers who do not read Arabic.
And invest in the workflow, not just the setup. Failing builds on missing keys and scanning for unused ones is what keeps Arabic accurate after the first release, which is where most products quietly degrade.
Planning a bilingual product? Get in touch — we build Arabic-first, so the i18n layer exists from the first commit. See our web development and mobile app services.