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.
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.
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.
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.