Handling Strings and Formats: ICU, XLIFF, JSON, and RTL Details
The technical details that make or break text localization: ICU MessageFormat for plurals and gender, XLIFF and JSON interchange, and the right-to-left layout considerations that keep every locale rendering correctly.

Most localization bugs don't originate in translation. They originate in how engineers structure strings, choose file formats, and handle edge cases like plurals, gender agreement, and bidirectional text. A misnamed placeholder silently breaks a Turkish build. A naive concatenation produces nonsensical word order in Japanese. A missing bidi mark flips an entire address in Arabic. These are engineering problems, not linguistic ones, and they require engineering solutions.
This guide covers the core technical decisions that determine whether your strings are robust, testable, and locale-correct: ICU MessageFormat syntax, file format tradeoffs between XLIFF, JSON, and YAML, segmentation and context strategies, text expansion handling, and the specific mechanics of right-to-left rendering. After reading, you'll have concrete patterns, schemas, and CI checks you can apply immediately.
ICU MessageFormat Essentials
ICU MessageFormat is the industry standard for encoding locale-sensitive string logic, plurals, gender selection, and conditional branches, directly into translatable messages. Maintained by the Unicode Consortium, it separates linguistic rules from application code, which means translators handle grammar while developers handle variables.
Plural, Gender, and Select Rules
English has two plural forms (singular and plural). Arabic has six. Polish has four, with complex rules governing which numeral triggers which form. Hardcoding if count == 1 checks in application code is fragile and fails the moment you add a language with different plural categories.
ICU MessageFormat solves this with plural, select, and selectordinal keywords:
{count, plural,
=0 {No messages}
one {# message}
other {# messages}
}
The # symbol is replaced by the numeric value. The plural categories (zero, one, two, few, many, other) are defined by CLDR plural rules, not by the developer. Every locale must include other as a fallback.
Gender selection works similarly:
{gender, select,
female {{name} updated her profile}
male {{name} updated his profile}
other {{name} updated their profile}
}
Nested constructs are valid, you can nest a plural inside a select, but deeply nested messages become difficult for translators to parse. As a rule, limit nesting to two levels and split more complex logic into separate strings.
Placeholder Naming and Safety
Placeholders are the contract between your code and your translators. Poorly named or undocumented placeholders cause mistranslations and runtime crashes.
Follow these conventions:
- Use descriptive names, not positional indices. {userName} is unambiguous; {0} is not.
- Use camelCase consistently across your codebase. Mixed conventions (user_name in one file, userName in another) create confusion.
- Never embed HTML or code logic inside placeholders. If you need formatting, use ICU's built-in number and date skeletons: {deadline, date, medium}.
- Document every placeholder with a developer comment explaining its type and expected content.
A placeholder naming schema might look like this:
| Pattern | Example | Use |
|---|---|---|
| {entityAction} | {fileUploaded} | Event descriptions |
| {count} | {itemCount} | Numeric values for plurals |
| {name} | {userName} | Proper nouns / display names |
| {date, date, skeleton} | {expiryDate, date, yMMMd} | Formatted dates |
Validation and Linting Strategies
ICU syntax errors, an unclosed brace, a missing other clause, should never reach production. Catch them in CI.
Several tools parse and validate ICU messages:
- @formatjs/cli for JavaScript/TypeScript projects can extract and validate ICU messages at build time.
- messageformat-validator checks structural correctness and enforces that all plural categories required by target locales are present.
- Custom lint rules can enforce your naming conventions, flag positional arguments, and reject concatenation patterns.
Ollang can centralize these checks in your localization pipeline and surface errors as part of pull-request feedback, making it easier to enforce ICU correctness across teams. You can see how this integrates into CI by booking a guided walkthrough: Schedule an Ollang demo.
A minimal CI check should:
- Parse every ICU string and reject syntax errors.
- Verify that all required CLDR plural categories exist for each target locale.
- Confirm that placeholder names in translations match the source exactly.
- Flag strings exceeding a maximum nesting depth.
File Format Comparison: XLIFF, JSON, and YAML
Your choice of localization file format affects tooling compatibility, translator context, and how much metadata you can attach to each string. There is no universally correct answer, the right format depends on your stack, your TMS, and how much contextual data you need to ship alongside your strings.
XLIFF 1.2 vs XLIFF 2.1 Feature Matrix
XLIFF (XML Localization Interchange File Format) is an OASIS standard designed specifically for localization interchange. It is the most feature-rich option and the lingua franca of professional translation tools.
| Feature | XLIFF 1.2 | XLIFF 2.1 |
|---|---|---|
| Inline markup (bold, links) | <g>, <x/>, <bx/> tags | Simplified <pc>, <ph>, <sc>/<ec> |
| Segmentation | External or embedded | Native <segment> and <ignorable> |
| Notes / comments | <note> element | <note> with appliesTo attribute |
| Metadata extensibility | Limited namespace support | First-class <mda:metadata> module |
| Validation module | None | Built-in validation module |
| Tool adoption | Universal TMS support | Growing; some tools still 1.2-only |
XLIFF 2.1 is the better technical specification, cleaner inline handling, native segmentation, and modular extensibility. However, XLIFF 1.2 remains more widely supported across legacy TMS platforms. If you're starting a new project, prefer 2.1. If you're integrating with an existing pipeline, verify your toolchain's support first.
JSON and YAML Patterns for Developers
For web and mobile projects, JSON is the most common localization format due to its simplicity and native support in JavaScript ecosystems. YAML is popular in Ruby on Rails and some game engines.
A flat JSON structure:
{
"nav.home": "Home",
"nav.settings": "Settings",
"error.notFound": "Page not found"
}
A nested JSON structure:
{
"nav": {
"home": "Home",
"settings": "Settings"
},
"error": {
"notFound": "Page not found"
}
}
Flat structures are easier to search and diff. Nested structures group related strings logically but can cause merge conflicts when multiple developers edit the same namespace. Pick one convention and enforce it project-wide.
YAML adds readability but introduces indentation-sensitivity bugs and inconsistent multiline string handling across parsers. If your stack doesn't require YAML, JSON is the safer choice.
Keys vs Source-as-Key: When to Use Each
Two schools of thought exist for identifying translatable strings:
- Semantic keys (error.auth.invalidPassword) decouple the identifier from the English text. Changing the source copy doesn't break references. This is the preferred approach for large, long-lived projects where strings are reused across components.
- Source-as-key ("Invalid password" is both the key and the English value) reduces boilerplate and is popular in frameworks like react-intl and gettext. The tradeoff: changing the English text means updating every translation file's key, and identical English strings with different meanings (e.g., "Save" as a verb vs. "Save" as a noun) collide.
| Criterion | Semantic Keys | Source-as-Key |
|---|---|---|
| Rename safety | High | Low |
| Disambiguation | Built-in via key | Requires manual context |
| Developer ergonomics | More boilerplate | Less boilerplate |
| Best for | Large apps, shared components | Small apps, rapid prototyping |
For enterprise-scale localization, especially when working with professional translators and a TMS, semantic keys with mandatory developer comments provide the most reliable foundation.
Segmentation, Context, and Developer Comments
Translators working without context make mistakes. Context is not optional; it is a direct input to translation quality.
SRX Segmentation Rules
Segmentation is the process of splitting text into translatable units, typically sentences. The SRX (Segmentation Rules eXchange) standard defines language-specific rules for where to break text.
Default segmentation breaks at sentence boundaries (periods, exclamation marks, question marks). But abbreviations like "Dr.", "U.S.A.", and "e.g." create false positives. SRX rules define exceptions:
<rule break="no">
<beforebreak>\b(Dr|Mr|Mrs|Ms|Prof)\.</beforebreak>
<afterbreak>\s</afterbreak>
</rule>
Most TMS platforms apply SRX rules automatically, but engineers should be aware of segmentation when:
- Strings contain multiple sentences (which should generally be split into separate keys).
- UI text includes abbreviations or domain-specific notation that could trigger false breaks.
- Content is pre-segmented in code, which can conflict with TMS segmentation.
The safest practice: one sentence per string key. This gives translators maximum flexibility to reorder words without being constrained by adjacent sentences.
Developer Comments and Screenshot Linking
A developer comment should answer three questions: Where does this string appear? What do the placeholders contain? Are there length or formatting constraints?
In JSON, comments can be added via a convention like _comment suffixes or a companion metadata file. In XLIFF, the <note> element is purpose-built:
<trans-unit id="btn.save">
<source>Save</source>
<note from="developer">Button label in the document editor toolbar. Max 10 characters. "Save" as a verb (action), not a noun.</note>
</trans-unit>
Screenshot linking takes context further. Attach a screenshot URL or reference ID to each string so translators see the exact UI where the text appears. XLIFF 2.1's metadata module supports this natively. For JSON-based workflows, tools like Lokalise and Phrase allow screenshot uploads tied to specific keys.
Ollang and similar platforms that handle the full localization pipeline, from string extraction to quality review, can integrate context delivery directly into translation workflows, reducing the back-and-forth that slows down release cycles.
Ready to see Ollang in action?
Talk to our team about your localization goals and see how the Ollang platform fits your workflow.
Inline Markup: HTML and Markdown in Strings
Strings that contain formatting, bold text, hyperlinks, line breaks, are among the most error-prone elements in localization.
Safe Patterns for Rich Text
Embedding raw HTML in translatable strings is risky. Translators may accidentally break tags, and different target languages may need different markup placement.
Preferred approaches, ranked by safety:
- ICU-style tags with post-processing. Use placeholder tags like <bold>important</bold> in your strings and map them to actual HTML at render time. Libraries like react-intl and @fluent/bundle support this pattern.
- XLIFF inline elements. XLIFF's <pc> (paired code) and <ph> (placeholder) elements explicitly mark formatting, keeping the structure visible to translation tools.
- Markdown in strings. Acceptable for simple formatting (bold, italic) if your rendering pipeline handles Markdown. Avoid complex Markdown (tables, nested lists) in translatable strings.
- Raw HTML. Last resort. If unavoidable, validate that all tags are closed and that translators haven't altered tag attributes.
Never split a sentence across multiple strings to avoid inline markup. The resulting fragments cannot be reordered for languages with different word order, producing ungrammatical output.
Text Expansion, Truncation, and Length Constraints
English is one of the most compact major languages. When you translate into German, Finnish, or Thai, strings grow, sometimes dramatically.
Expansion Ratios and Truncation Guards
IBM's globalization guidelines and W3C internationalization best practices document typical expansion ratios:
| Source Length (English chars) | Typical Expansion |
|---|---|
| 1-10 | 200-300% |
| 11-20 | 180-200% |
| 21-30 | 160-180% |
| 31-50 | 140-160% |
| 51-70 | 130-140% |
| 70+ | 120-130% |
Short strings expand the most. A 4-character English button label like "Save" becomes "Speichern" (10 characters) in German, a 150% increase.
To guard against truncation:
- Design UI with expansion budgets. Allow at least 40% extra space for most elements, more for short labels.
- Set maxLength constraints in your string metadata and validate translations against them in CI.
- Use CSS text-overflow: ellipsis as a visual safety net, but never as a substitute for proper length management, truncated translations are broken translations.
- Pseudo-localization during development simulates expansion by padding strings with accented characters and extra length. Tools like pseudo-localize can automate this.
A CI check for length constraints (assuming a JSON file whose values are objects with value and optional maxLength):
# Outputs keys whose translated value exceeds maxLength
jq -r 'to_entries[] | select(.value.maxLength and ((.value.value | length) > .value.maxLength)) | .key' translations.json
Right-to-Left (RTL) Rendering
Supporting Arabic, Hebrew, Farsi, and Urdu means confronting bidirectional text rendering, one of the most technically nuanced areas of internationalization.
Bidi Marks and the Unicode Bidi Algorithm
The Unicode Bidirectional Algorithm (UBA) determines display order for mixed-direction text. It works well for pure RTL or pure LTR content but struggles with embedded numbers, brand names, and punctuation.
Bidi control characters solve ambiguous cases:
| Character | Unicode | Purpose |
|---|---|---|
| LRM (Left-to-Right Mark) | U+200E | Forces LTR ordering for adjacent characters |
| RLM (Right-to-Left Mark) | U+200F | Forces RTL ordering for adjacent characters |
| LRE / RLE | U+202A / U+202B | Embedding overrides (discouraged; prefer isolates) |
| LRI / RLI / FSI | U+2066 / U+2067 / U+2068 | Directional isolates (preferred) |
| PDI | U+2069 | Pop directional isolate |
Modern best practice uses directional isolates (LRI, RLI, FSI) rather than the older embedding overrides. Isolates prevent the enclosed text from affecting the bidirectional ordering of surrounding content.
In HTML, use the dir attribute and <bdi> element:
<p dir="rtl">Ù…Ø±ØØ¨Ø§ <bdi>John Smith</bdi>, طلبك جاهز</p>
UI Mirroring and Numeral Systems
RTL localization extends beyond text direction. The entire UI layout should mirror:
- Navigation moves from right to left.
- Icons with directional meaning (arrows, progress indicators) flip horizontally.
- Padding and margins swap sides.
- Icons without directional meaning (a magnifying glass, a trash can) do not flip.
CSS logical properties (margin-inline-start instead of margin-left) handle mirroring automatically and are supported in all modern browsers.
Numeral systems add another layer. While Arabic-speaking countries use both Western Arabic numerals (0-9) and Eastern Arabic numerals (Ù -Ù©), the choice varies by region and context. Hebrew uses Western numerals almost exclusively. Your formatting library should respect the locale's numbering system, ICU's {count, number} handles this correctly when the locale is set properly.
RTL-Specific Linting and Testing
Automated checks for RTL correctness should include:
- Bidi character validation: Flag strings containing deprecated bidi overrides (LRE, RLE, LRO, RLO) and suggest isolates instead.
- Unmatched isolate detection: Every LRI/RLI/FSI must have a corresponding PDI.
- Hardcoded direction detection: Lint CSS for text-align: left, margin-left, float: left and flag them as potential mirroring failures.
- Visual regression testing: Render key screens in an RTL locale and compare against baseline screenshots. Tools like Percy or Chromatic can automate this.
CI Checks, Schemas, and Automation
Localization quality gates belong in CI, not in manual review. Every check described in this article, ICU syntax validation, placeholder matching, length constraints, bidi correctness, can and should run automatically on every pull request.
A recommended CI pipeline for localization:
- Extract strings from source code and compare against the base locale file. Flag new, changed, or deleted keys.
- Validate ICU syntax for every string in every locale file.
- Check placeholder parity, every placeholder in the source must appear in the translation, and vice versa.
- Enforce length constraints where maxLength metadata exists.
- Lint for bidi issues in RTL locale files.
- Run pseudo-localization and verify that no UI element clips or overflows.
- Validate file format schema, ensure JSON files parse correctly, XLIFF files validate against the OASIS schema, and no duplicate keys exist.
A JSON schema for a localization string entry:
{
"type": "object",
"properties": {
"key": { "type": "string", "pattern": "^[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)*$" },
"value": { "type": "string", "minLength": 1 },
"maxLength": { "type": "integer", "minimum": 1 },
"description": { "type": "string" },
"placeholders": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"example": { "type": "string" },
"type": { "enum": ["string", "number", "date"] }
}
}
}
},
"required": ["key", "value", "description"]
}
For teams managing localization across text, software, websites, and legal documents, Ollang provides the execution layer that connects engineering workflows with professional translation and quality review, book a demo to see how it fits your pipeline.
Frequently Asked Questions
What is ICU MessageFormat and why should I use it instead of string concatenation?
ICU MessageFormat is a syntax standard maintained by the Unicode Consortium for encoding plurals, gender selection, and conditional text branches directly into translatable strings. Unlike string concatenation, where you build sentences from fragments like "You have " + count + " items", MessageFormat keeps the entire sentence intact, allowing translators to reorder words freely for each language's grammar. It also handles plural rules automatically based on CLDR data, which is essential for languages with complex plural systems like Arabic (six forms) or Polish (four forms).
Should I use XLIFF or JSON for my localization files?
It depends on your toolchain and context requirements. XLIFF is the richer format: it supports inline markup handling, translator notes, segmentation metadata, and is the standard interchange format for professional TMS platforms. JSON is simpler, integrates natively with JavaScript ecosystems, and is easier for developers to read and edit. For enterprise projects with professional translation workflows, XLIFF (preferably 2.1) provides the most robust foundation. For web apps with simpler localization needs, structured JSON with a companion metadata convention works well. For enterprise teams, platforms like Ollang integrate XLIFF workflows with CI and quality gates, which simplifies management across formats. You can see this end-to-end by joining a live session: Book an Ollang demo.
How do I prevent translated strings from breaking my UI layout?
Design with text expansion in mind. Short English strings can expand by 200-300% in languages like German or Finnish. Set explicit maxLength constraints in your string metadata, validate translations against those constraints in CI, and use CSS techniques like text-overflow: ellipsis as a visual safety net. Run pseudo-localization during development to simulate expansion before real translations arrive. Never hard-code UI element widths to fit English text exactly.
What are the most common RTL rendering mistakes?
The most frequent issues are: using hardcoded margin-left / text-align: left instead of CSS logical properties, failing to insert directional isolate characters around embedded LTR content (like brand names or numbers) in RTL strings, forgetting to mirror directional icons, and using deprecated bidi override characters instead of modern isolates. Automated linting for these patterns, combined with visual regression testing in RTL locales, catches the majority of these bugs before they reach production.
Ready to see Ollang in action?
Talk to our team about your localization goals and see how the Ollang platform fits your workflow.
Take the next step
If you want these practices wired into your pipeline, extraction, ICU validation, placeholder checks, RTL linting, pseudo-localization, and XLIFF interchange, Ollang can help. See a concrete, end-to-end example tailored to your stack: Schedule an Ollang demo.
Published on July 28, 2026