Compare ICU source and translation messages

Paste the source message and its translation to compare their structure. Confirm the final messages with your production ICU or i18n library.

Runs locally in your browser
Messages are parsed entirely in this browser. They are not uploaded, translated, or stored by VoriTools.
Source ICU message
Translation ICU message
Message compatibility report
—Source arguments
—Translation arguments
—Issues
  • INFOPaste both messages, then run the local compatibility check.

Catch translation contract mistakes before rendering

The checker validates practical ICU MessageFormat structures, including nested arguments, select branches, plural/selectordinal fallbacks, and placeholder parity. It is a review aid, not a replacement for rendering messages with your production i18n library.

How to compare two ICU messages

Paste the message you translate from into “Source ICU message” and its translation into “Translation ICU message”. Both strings are read as ICU MessageFormat, their structure is compared, and every finding is printed on its own line, prefixed OK, CHECK or ERROR.

The three counters above the list show how many arguments each message declares and how many findings were raised. Everything is parsed in this browser: nothing is uploaded, and the page keeps working offline once it has loaded.

  1. Paste the source message into the left field and the translation into the right one, or press Load example to start from a working pair.
  2. Press Check messages. A compatible pair returns a single OK line; every other difference becomes a CHECK or an ERROR line.
  3. Read the report: ERROR marks a broken contract — an argument or an other branch the translation lost, or a message that cannot be parsed; CHECK marks a difference worth reviewing, such as an extra argument, a different type or a select branch only one side has.
  4. Press Copy report to take the findings as plain text, or Clear to empty both fields and start again.
  5. Render the final messages with the ICU or i18n library used in production: this page compares structure, not the wording of the translated text.

What the checker compares and what it skips

What is compared

Arguments are matched by name across the two messages. An argument that exists only in the source is an ERROR, one that exists only in the translation is a CHECK, and when the same name carries a different type — plural in one message, number in the other — the report raises a CHECK naming both types.

For select arguments the branch keys are compared one by one, so a branch dropped in the translation, or added to it, is reported with its name. plural and selectordinal arguments must keep an other branch, and a missing other is an ERROR attributed to the side that lost it. Messages nested inside a branch are parsed recursively, so arguments inside them are counted too.

What is not compared

plural and selectordinal branch values are not compared one by one: a source with one/other against a translation with only other is reported as compatible. The checker also does not read the translated wording, does not check which plural categories the target language needs (few, many, …), and does not render the message with values.

A green report therefore means that both messages declare the same arguments and the same select branches — nothing more. Render both with your production library before shipping, and keep the plural rules of the target language in mind.

Syntax it accepts

The parser reads {name} arguments, plural, selectordinal and select arguments with their branches, offset:N in a plural header, nested messages, the # placeholder inside plural branches, and apostrophe quoting: two apostrophes stand for one literal apostrophe, while a single one opens and closes quoted text. Types such as number, date or time are recorded by name, so a format skeleton like {value, number, ::percent} is accepted.

A message that cannot be read stops the run with an ERROR instead of a report: an unclosed {, a } without its partner, an unclosed apostrophe, an argument name that does not start with a letter or an underscore, or a branch that is not followed by its { … } block.

Recent tools: