Flutter gen-l10n Errors: What Each Means and the Fix
The errors flutter gen-l10n prints for ARB files, quoted exactly from Flutter 3.47: ICU syntax, placeholder types, locale files. What causes each and the fix.
Last updated
flutter gen-l10n stops on two kinds of problem: ARB values that are not valid ICU MessageFormat, and ARB files or placeholders it cannot turn into Dart. Each section below quotes one error exactly as Flutter 3.47.0 prints it, says what causes it, and gives the fix. Every error here exits with code 1, so the build stops.
Errors in an ICU value start with the file and key, then show the value with a caret under the problem:
[app_de.arb:items] ICU Syntax Error: Plural expressions must have an "other" case.
{count, plural, one {{count} Artikel}}
^
Found syntax errors.ICU syntax errors#
Plural expressions must have an "other" case#
ICU Syntax Error: Plural expressions must have an "other" case.Cause: a plural with no other branch, in the template or in any translation.
Fix: add other. It is required in every language, because it is the fallback for any count no other branch matches:
"items": "{count, plural, one {{count} Artikel} other {{count} Artikel}}"Select expressions must have an "other" case#
ICU Syntax Error: Select expressions must have an "other" case.Cause: a select with only named branches, such as {gender, select, male {He} female {She}}.
Fix: add other {They}. It is used for any value that matches no branch.
Plural expressions case must be one of "zero", "one", "two", "few", "many", or "other"#
ICU Syntax Error: Plural expressions case must be one of "zero", "one", "two", "few", "many", or "other".Cause: a branch name that is not a CLDR plural category, such as single or plural.
Fix: use the category names, or an exact match. gen-l10n builds only =0, =1 and =2 as exact matches; any other, such as =3, also fails the whole file, and StringLane reports it as icu_plural_selector_unsupported. Which categories a language needs is set by CLDR: English uses one and other, Ukrainian one, few, many and other. See What Is ICU MessageFormat?.
Expected "}" but found no tokens#
ICU Syntax Error: Expected "}" but found no tokens.Cause: unbalanced braces, almost always a missing closing } at the end of a nested plural or select. The caret points at the end of the value.
Fix: count the braces. Each branch is name {text}, a placeholder inside a branch adds its own pair, and the whole expression closes with one more }.
Placeholder errors#
Placeholders used in plurals must be of type 'num' or 'int'#
Placeholders used in plurals must be of type 'num' or 'int'Cause: the plural variable is declared with another type in its @key block, usually "type": "String".
Fix: declare it as int or num, or leave the type out and let gen-l10n infer num.
Placeholders used in selects must be of type 'String'#
Placeholders used in selects must be of type 'String'Cause: the select variable is declared as int, bool or an enum.
Fix: declare it as String and pass a string, such as gender.name for an enum.
The placeholder has its "type" resource attribute set to a different type#
For the message "n" the placeholder "count" has its "type" resource attribute set to the type "String" in locale "de", but it is "int" in the template placeholder. For compatibility with template placeholder, change the "type" attribute to "int".Cause: a translated file carries its own @key block with a different type than the template.
Fix: change the type to match the template, or delete the @key block from the translation. Only the template file needs metadata.
ARB file and locale errors#
Arb file for a fallback does not exist#
Arb file for a fallback, pt, does not exist, even though
the following locale(s) exist: [pt_BR].Cause: a regional or script file, such as app_pt_BR.arb, with no language-only file beside it.
Fix: add app_pt.arb. It can hold the same strings as app_pt_BR.arb.
The locale specified in @@locale and the arb filename do not match#
The locale specified in @@locale and the arb filename do not match.Cause: a file such as app_de.arb whose @@locale says fr, usually after copying one locale file to start another.
Fix: make them agree, or drop @@locale and let the file name decide.
Invalid ARB resource name#
Invalid ARB resource name "item-count" in LocalFile: '.../lib/l10n/app_en.arb'.
Resources names must be valid Dart method names: they have to be camel case, cannot start with a number or underscore, and cannot contain non-alphanumeric characters.Cause: a key that cannot become a Dart method, such as item-count, 2fa_title or _hidden.
Fix: rename it to camel case, such as itemCount, and update its call sites.
The arb file has the following formatting issue#
The arb file .../app_de.arb has the following formatting issue:
FormatException: Unexpected character (at character 15)
{"items": "x",}
^Cause: the file is not valid JSON. The usual causes are a trailing comma, a missing comma between keys, or an unescaped " inside a value.
Fix: fix the JSON at the character the caret points to. Write a literal double quote inside a value as \".
What gen-l10n does not report as an error#
"de": 1 untranslated message(s)#
"de": 1 untranslated message(s).This is a status line, not an error: gen-l10n exits 0 and generates the code, and the missing string falls back to the template locale. To see which keys, add this to l10n.yaml:
untranslated-messages-file: untranslated.jsonThe next run writes the missing keys per locale:
{
"de": ["checkoutButton"]
}Problems with no message at all#
These generate cleanly, with no error and no warning:
- A translation drops a placeholder. French
"greeting": "Bonjour !"compiles and shows no name. - A translation adds a placeholder.
gen-l10nadds it to the method signature in every locale, so the error appears at your call sites instead of in the ARB file. - A plural with English's categories in a language that needs more, such as Ukrainian with only
oneandother. - A
#in a plural branch, which the generated code keeps as a literal#. - A
selectbranch missing from a translation, which falls back toother. - A plural with both
=1andone(or=0andzero,=2andtwo).gen-l10ntreats each pair as one form and keeps the later one, so the earlier branch is never shown.
Flutter ARB File Format shows each of these with an example.
How do you catch these before running gen-l10n?#
StringLane checks every app_*.arb file against the template: invalid ICU, missing other, missing plural categories, exact plural matches other than =0, =1 and =2, branches a =1 and one pair hides, dropped or extra placeholders, missing select branches, # in plurals, missing keys, and keys only a translation holds. It reads apostrophes the way gen-l10n does: as plain text unless l10n.yaml sets use-escaping: true. Unlike gen-l10n, it reports the problems that build. The free CLI runs the checks from a terminal or CI, exits 1 when a locale is broken, and writes nothing into your repository:
npx @stringlane/cli@latest check .Next steps#
- Flutter ARB File Format: a complete example file and what each part generates.
- Localize Your Flutter App End to End: setup through
flutter gen-l10n. - Inspect and Check a Project: run the checks in CI.
Frequently asked questions
- Does flutter gen-l10n fail on untranslated messages?
- No. It prints a line such as "de": 1 untranslated message(s), exits 0 and generates the code. The missing string falls back to the template locale. Set untranslated-messages-file in l10n.yaml to get a JSON list of the missing keys per locale.
- Why does gen-l10n say Arb file for a fallback does not exist?
- Because you have a regional file such as app_pt_BR.arb without a plain app_pt.arb. gen-l10n needs the language-only file as the fallback for every regional or script variant. Add app_pt.arb, even if it holds the same strings.
- How do I get the list of untranslated messages from gen-l10n?
- Add untranslated-messages-file: untranslated.json to l10n.yaml. After the next flutter gen-l10n, the file maps each locale to the keys it is missing, for example {"de": ["checkoutButton"]}.
- Which gen-l10n problems do not show any error?
- A translation that drops a placeholder, a plural that lacks a category its language needs, a # in a plural branch, and a missing select branch all generate without an error or warning. They need a check that compares each locale against the template.

See every locale side by side
The desktop app flags what is missing or broken on the cell, and saves to your files.
14-day trial, no email