Zum Hauptinhalt springen
🔧 ProgrammierungWebKit Features for Safari 27.0(17.09.2026 um 13:30 Uhr)
🔧 ProgrammierungWebKit Features for Safari 27.0(17.09.2026 um 13:30 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

5 ARB and ICU edge cases I wish I'd tested earlier

When I first added localization-file conversion, the job looked almost embarrassingly small: parse a file, copy the strings into a map, serialize that map in another format.

Then I tried it on real Flutter projects.

The converted files parsed correctly, which felt like success, but some of them had quietly lost translator context. Others kept the text and damaged a placeholder. ICU plurals were the most misleading case: a message could be perfectly valid and still be wrong for the target language.

These are the five fixtures I now reach for before I trust a converter.

1. @key is not another string

An ARB message can have a companion metadata entry:

{
  "welcome": "Welcome, {name}!",
  "@welcome": {
    "description": "Greeting shown after sign-in",
    "placeholders": {
      "name": {
        "type": "String"
      }
    }
  }
}

welcome is the text. @welcome tells Flutter and the translator what that text means.

My first parser skipped every key beginning with @. That was convenient because metadata should not appear in the translation list. It also meant the conversion was not lossless. A round trip restored the greeting but forgot its description and the type of name.

The model I use internally now looks more like this:

id: "welcome"
value: "Welcome, {name}!"
description: "Greeting shown after sign-in"
placeholders: ["name"]

That does not magically solve export. CSV, for example, may have nowhere to put a placeholder type. The important part is knowing what was lost. Depending on the target format, I can keep metadata in comments, write a sidecar file, or show a warning.

I also test @missing without a matching missing message. Orphan metadata looks odd, but it turns up in repositories after keys are renamed or deleted.

2. A valid plural can still be wrong for the locale

This is enough for most English cardinal plurals:

{count, plural,
  one {# file}
  other {# files}
}

Using the same branches for Russian loses information:

{count, plural,
  one {# файл}
  few {# файла}
  many {# файлов}
  other {# файла}
}

English normally needs one and other. Russian uses one, few, many, plus the required other fallback. Arabic may use all six named categories: zero, one, two, few, many, and other.

So I treat validation as two separate questions:

  1. Does the ICU message parse?
  2. Does it cover the categories used by this locale?

Checking only the first question catches unbalanced braces and a missing other, but it will not tell you that the Russian few branch is absent.

Exact selectors need to survive as well:

{count, plural,
  =0 {No files}
  one {One file}
  other {# files}
}

=0 is an exact-number override. It is not the same thing as the locale category zero, and a converter should not merge the two.

Then there is nesting:

{gender, select,
  female {{count, plural, one {She has one task} other {She has # tasks}}}
  male   {{count, plural, one {He has one task} other {He has # tasks}}}
  other  {{count, plural, one {They have one task} other {They have # tasks}}}
}

This was the point where I stopped trying to count braces with regular expressions.

3. One value has to satisfy both JSON and ICU

ARB is JSON, but its string values may contain another language: ICU MessageFormat. The converter has to get through both layers without "helping" too much.

{
  "receipt": "Line 1\nLine 2: \"{total}\"",
  "path": "C:\\Exports\\{locale}\\messages.json",
  "emoji": "Saved ✓ 🚀"
}

After JSON parsing, receipt contains a real newline. path contains backslashes and an ICU-style placeholder. The last value is a quick check that the pipeline is genuinely Unicode-safe.

The bugs here are small and annoying:

  • \n becomes the visible characters \ and n;
  • an escaped backslash is escaped a second time;
  • UTF-8 is saved with an unexpected byte-order mark;
  • ICU apostrophe quoting changes in one direction of the conversion;
  • Unicode normalization changes a value that looks identical on screen.

I compare decoded values, not their JSON spelling. These two strings represent the same character:

"\u2713"
"✓"

Byte equality would fail that test for no useful reason. On the other hand, a changed non-breaking space can look fine in a diff and still break the UI.

4. Not every brace is a placeholder

Localization projects rarely agree on one placeholder dialect:

Hello, {name}
Downloaded %s of %d files
You have :count messages
Welcome, {{user}}
Price: %(amount)s

Unless the user explicitly asks for a syntax migration, I leave those tokens alone. I also compare them as a multiset rather than a set:

input:  ["{name}", "{count}", "{count}"]
output: ["{name}", "{count}", "{count}"]

The repeated {count} matters.

The opposite problem is detecting placeholders where there are none:

CSS: .button { color: red; }
Discount: 20%
Object: {"id": 42}

A single broad regular expression will eventually eat ordinary text. Format-aware parsing is better. If that is not possible, I would rather ask which placeholder dialect a file uses than silently guess.

ARB gives us one extra consistency check. Tokens in the message should agree with the placeholders object in @key. If the text contains {name} but the metadata only declares count, that deserves a warning.

5. "It parses" is a very low bar for a round trip

Consider this ARB file:

{
  "@@locale": "en",
  "save": "Save",
  "@save": {
    "description": "Button label, not a noun"
  }
}

A simple CSV export might be:

key,value
save,Save

The CSV is valid. Turning it back into this ARB is also valid:

{
  "save": "Save"
}

But the locale and the translator's context have disappeared.

For a round-trip test, I compare message IDs, decoded values, placeholder counts, ICU selectors, metadata, and locale information. I do not require the same key order, whitespace, or Unicode escape style.

When the target format cannot represent something, I want the result described plainly:

lossless
lossy (with a reason)
unsupported

Returning a green checkmark merely because the output parses is how quiet data loss reaches a production translation file.

The small fixture I keep around

This one file covers most of the cases above:

{
  "@@locale": "en",
  "welcome": "Welcome, {name}!\nYou have {count, plural, =0 {no tasks} one {# task} other {# tasks}}.",
  "@welcome": {
    "description": "Dashboard greeting",
    "placeholders": {
      "name": {"type": "String"},
      "count": {"type": "int"}
    }
  },
  "exportPath": "C:\\Exports\\{name}\\✓"
}

For each target format, I verify what it can preserve and expect an explicit limitation for everything else. Then I convert it back and compare meaning rather than bytes.

I put two tools I use while chasing these cases online: an ARB-to-JSON converter and an ICU plural validator. They work without signup.

One honest caveat: the converter currently extracts translatable keys and values but does not carry ARB @key metadata into flat target formats. Treat that conversion as lossy.

Disclosure: both tools are part of Localization.One, a product I am building. If you have a file that breaks either tool, a minimal reproducible example would be genuinely useful.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten 5 ARB and ICU edge cases I wish I'd tested earlier

Thematisch verwandte Begriffe: edge, cases, wish, tested · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-61591 | djust provides Phoenix LiveView-style reactive server-side rendering for…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
News ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

↗ Original-Quelle