agentsclimarketplace

Ios i18n workflow

Skill HeathWang/hw-cc-plugin/skills/ios-i18n-workflow

Use when iOS apps using SwiftGen L10n need localization work - adding keys, checking missing translations, or cleaning unused entries. Triggered by hardcoded strings, missing L10n references, incomplete translations across multiple languages, or unused localization cleanup requests.From its SKILL.md

Install
npx -y skills add HeathWang/hw-cc-plugin --skill ios-i18n-workflow

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

15.3 KB, as published. Nobody here has run it

iOS Internationalization Workflow

Complete workflow for managing iOS app internationalization (i18n) using SwiftGen L10n code generation. Supports any number of target languages. Covers three main scenarios: internationalizing code, verifying translation completeness, and cleaning unused entries.

When to Use

digraph when_to_use {
    rankdir=TB;
    "User provides code/folder for i18n?" [shape=diamond, style=filled, fillcolor=lightblue];
    "User asks to check missing translations?" [shape=diamond, style=filled, fillcolor=lightgreen];
    "User asks to clean unused i18n?" [shape=diamond, style=filled, fillcolor=lightyellow];
    "Standardize" [shape=box, style=filled, fillcolor=lightcoral];
    "CheckMissing" [shape=box, style=filled, fillcolor=lightcoral];
    "CleanUp" [shape=box, style=filled, fillcolor=lightcoral];

    "User provides code/folder for i18n?" -> "Standardize" [label="Yes\n(Full workflow)"];
    "User asks to check missing translations?" -> "CheckMissing" [label="Yes\n(Verify only)"];
    "User asks to clean unused i18n?" -> "CleanUp" [label="Yes\n(Cleanup only)"];
}

Three main scenarios:

  1. Internationalize code - User provides code or folder: "Here's my view controller, add i18n" → Run full workflow (baseline → ALL target languages → verify → SwiftGen)

  2. Check missing translations - User explicitly asks: "Check if translations are complete" or "Find missing i18n keys" → Run check_missing_localizations.py for each target language and fill gaps

  3. Clean unused entries - User explicitly asks: "Clean unused localizations" or "Remove unused i18n keys" → Run clean_unused_localizations.py with dry-run first

Key principle: Complete translations for ALL target languages before verification and SwiftGen generation.

Do NOT use when:

  • Projects using raw NSLocalizedString without SwiftGen
  • Non-iOS platforms (Android, Web)

Identifying User Intent

Determining which scenario applies:

Scenario 1: Full Workflow

User provides code files or folder paths, says things like "internationalize this" or "add i18n to this code"

Scenario 2: Check Missing

User asks to verify translation completeness with phrases like "check if translations complete" or "find missing i18n keys"

Scenario 3: Cleanup

User requests removal of unused entries with phrases like "clean unused localizations" or "remove unused i18n keys"

Quick decision guide:

  • Provides code/folder → Scenario 1 (Full workflow)
  • Asks to check/verify → Scenario 2 (Check missing)
  • Asks to clean/remove → Scenario 3 (Cleanup)

Red Flags - STOP and Re-read

For Scenario 1 (Full internationalization):

  • "I'll just run swiftgen real quick"
  • "The translations can wait"
  • "I'll add the keys manually"
  • "This phase doesn't apply to my case"
  • "I can skip phase 4 and run it later"
  • "No need to verify with scripts, trust me"

For Scenario 2 (Check missing):

  • "I can just eyeball the files"
  • "Checking one target language is enough"
  • "I'll add keys later"
  • "The other languages look fine"

For Scenario 3 (Cleanup):

  • "Just delete everything without checking"
  • "No need for --dry-run first"

All of these mean: Read the skill again and follow the correct workflow.

Configuration

Project-specific variables:

project_root: "/path/to/your/iOS/project"
localization_dir: "*/Localization"
baseline_language: "zh-Hans"  # Source of truth (can be any language)
target_languages: ["zh-Hant", "en", "ja", "ko", "de"]  # ALL target languages
generated_file: "Generated/Strings.swift"

Default structure:

<ProjectRoot>/path/<localization_dir>/
├── zh-Hans.lproj/Localizable.strings  # Baseline (source of truth)
├── zh-Hant.lproj/Localizable.strings  # Target 1
├── en.lproj/Localizable.strings       # Target 2
└── ja.lproj/Localizable.strings       # Target 3 (add more as needed)

Note: The baseline language can be any language (zh-Hans, en, ja, etc.). Choose the language you work with most frequently as baseline.

Scenario 1: Full Internationalization Workflow

Use when: User provides code/folder for internationalization

Flow: Sequential phases with iterative target language processing

Phase 1: Baseline → Phase 2+: Target Languages (repeat for ALL) → Verification → SwiftGen

