Localization Friendly Writing
Localization-Friendly Writing
Reference for writing source-language strings that translate cleanly into 30+ locales.
The one rule: write so a translator can reorder, expand, and replace
Every translation operation needs three freedoms:
- Reorder — subject-verb-object in English is not subject-verb-object in Japanese or German.
- Expand — German, Russian, Finnish run 30–40% longer than English.
- Replace — Plural forms, gendered forms, formal/informal address.
Core concept 1 — The translation-friendly English rules
- One sentence, one idea. Compound sentences with subordinate clauses become unparseable in OV languages.
- Subject-verb-object, in that order.
- No idioms. “Hit the ground running” has no German equivalent.
- No metaphors. “Move the needle” requires a needle, which requires a gauge, which requires the metaphor to land.
- No cultural references. No baseball, no Thanksgiving, no Marvel cinematic universe.
- Avoid humour and wordplay. Puns are untranslatable by definition.
- No abbreviations the reader must decode. “Q1,” “EOY,” “ASAP” — spell them out at first use.
- No phrasal verbs where a single verb works. “Set up the account” becomes “create the account.”
- No latinate jargon. “Utilize” → “use.” “Initiate” → “start.”
- Active voice as the default.
Core concept 2 — ICU MessageFormat: plural
{count, plural,
=0 {No items}
one {# item}
other {# items}
}
Common mistake — only English plural categories:
{count, plural,
one {# item}
other {# items}
}
This works for English. It silently breaks Russian, Polish, Arabic.
Core concept 3 — CLDR plural categories
| Category | Used by | Example values |
|---|---|---|
zero |
Arabic, Welsh, Latvian | 0 (in some languages) |
one |
English, Spanish, French | 1 |
two |
Arabic, Welsh | 2 |
few |
Russian, Polish, Czech | 2–4 (Russian: 2, 3, 4, 22, 23, 24…) |
many |
Russian, Polish | 5+ in some languages |
other |
every language; required fallback | Everything else |
other is required. Every plural block must include other.
Core concept 4 — ICU select and selectordinal
select is a switch over a string variable, typically used for gender:
{gender, select,
female {She updated her profile.}
male {He updated his profile.}
other {They updated their profile.}
}
other is required even in select.
Core concept 5 — Placeholders that survive translation
- Use named placeholders, not positional.
{username}survives word reorder.%s %sdoes not. - Never concatenate.
"Hello, " + username + "!"forces English word order. - Always provide a comment describing the placeholder.
Concatenation anti-pattern:
// BAD
const msg = t('error.prefix') + ' ' + filename + ' ' + t('error.suffix');
// GOOD
const msg = t('error.full', { filename });
// strings.en.json: { "error.full": "Could not save file {filename}." }
Core concept 6 — Translator comments
Every non-trivial string gets a translator comment answering:
- What is this? UI element type (button, error, tooltip, heading).
- What does the placeholder mean?
{count}= unread messages, integer ≥ 0. - Where does it appear?
Core concept 7 — Pseudo-localization
Pseudo-localization is a smoke test that runs before any human translator sees the strings. It:
- Expands every string 30–40% to surface truncation bugs
- Replaces ASCII characters with accented Latin equivalents
- Wraps every string with sentinels like
[!! … !!]to surface un-extracted strings
Core concept 8 — RTL-friendly writing
- Avoid baked-in directional assumptions. “Click the arrow on the right” becomes wrong in Arabic. Prefer “Click the arrow next to the search box.”
- Numbers stay LTR inside RTL text. This is automatic in Unicode bidi.
Core concept 10 — Key naming conventions
GOOD (semantic, namespaced):
inbox.unread.label
settings.security.two_factor.toggle
errors.network.timeout.body
BAD (content-derived, fragile):
"Save changes" # key changes every time copy changes
"msg1", "label2" # opaque
Three rules:
- The key describes the role, not the content.
- Namespace by feature, then by sub-feature.
- Don’t bury locale in the key.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Concatenating UI fragments | Word order is fixed by English | Single string with named placeholders |
Positional %s %s |
Translator can’t reorder | Named {username}, {date} |
Only one and other plural categories |
Russian, Arabic, Polish break | Author all CLDR forms |
| No translator comment | Translator guesses; gets it wrong | Comment every non-trivial string |
| Idioms in source | No literal translation | Rewrite to the underlying meaning |
| Text baked into images | Untranslatable without re-rendering | HTML overlay or SVG <text> |
References
- Unicode CLDR: Plural Rules
- ICU: Formatting Messages
- Mozilla L10n: Best practices for developers