Skip to content

Enforce localization key immutability in CI #25790

Description

@jkmassel

Summary

The regular-string String Catalog fold treats each (key, locale) cell's stored English as the record of what its translation was made for. If an explicit-key string's English is reworded in place (the key stays, only value: changes), xcstringstool sync silently keeps the old translations — now stale for the new text — and the fold can't tell a translation of the old English from a current one.

We've decided localization keys are immutable: rewording requires a new key. generate_strings_catalog now hard-fails on a reworded key (CatalogHelper.reworded_keysenforce_immutable_source_keys in fastlane/lanes/localization_catalog.rb).

The gap this issue tracks

That check compares freshly-extracted English against the persisted catalog, which works for the local manual lane but not in CI: Localizable.xcstrings is gitignored and generated transiently, so CI has no prior English to diff against. A reword can therefore merge without CI catching it.

Proposal

Add a CI lint on the committed English .strings — the one cross-run baseline we do keep (it's the GlotPress source of truth): fail when an existing key's value changes (same key, different English).

  • A key-as-source reword shows up in the .strings diff as a remove + add → passes (that key genuinely changed).
  • An explicit-key reword shows up as same-key / changed-value → fails, with a message to mint a new key.

This is what makes immutable keys enforced on every PR, not only where the catalog persists.

Escape hatch

Hard-fail everywhere for now, no exceptions. If the friction on cosmetic edits (typo fixes) proves burdensome, add an opt-in ack later — a Reword: commit trailer, or a PR-comment + review gate — that flips reconcile from fail to its keep-and-flag (needs_review) path. The placeholder-format gate runs regardless, so an ack could only ever let through prose drift, never a runtime crash.

References

  • Enforcement today: CatalogHelper.reworded_keys (fastlane/lanes/catalog_helper.rb), enforce_immutable_source_keys (fastlane/lanes/localization_catalog.rb).
  • Rationale + the human ?? AI ?? English contract and immutable-key note: docs/localization-pipeline.md.

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