Critical:

  • Complete ALL target language translations sequentially before verification
  • Verification step must confirm ALL languages aligned before SwiftGen
  • If missing keys found, return to incomplete target language phase

Phase 1: Baseline Localization

Update baseline language file and replace hardcoded strings with L10n references.

  1. Extract hardcoded strings from Swift/ObjC files
  2. Add new/modified strings to baseline file following naming conventions
  3. Replace strings with L10n.xxx calls

Completion: Baseline updated and code references replaced.

Phase 2+: Target Language Translations (Repeat for ALL Target Languages)

For EACH target language in your configuration:

  1. Identify new keys from Phase 1 - Review the keys added to baseline file
  2. Translate each new key from baseline language to this target language
  3. Add translations to this target language's .strings file following the same structure

Process:

  • Complete ONE target language fully before moving to the next
  • Copy new/modified keys from Phase 1 baseline
  • Translate values (not keys) to target language
  • Maintain same key names and structure as baseline
  • Preserve placeholders like %@, %d, etc.

Verification Step (CRITICAL)

After completing ALL target language translations, verify alignment:

This step confirms ALL translations are complete and aligned with baseline.

# Check each target language against baseline
for target_lang in "${target_languages[@]}"; do
  python3 <skill_path>/scripts/check_missing_localizations.py \
    <localization_dir>/<baseline_lang>.lproj/Localizable.strings \
    <localization_dir>/${target_lang}.lproj/Localizable.strings
done

Example: python3 scripts/check_missing_localizations.py Localization/zh-Hans.lproj/Localizable.strings Localization/en.lproj/Localizable.strings

Expected output for EACH language: ✅ [target] 文件没有缺失的键。

If missing keys found: Return to the incomplete target language phase and add missing translations.

Only proceed to Final Phase after verification confirms ALL languages aligned.

Final Phase: Generate Swift Code

Critical: Run after ALL changes and verification.

cd <project_root>
swiftgen

Completion: Code generated with all L10n references available for ALL languages.

Scenario 2: Check Missing Translations

Use when: User explicitly asks "Check if translations are complete" or "Find missing i18n keys"

Workflow

  1. Identify baseline language (usually zh-Hans or en)
  2. Check EACH target language against baseline:
python3 <skill_path>/scripts/check_missing_localizations.py \
  <localization_dir>/<baseline_lang>.lproj/Localizable.strings \
  <localization_dir>/<target_lang>.lproj/Localizable.strings

Repeat for ALL target languages.

  1. Review reports - Each check shows missing keys for that target language
  2. Add missing translations to each target language file
  3. Re-run verification for all modified languages to confirm all aligned
  4. Run SwiftGen if any translations were added

Completion: ALL target languages have complete translations matching baseline.

Scenario 3: Clean Unused Localizations

Use when: User explicitly asks "Clean unused localizations" or "Remove unused i18n keys"

Workflow

  1. Dry-run first (see what would be deleted):
cd <project_root>
python3 <skill_path>/scripts/clean_unused_localizations.py --dry-run
  1. Review the report - Shows unused entries and usage statistics
  2. Run actual cleanup (after confirmation):
python3 <skill_path>/scripts/clean_unused_localizations.py
  1. Run SwiftGen to regenerate code:
swiftgen

Safety:

  • Script creates .backup file before deletion
  • Prompts for confirmation before deleting
  • Requires explicit user confirmation

Completion: Unused entries removed, SwiftGen regenerated.

Quick Reference

Scenario 1: Full Internationalization

TaskCommand/ApproachPhase
Extract hardcoded stringsManual review of provided code1 (Baseline)
Add keys to baselineEdit baseline .strings file1
Replace with L10nL10n.Module.key syntax1
Translate to ALL targetsCopy keys from baseline, translate values sequentially2+
Verify alignmentpython3 scripts/check_missing_localizations.py <baseline> <target> for EACH targetAfter ALL translations
Generate Swift codeswiftgenFinal

Scenario 2: Check Missing Only

TaskCommand
Check each targetpython3 scripts/check_missing_localizations.py <baseline> <target> for ALL targets
Add missing keysEdit target .strings files
Verify fixedRe-run check script for all modified targets
Generate (if changed)swiftgen

Scenario 3: Cleanup Only

TaskCommand
Preview cleanuppython3 scripts/clean_unused_localizations.py --dry-run
Execute cleanuppython3 scripts/clean_unused_localizations.py
Regenerateswiftgen

Common Mistakes

