iOS and Flutter placeholder checker

Paste a source string and its translations: the tool flags placeholders that were dropped, renamed, changed type or reordered, for Apple formats (%@, %1$@, %lld) and for Flutter ICU messages alike. Your text stays in your browser, and the tool is free with no signup.

Compare placeholders

Translations to check

Compared in your browser: nothing is uploaded.

Check every translation before export, without copy-pasting

LocalePilot is a desktop app that automatically compares the placeholders of all your translations with the source's (%@, %1$@, %lld, ICU), lists them in a QA center and blocks export until a blocking issue is fixed. It is being finalized for macOS: leave your email to hear about the launch.

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.

Frequently asked questions

Which iOS format specifiers are recognized?

%@, %1$@ (and other positions), %d, %ld, %lld, %.2f, %u, %i, %s; %% is treated as text. Rare conversions such as %e or %x are deliberately ignored.

Why is "20 % de remise" flagged?

In a format string, "% d" is a valid specifier (the space is a printf flag), so "%lld % de remise" holds two specifiers for a single argument. Write "%lld %% de remise".

Can I change the order of arguments in a translation?

Yes, by numbering every argument: %1$@ is the first, %2$@ the second, wherever they sit in the sentence. Within one string, everything is numbered or nothing is.

Does the tool also check Flutter ICU messages?

Yes: placeholders in braces are compared with the source's, including inside plural and select branches.

Is my text sent to a server?

No. Your strings, sometimes not yet released, are compared in the page: nothing is sent.

How is this different from LocalePilot?

This tool compares one string at a time. LocalePilot is a desktop app, being finalized for macOS, that compares every key of your ARB, .strings and .xcstrings files and blocks export on a blocking issue.

Get notified at launch

LocalePilot is being finalized. Leave your email to hear when it launches.

  • macOS 12 or later · Ollama or LM Studio optional
  • App interface in English, French, Spanish, Italian and Portuguese.