Skip to content

check-translation-completeness incorrectly reports language-specific plural forms as unused / can be removed #65229

Description

@roykoand

Under which category would you file this issue?

Airflow Core

Apache Airflow version

not related to airflow version

What happened and how to reproduce it?

Steps to reproduce

breeze ui check-translation-completeness --language pl

Among the output you will see keys like dagRun_few/dagRun_many, etc. listed as unused:

Unused keys (not required, can be removed):
...
    common.json:
      assetEvent_few
      assetEvent_many
      asset_few
      asset_many
      backfill_few
      backfill_many
      createdAssetEvent_few
      createdAssetEvent_many
      dagRun_few
      dagRun_many

Root cause

The logic in expand_plural_keys (dev/breeze/src/airflow_breeze/commands/ui_commands.py) skips expanding plural forms for a base key when none of the English values for that base contain the literal string {{count}}:

any_has_count = any(COUNT_PLACEHOLDER in en_key_to_value.get(k, "") for k in en_keys_for_base)                                                                                                                                                      
if not any_has_count:
    continue

The English translations for dagRun are:

{
    "dagRun_one": "Dag Run",                                                                                                                                                                                                                            
    "dagRun_other": "Dag Runs"
}

Neither value contains {{count}}, so the script concludes no plural expansion is needed for non-English locales and marks dagRun_few / dagRun_many as unused.

However, i18next uses the count option passed to translate() for two independent purposes:

  1. Plural form selection — choosing _one, _few, _many, _other based on the numeric value.
  2. Interpolation — inserting the number into the translated string via {{count}}.

The script only accounts for purpose 2. Purpose 1 can occur even when {{count}} never appears in the string value. For example in TriggeredRuns.tsx (link):

translate("dagRun_other", { count: dagRuns.length })                                                                                                                                                                                                

Here count is passed solely to trigger plural form selection. For Polish:

  • count: 2 → i18next selects dagRun_few ("Wykonania")
  • count: 6 → i18next selects dagRun_many ("Wykonań")

Impact

Any locale with grammatical plural forms where the count is displayed separately (not via {{count}} inside the translation string) will have those keys incorrectly flagged as unused. Running --remove-unused would silently delete valid, necessary translations.

What you think should happen instead?

I suggest a bit more accurate heuristic for check-translation-completeness: if a key base already has multiple English plural forms (e.g. dagRun_other or dagRun_one exist in English), treat it as a plural key requiring all forms for the target language — regardless of whether {{count}} appears in the values.

Operating System

No response

Deployment

None

Apache Airflow Provider(s)

No response

Versions of Apache Airflow Providers

No response

Official Helm Chart version

Not Applicable

Kubernetes Version

No response

Helm Chart configuration

No response

Docker Image customizations

No response

Anything else?

No response

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dev-toolskind:bugThis is a clearly a bugneeds-triagelabel for new issues that we didn't triage yet

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions