StringLane

Browse docsWorking with ICU Plurals and Select Forms
Tutorial

Working with ICU Plurals and Select Forms

Write, validate, and fix ICU MessageFormat plural and select strings directly in StringLane, without leaving the editor.

Last updated

ICU MessageFormat is a standard for writing strings that change based on a number or a choice: things like "1 item" vs "2 items", or gender-specific phrasing. Flutter ARB, iOS .stringsdict, and i18next all support ICU. StringLane has built-in helpers to write and validate these strings correctly.

New to the syntax? Start with What Is ICU MessageFormat? for a plain-English overview, then come back here to write and validate it in StringLane.

What is ICU MessageFormat?#

A basic plural string looks like this:

{count, plural, one {# item} other {# items}}

Breaking it down:

  • count: the variable name
  • plural: the ICU message type
  • one, other: plural categories that apply to different numbers
  • # item: the text, where # is replaced by the number at runtime

Plural categories#

ICU defines six plural categories. Not all languages use all six:

CategoryWhen it applies (English)
zeroExactly 0
oneExactly 1
twoExactly 2
fewSmall numbers (language-specific)
manyLarge numbers (language-specific)
otherAll other cases: always required

English only uses one and other. Arabic uses all six. Ukrainian uses one, few, many, and other. StringLane's validation engine checks that each locale has the plural categories required for that language.

Select forms#

select works like a conditional:

{gender, select, male {He edited} female {She edited} other {They edited}}

Use select for grammatically gendered strings. Unlike plural, select has no standard category set. You define the options yourself. other is always required as a fallback.

Writing plurals in StringLane#

Using the ICU Format Helper#

When you're editing a cell, click the {} button next to the value (or press ⌘I) to open the ICU Format Helper. It shows a guided form where you fill in each plural category. StringLane assembles the correct ICU syntax from your inputs.

The ICU button is visible on every row of an ICU-supporting format. You can use it to promote a plain string into an ICU plural without typing the syntax first.

ICU editor raw tab showing the ICU MessageFormat string being edited

Using the Visual ICU Editor#

For more complex strings, the Visual ICU Editor shows each category as its own field and previews the output in real time. The editor opens on the Build tab; switch to Raw to drop into the ICU textarea, or Reference for a copy-ready cheat-sheet of plural / select / selectordinal templates.

Per-key detail pane showing a plural key with one locale panel per language and per-quantity rows

The visual editor validates as you type, if a placeholder like # is missing in one category but present in another, a warning appears immediately.

Per-locale plural panels#

Plural keys render as one panel per locale in the detail pane. Each panel has a {} button in its header that pre-fills the ICU editor's Build tab with all current plural forms for that locale. Each quantity row has its own value field, character-width bar, and validation rail.

When a locale needs a plural category that the base locale doesn't have (e.g. Ukrainian needs few and many but English only has one/other), the Add form: chips at the bottom of that locale's panel highlight the CLDR-required categories so they stand out from optional ones.

Understanding ICU validation badges#

StringLane validates every translation when a project loads and after each edit. ICU issues appear as a red ICU badge on the affected cell.

Common ICU errors and their fixes:

ErrorCauseFix
ICU parse errorMismatched braces { }, or invalid syntaxCheck that every opening { has a matching }
Missing "other" categoryThe other clause is absentAdd other {…}. It is always required
Placeholder mismatch# or a named variable is in some categories but not othersUse the visual editor to add the placeholder to every category
Literal # in pluralA bare # in an ARB plural branch — Flutter will not substitute itReplace # with {yourVariable}. See below

Click the ICU badge to see the full error message in a tooltip.

Issues panel listing every validation problem as its own row, each naming the key and locale with a Fix with AI button, and Fix all with AI at the foot

Fix-with-AI for ICU errors#

Any plural or select string that fails to parse, or is missing the other category, gets a Fix with AI button on its row in the Issues Panel. Clicking it asks the AI to repair the syntax while preserving your translation text. The footer Fix all with AI sweeps every affected row in one pass. See How to Fix Issues with AI.

CLDR-aware plural translate#

When you click ✨ on a plural key, StringLane auto-fills every quantity the target language requires from CLDR rules, translating into Ukrainian from an English source that has only one/other produces all four forms (one, few, many, other) in one pass. No need to manually add + few / + many first.

Flutter renders # literally#

This one costs people a release, so it gets its own section.

In standard ICU, # inside a plural branch is replaced by the number at runtime. Flutter's gen-l10n does not implement the pound token. Write this in an ARB file:

{count, plural, one {# item} other {# items}}

…and your Flutter app displays a literal "# items" on screen. Not "3 items". The #.

Use the explicit variable instead:

{count, plural, one {{count} item} other {{count} items}}

StringLane checks for this on ARB projects. Every standalone # in a plural branch raises a Literal # in plural warning in the Issues Panel — in every locale including your base one, because the bug is in the string, not the translation. Each row has a Fix with AI button that rewrites # to the plural variable, and it tries that deterministically before calling the model.

Deliberate literals are safe: rank #1 and #{count} are not flagged. Only a bare # standing where the number should be.

The ICU editor is format-aware too — open it on an ARB project and every template and example it offers uses {count} rather than #, so following the built-in guidance cannot produce the bug.

This applies to ARB only. iOS .strings / .stringsdict, Android XML and i18next all substitute # correctly and are not checked.

Common mistakes#

Missing other category. Every plural and select string must have an other clause: no exceptions. This is the most common ICU error. Fix:

{count, plural, one {# item} other {# items}}

Wrong variable name. In ARB, the placeholder in the ICU string must match the ARB metadata. If your key has @description saying the variable is itemCount, use itemCount in the ICU string, not count or n.

Copying English plural to languages with more categories. Ukrainian, Arabic, Polish, and others need few and many in addition to one and other. StringLane flags this as a validation warning. Use the visual editor to add the missing categories.

What's next#