skills/tuist-app-localization/SKILL.md
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.
npx skillsauth add tddworks/claude-skills app-localizationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Manage iOS/macOS .strings files in Tuist-based projects.
<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.
Find hardcoded strings in Swift files:
# 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/
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> %@";
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.
Replace hardcoded strings with generated accessors.
| Hardcoded Pattern | Localized 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 |
Before:
Text("Settings")
.font(.headline)
TextField("Enter your name", text: $name)
Button("Submit") { ... }
Text("Hello, \(userName)!")
After:
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))
String with parameter (key: "search.noResults" = "No results for \"%@\""):
// Before
Text("No results for \"\(searchText)\"")
// After
Text(<Module>Strings.Search.noResults(searchText))
Conditional plurals:
// 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"):
Text(<Module>Strings.Message.detail(userName, fileCount))
python scripts/validate_strings.py /path/to/<ModuleName>
When translating strings to non-English locales:
search.noResults = search UI)Translation context by UI element:
python scripts/validate_strings.py /path/to/<ModuleName>
Checks for:
Report missing keys:
python scripts/sync_translations.py /path/to/<ModuleName> --report
Add missing keys as placeholders:
python scripts/sync_translations.py /path/to/<ModuleName> --sync
Pattern: "domain.context.element" → <Module>Strings.Domain.Context.element
Keys should reflect what the user is doing, not technical UI components:
| User Mental Model | Key Pattern | Generated 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 |
| Bad (Technical) | Good (Domain-Focused) |
|-----------------|----------------------|
| button.save | profile.save |
| field.email | registration.email |
| placeholder.search | appSelector.searchPlaceholder |
| error.network | sync.connectionFailed |
| label.title | settings.title |
| alert.confirm | build.expireConfirm |
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
/* 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:
\"tools
Replace with description of the skill and when Claude should use it.
development
Guide for building SwiftUI components using Brad Frost's Atomic Design methodology — organizing views into Atoms, Molecules, Organisms, Templates, and Pages with Design Tokens for theming. Use this skill whenever building new SwiftUI UI components, refactoring existing views into reusable pieces, creating a design system, or organizing a component library. Also trigger when the user mentions "atomic design", "design system", "component hierarchy", "reusable components", "atoms and molecules", "design tokens", or wants to decompose a complex SwiftUI view into smaller, composable parts, or needs to implement theming/customization across a SwiftUI app.
development
Create interactive iOS/mobile app UX flow prototypes as HTML documents with realistic phone mockups. Use when: (1) Visualizing user journeys and navigation flows, (2) Creating mobile app wireframes, (3) Documenting screen-to-screen navigation patterns, (4) Presenting iOS UI designs with annotations, (5) Prototyping app architecture before implementation. Generates self-contained HTML files with iOS-native styling, phone frames, flow arrows, and callout annotations.
devops
Scaffold iOS apps with Tuist and layered architecture (Domain, Infrastructure, App). Use when: (1) Creating a new iOS app project, (2) Setting up Tuist project structure, (3) User asks to "create an iOS app", "scaffold an app", or "set up a new Swift project", (4) User wants layered/clean architecture for iOS, (5) User mentions Tuist setup.