MistakeFixScenario
Running swiftgen before syncing ALL translationsAlways complete ALL target language translations before SwiftGen1
Skipping verification stepAlways run check script after ALL target translations to confirm ALL languages aligned1
Only verifying some target languagesCheck EVERY target language against baseline before SwiftGen1, 2
Using string interpolation on format functionsCall as function: L10n.Xxx.func(arg1, arg2)1
Translating placeholders like %@Preserve placeholders exactly: %@, %d, etc.1, 2, 3, 4
Naming keys starting with numbersKeys must start with letters: secure3d not 3dsecure1, 4
Skipping final phase for "later"SwiftGen MUST run after all changes or code won't compile1, 2, 3
Cleanup without --dry-run firstAlways preview what will be deleted before actual cleanup3
Forgetting SwiftGen after cleanupMust regenerate code after removing unused keys3

Key Rules

0. Multi-Language Workflow (CRITICAL)

For projects with multiple target languages:

  • Sequential completion: Finish ONE target language completely before starting the next
  • Baseline as source: Always copy keys from baseline, never between target languages
  • Verify ALL languages: Every target language must be checked against baseline before SwiftGen

Workflow: Baseline → Target1 → Target2 → ... → TargetN → Verify ALL → SwiftGen (once)

1. Localization Key Naming

Core requirements:

  • Domain-like naming with hierarchical structure
  • All lowercase letters, numbers (after first character), and dots (.)
  • CRITICAL: Each segment MUST start with a letter (a-z), NOT a number
  • Use descriptive, domain-based names for clarity

Examples:

✅ common.ok
✅ market.header.name
✅ trade.confirm.title
✅ futuresrecords.header.amount_usdt

❌ symboldetail.24h.high (number in middle) → Use symboldetail.daily.high
❌ 2fa.enabled (starts with number) → Use twofa.enabled

2. L10n Code Reference Transformation

SwiftGen auto-transforms keys to code references:

  • All levels except last: Title Case (capitalize first letter only)
  • Last level:
    • Contains underscores (snake_case) → convert to camelCase
    • Single lowercase word → keep as-is
    • Compound words without underscores → stay lowercase

Examples:

Localization KeyL10n ReferenceNotes
common.okL10n.Common.okSingle level
market.header.nameL10n.Market.Header.nameAll Title Case
futuresrecords.header.amount_usdtL10n.Futuresrecords.Header.amountUsdtLast: snake_case → camelCase
paybridge.error.missingproductidL10n.Paybridge.Error.missingproductidCompound stays lowercase
challenge.status.inprogress.fullL10n.Challenge.Status.Inprogress.fullinprogress → Inprogress

3. Format Strings (with Parameters)

When localization strings contain format specifiers (%@, %d, %f, etc.), SwiftGen generates a FUNCTION, not a static property.

Format specifier examples:

  • %@ - Any object (String, etc.)
  • %d / %i - Integer
  • %f - Float/Double
  • %.2f - Formatted float
  • %% - Literal percent (does NOT create parameter)

Usage examples:

// Static property (no format specifiers)
button.setTitle(L10n.Common.ok, for: .normal)

// Function call (with format specifiers)
label.text = L10n.Challenge.Progress.stage(phaseText, statusText)
label.text = L10n.Futurestrading.Orderbook.fundingrate(8)

For detailed examples, troubleshooting, and migration guide: See references/naming-conventions.md

Helper Scripts

Located in scripts/ directory. For detailed usage, see scripts/README.md.

ScriptPurposeCommand
check_missing_localizations.pyCompare files for missing keyspython3 scripts/check_missing_localizations.py <baseline> <target>
clean_unused_localizations.pyFind/remove unused entriespython3 scripts/clean_unused_localizations.py --dry-run

Important Constraints

For ALL phases:

  • Only add/modify missing or outdated keys identified by scripts or requirements
  • Do NOT edit existing, correct keys and values
  • Preserve file structure, MARK comments, and formatting
  • Maintain alphabetical or logical grouping
  • Only modify the specific language file for that phase

Best Practices

For All Scenarios

  • Always verify with scripts before assuming completeness
  • Run SwiftGen after any localization file changes
  • Test translations in actual app context

For Scenario 1 (Full Workflow)

  • Start with baseline phase first
  • Complete ALL target language translations before running verification
  • Verify EVERY target language against baseline before SwiftGen
  • Complete each phase fully before next

For Scenario 2 (Check Missing)

  • Check ALL target languages, not just one
  • Re-run verification after adding missing keys
  • Run SwiftGen if any keys were added

For Scenario 3 (Cleanup)

  • Always use --dry-run first
  • Review the cleanup report before confirming
  • Clean periodically (e.g., before releases)
  • Keep backup files until verified in app

Requirements

  • Python 3.x
  • SwiftGen (brew install swiftgen)
  • iOS project using SwiftGen for localization
  • Access to *.lproj/Localizable.strings files

Advanced Usage

For CI/CD integration, automated translation, and performance optimization, see references/advanced-usage.md.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.