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

Source: https://stringlane.app/docs/flutter-gen-l10n-errors
Last updated: 2026-10-22

`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:

```text
[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

```text
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:

```json
"items": "{count, plural, one {{count} Artikel} other {{count} Artikel}}"
```

### Select expressions must have an "other" case

```text
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"

```text
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?](/docs/icu-messageformat-explained).

### Expected "}" but found no tokens

```text
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'

```text
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'

```text
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

```text
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

```text
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

```text
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

```text
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

```text
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)

```text
"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`:

```yaml
untranslated-messages-file: untranslated.json
```

The next run writes the missing keys per locale:

```json
{
  "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-l10n` adds 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 `one` and `other`.
- **A `#` in a plural branch**, which the generated code keeps as a literal `#`.
- **A `select` branch missing** from a translation, which falls back to `other`.
- **A plural with both `=1` and `one`** (or `=0` and `zero`, `=2` and `two`). `gen-l10n` treats each pair as one form and keeps the later one, so the earlier branch is never shown.

[Flutter ARB File Format](/docs/what-is-an-arb-file) 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:

```sh
npx @stringlane/cli@latest check .
```

## Next steps

- **[Flutter ARB File Format](/docs/what-is-an-arb-file)**: a complete example file and what each part generates.
- **[Localize Your Flutter App End to End](/docs/localize-flutter-app)**: setup through `flutter gen-l10n`.
- **[Inspect and Check a Project](/docs/cli/inspect-and-check)**: 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.
