Skip to content

[FEAT] Add CLDR plural form support to localization system #706

@PawiX25

Description

@PawiX25

Problem

The current localization system (FlowLocalizations) uses simple string interpolation with {count} or {} placeholders for numeric values. This doesn't support grammatical plural forms, which differ across languages.

For example, the key tabs.home.transactionsCount with value "{count} transactions" always shows the same noun form regardless of the count:

  • English: "1 transactions" ❌ (should be "1 transaction")
  • Polish: "1 transakcji" ❌ (should be "1 transakcja", and "2 transakcje" for few)
  • Russian: "1 транзакций" ❌ (should be "1 транзакция", and "2 транзакции" for few)

This affects all 14 supported languages — some have simple singular/plural (English, German), while others have complex plural rules (Polish, Russian, Czech, Arabic).

Proposed Solution

Add CLDR-based plural category resolution to FlowLocalizations.getTransalation():

  1. New static method _pluralCategory(num n, String langCode) that returns the appropriate CLDR plural category (one, few, many, two, zero, other) based on the language and count value.

  2. Modified num handling in getTransalation(): When replace is a num, the system checks for a suffixed key (e.g., key.one, key.few, key.many) before falling back to the base key.

  3. Plural form keys in l10n JSON files: Languages can optionally define suffixed keys for each plural form. If a suffixed key doesn't exist, the base key is used (fully backward-compatible).

CLDR Rules Coverage (all 14 Flow languages)

Language Code Categories Rule
Polish pl one/few/many one (1), few (2-4 excl. 12-14), many (rest)
Russian ru one/few/many one (%10=1, %100≠11), few (%10=2-4, %100≠12-14), many
Ukrainian uk one/few/many same as Russian
Belarusian be one/few/many same as Russian
Czech cs one/few/other one (1), few (2-4), other
Arabic ar zero/one/two/few/many/other full CLDR Arabic rules
French fr one/other one (0-1), other
Persian fa one/other one (0-1), other
English en one/other one (1), other
German de one/other one (1), other
Italian it one/other one (1), other
Spanish es one/other one (1), other
Turkish tr one/other one (1), other
Mongolian mn one/other one (1), other

Example JSON (Polish)

"tabs.home.transactionsCount": "{count} transakcji",
"tabs.home.transactionsCount.one": "{count} transakcja",
"tabs.home.transactionsCount.few": "{count} transakcje",
"tabs.home.transactionsCount.many": "{count} transakcji"

Backward Compatibility

  • If no suffixed keys exist for a language, the base key is used as before
  • No changes needed in calling code — the .t(context, count) API is unchanged
  • Languages can incrementally add plural forms for any key

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions