tddworks/claude-skills/tuist-app-localization

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

iOS/macOS app localization management for Tuist-based projects with .strings files. Use when: (1) Adding new translation keys to modules, (2) Validating .strings files for missing/duplicate keys, (3) Syncing translations across languages, (4) AI-powered translation from English to other locales, (5) Checking placeholder consistency (%@, %d), (6) Generating localization reports, (7) Updating Swift code to use localized strings instead of hardcoded text.

Install

npx vskill@latest install tddworks/claude-skills/tuist-app-localization

SKILL.md

App Localization

Manage iOS/macOS .strings files in Tuist-based projects.

Project Structure

<ModuleName>/
├── Resources/
│ ├── en.lproj/Localizable.strings # Primary language (English)
│ ├── <locale>.lproj/Localizable.strings # Additional locales
│ └── ...
├── Derived/
│ └── Sources/
│ └── TuistStrings+<ModuleName>.swift # Generated by Tuist
└── Sources/
└── **/*.swift # Uses <ModuleName>Strings.Section.key

After editing .strings files, run tuist generate to regenerate type-safe accessors.

Complete Localization Workflow

Step 1: Identify Hardcoded Strings

Find hardcoded strings in Swift files:

bash
# Find Text("...") patterns with hardcoded strings
grep -rn 'Text("[A-Z]' <ModuleName>/Sources/
grep -rn 'title: "[A-Z]' <ModuleName>/Sources/
grep -rn 'label: "[A-Z]' <ModuleName>/Sources/
grep -rn 'placeholder: "[A-Z]' <ModuleName>/Sources/
Step 2: Add Translation Keys

Add keys to all language files:

en.lproj/Localizable.strings (primary):

/* Section description */
"section.key.name" = "English value";
"section.key.withParam" = "Value with %@";

Other locales (translate appropriately):

"section.key.name" = "<translated value>";
"section.key.withParam" = "<translated> %@";
Step 3: Generate Type-Safe Accessors
bash
tuist generate

This creates Derived/Sources/TuistStrings+<ModuleName>.swift with accessors:

  • <ModuleName>Strings.Section.keyName (static property)
  • <ModuleName>Strings.Section.keyWithParam(value) (static function for %@ params)

See references/tuist-strings-patterns.md for detailed patterns.

Step 4: Update Swift Code

Replace hardcoded strings with generated accessors.

Pattern Mapping
Hardcoded PatternLocalized Pattern
Text("Title")Text(<Module>Strings.Section.title)
Text("Hello, \(name)")Text(<Module>Strings.Section.hello(name))
title: "Submit"title: <Module>Strings.Action.submit
placeholder: "Enter..."placeholder: <Module>Strings.Field.placeholder
Example Transformations

Before:

swift
Text("Settings")
.font(.headline)
TextField("Enter your name", text: $name)
Button("Submit") { ... }
Text("Hello, \(userName)!")

After:

swift
Text(<Module>Strings.Section.settings)
.font(.headline)
TextField(<Module>Strings.Field.namePlaceholder, text: $name)
Button(<Module>Strings.Action.submit) { ... }
Text(<Module>Strings.Greeting.hello(userName))
Handling Parameters and Plurals

String with parameter (key: "search.noResults" = "No results for \"%@\""):

swift
// Before
Text("No results for \"\(searchText)\"")
// After
Text(<Module>Strings.Search.noResults(searchText))

Conditional plurals:

swift
// Keys:
// "item.count" = "%d item"
// "item.countPlural" = "%d items"
// Swift:
let label = count == 1
? <Module>Strings.Item.count(count)
: <Module>Strings.Item.countPlural(count)

Multiple parameters (key: "message.detail" = "%@ uploaded %d files"):

swift
Text(<Module>Strings.Message.detail(userName, fileCount))
Step 5: Validate Changes
  1. Build the project to catch missing keys
  2. Run validation script to check consistency:
bash
python scripts/validate_strings.py /path/to/<ModuleName>

AI-Powered Translation

When translating strings to non-English locales:

  1. Read the English source string
  2. Consider context from the key name (e.g., search.noResults = search UI)
  3. Translate appropriately for the target locale:
  • zh-Hans: Simplified Chinese, formal but friendly
  • zh-Hant: Traditional Chinese
  • ja: Japanese, polite form (desu/masu style)
  • ko: Korean, polite form (hamnida/yo style)
  • de/fr/es/etc.: Appropriate regional conventions
  1. Preserve all placeholders exactly (%@, %d, %ld, etc.)

Translation context by UI element:

  • Labels: Keep concise
  • Buttons: Action-oriented verbs
  • Placeholders: Instructive tone
  • Error messages: Helpful and clear
  • Confirmations: Clear consequences

Validation Scripts

Validate .strings Files
bash
python scripts/validate_strings.py /path/to/<ModuleName>

Checks for:

  • Missing keys between languages
  • Duplicate keys
  • Placeholder mismatches (%@, %d, %ld)
  • Untranslated strings (value = English)
Sync Missing Translations

Report missing keys:

bash
python scripts/sync_translations.py /path/to/<ModuleName> --report

Add missing keys as placeholders:

bash
python scripts/sync_translations.py /path/to/<ModuleName> --sync

Key Naming Convention

Pattern: "domain.context.element" → <Module>Strings.Domain.Context.element

Domain-Focused Naming (User Mental Model)

Keys should reflect what the user is doing, not technical UI components:

User Mental ModelKey PatternGenerated Accessor
"I'm looking at my profile""profile.name"Strings.Profile.name
"I'm testing a build""betaBuild.whatToTest"Strings.BetaBuild.whatToTest
"I'm adding a tester""testerGroup.addTester"Strings.TesterGroup.addTester
"Something went wrong with sync""sync.error.failed"Strings.Sync.Error.failed
Good vs Bad Examples
Bad (Technical)Good (Domain-Focused)
button.saveprofile.save
field.emailregistration.email
placeholder.searchappSelector.searchPlaceholder
error.networksync.connectionFailed
label.titlesettings.title
alert.confirmbuild.expireConfirm
Structure by Feature/Screen

Organize keys by the feature or screen where they appear:

/* Profile Section */
"profile.title" = "Profile";
"profile.name" = "Name";
"profile.save" = "Save Changes";
"profile.saveSuccess" = "Profile updated";
/* Beta Builds */
"betaBuild.title" = "Beta Builds";
"betaBuild.whatToTest" = "What to Test";
"betaBuild.submitForReview" = "Submit for Review";
"betaBuild.expireConfirm" = "Expire this build?";
/* Tester Groups */
"testerGroup.create" = "Create Group";
"testerGroup.addTester" = "Add Tester";
"testerGroup.empty" = "No testers yet";

This mirrors how users think: "I'm in Beta Builds, submitting for review" → betaBuild.submitForReview

.strings File Format

/* Comment describing the section */
"key.name" = "Value";
"key.with.parameter" = "Hello, %@!";
"key.with.number" = "%d items";
"key.with.multiple" = "%1$@ has %2$d items";

Rules:

  • Keys must be unique within a file
  • Values are UTF-8 encoded
  • Escape quotes with backslash: \"
  • Line ends with semicolon
  • Use positional parameters (%1$@, %2$d) when order differs between languages

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
35
Repository forks
6
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/tddworks/claude-skills/tuist-app-localization/badge)](https://verified-skill.com/skills/tddworks/claude-skills/tuist-app-localization)

Something wrong with this skill or its page? Tell us.