What the tool compares
- Apple formats:
%@, %1$@, %d, %ld, %lld, %.2f, %u, %i and %s, position by position. %% is text.
- Flutter ICU placeholders:
{name}, including inside plural and select branches.
Rare conversions such as %e or %x are deliberately ignored, so that text like "20%en" isn't taken for an error.
The most common mistakes
- A dropped specifier: nothing crashes, but the name or the number vanishes from the screen.
- A changed type:
%lld replaced by %@ reads an integer as an object, with a risk of crash.
- A changed order without positions: to reorder, number every argument (
%1$@, %2$@); mixing numbered and unnumbered is undefined behavior.
- "20 % off" in French: the space before the percent sign makes
% d read as a specifier. Double the sign: %%.
- A renamed ICU placeholder:
{count} turned into {nombre} changes the API of every locale in Flutter.
These cases are detailed in the guide on iOS format specifiers and the guide on ICU placeholders.
Limits of the tool
The tool only compares placeholders: it does not detect two swapped %@ when the arguments are not numbered, nor a badly translated sentence. It does not interpret ICU apostrophe escaping (Flutter's use-escaping option): '{' is read as a brace. It compares one string at a time; for a whole file, use the ARB missing keys checker or an editor that checks every key.