Add ios_lint_localization_placeholder_changes action#738
Draft
jkmassel wants to merge 1 commit into
Draft
Conversation
Collaborator
Generated by 🚫 Danger |
e329223 to
cbbfbfd
Compare
Adds a platform-agnostic `StringPlaceholdersHelper` primitive (`placeholder_signature`, `placeholders_compatible?`, `incompatible_placeholder_changes`) and an iOS action that fails when an existing localization key's source-language value changes its format placeholders — count, position, or argument type — between two versions of the base `.strings` file. Such a change silently breaks every existing translation filed under that key. Complements `ios_lint_localizations` on the temporal (base↔base across versions) axis. The action aborts (by default, via `check_duplicate_keys`) when either input file defines a key more than once, since `plutil` keeps only the last value and a duplicate could otherwise hide a real change; and it reports a clean error when an input file cannot be parsed. As part of the duplicate-key check, `StringsFileValidationHelper.find_duplicated_keys` now also handles unquoted keys (valid `.strings` syntax, common in `InfoPlist.strings`). Part of #736.
cbbfbfd to
d2e4bc3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does it do?
Adds a placeholder-compatibility guardrail for localization source strings, as requested in #736.
Existing translations are keyed by a string's key, not its English text. If an existing key's source value changes its format placeholders — count, position, or argument type — every translation filed under that key silently breaks (or crashes) at the new call site. This is a different axis from
ios_lint_localizations, which compares each translation against the base language at a single point in time; this compares base ↔ base across versions (temporal).1. Reusable primitive —
Fastlane::Helper::StringPlaceholdersHelperPure Ruby, no file I/O, platform-agnostic (the
%@/%1$dplaceholder syntax is shared by iOS and Android):placeholder_signature(value)— canonical signature of a value's placeholders (''if none), e.g."1:int,2:object".placeholders_compatible?(a, b)—signature(a) == signature(b).incompatible_placeholder_changes(old_hash, new_hash)— keys present in both whose signature differs, each{ key:, old:, new:, old_signature:, new_signature: }. New/removed keys are ignored on purpose: copy that needs a fresh translation is expected to land under a new key.The signature is invariant to benign copy edits and to reordering equivalent positional args, but sensitive to count/position/type changes.
%d↔%iare compatible (bothint);%d↔%@are not.%%is skipped; width/precision/length modifiers are ignored for type-classing.2. iOS action —
ios_lint_localization_placeholder_changesTakes
old_file/new_file(the previous and newly-generated base.strings), parses them via the existingL10nHelper.read_strings_file_as_hash(plutil), runs the primitive, and reports. Mirrorsios_lint_localizations:abort_on_violations(defaulttrue) andallow_retry(interactive retry loop).Worked examples (match the issue spec exactly — verified by tests)
Just textHello %@1:object%1$d items in %2$@1:int,2:object%2$@ told by %1$@1:object,2:object100%% sure about %@1:object%d likes1:intNotes / scope
strings.xmlparsing (Nokogiri, plurals, string-arrays, distinct escaping rules) is non-trivial and would roughly double this diff. The primitive is already platform-agnostic, so the follow-up is just a thinandroid_*parse-and-call wrapper.L10nHelper.read_strings_file_as_hashrather than adding a second.stringsparser — the action is therefore macOS-only by design, likeios_lint_localizations..stringsdict/ ICU plurals are out of scope (open question in the issue; likely v2).MIGRATION.mdentry — purely additive.Test Plan
bundle exec rubocop— no violations (full repo, 204 files).bundle exec rspec— 36 new examples pass (30 helper + 6 action); rest of the suite green. The one failinggit_helperlfs test is environmental (git-lfsnot installed locally) and unrelated to this change.%%, nil/whitespace, non-ASCII, escapes, mixed positional/non-positional, width/precision/length modifiers,%d↔%i)..stringsfixtures parsed byplutil: no-op, abort,abort_on_violations: false, add/remove ignored, retry-after-fix, missing file.Related issues
fastlane/helpers/string_placeholders.rb+ thevalidate_string_placeholderslane). WPiOS will adopt the toolkit version and delete the project-side copy once this lands.Checklist before requesting a review
bundle exec rubocopto test for code style violations and recommendations.specs/*_spec.rb) if applicable.bundle exec rspecto run the whole test suite and ensure all your tests pass.CHANGELOG.mdfile to describe your changes under the appropriate existing###subsection of the existing## Trunksection.MIGRATION.mdfile — N/A, purely additive.