String Catalog Translator

SkillFiles & storage

Translate strings in Xcode String Catalogs (.xcstrings files). Prefer to use the `xcode-skills:translation-coordinator` skill for task-coordination. Use this skill when translating individual strings or working with String Catalogs. Should only be activated when translating a single string or a small batch of known string keys. The `translation-coordinator` skill should be used for anything else.

Use String Catalog Translator in Claude, ChatGPT or Ahel Desktop

Free. Sign in, add String Catalog Translator and connect your AI. About a minute.

Also: Claude Code · Cursor · Codex

Then ask your AI: use the String Catalog Translator skill

Details

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

String Catalog TranslatorStart free

What this skill tells your AI

The instructions your AI receives, as published by artemnovichkov/xcode-skills in skills/translation/SKILL.md and read by Ahel’s review.

Translate a given set of strings in Xcode String Catalogs using specialized MCP tools. These strings are user-facing software strings for apps on Apple platforms — typically short UI text such as button titles, labels, and messages. Translate them as you would for a native app on those platforms. Access String Catalogs only through these tools—never write .xcstrings files directly.

Abort if no list of keys was provided, or if no target locale identifier was provided — something went wrong. Do not guess a locale from examples; the target locale must come from your initial instructions.

Role Boundaries

A specific list of string keys and a target locale identifier have been provided via your initial instructions.

  • Do not fetch additional string keys beyond what you were given
  • Do not translate into any locale other than the one explicitly provided
  • Do not use LocalizationPlanner (your coordinator already ran it)
  • Do not spawn sub-agents of your own

Quick Reference

ToolPurpose
StringCatalogReadGet string keys by translation state (new, needs_review, translated, machine_translated)
StringCatalogContextGet source value and context: comments, similar strings, code locations, plural cases
StringCatalogEditInsert the translation

Workflow

Skip the LocalizationPlanner tool when told to do so.

For each string, one at a time, follow these steps in order.

Step 1: Get source value and context Call StringCatalogContext with the target locale. The sourceValues field in the response contains the text that must be translated. The rest of the response provides context:

  • Developer comments explaining intent
  • Existing translations in other languages
  • Similar strings with their translations (for terminology consistency)
  • Code locations where the string is used
  • UI appearance hints (button vs. label affects verb/noun choice)
  • Required plural cases for the target locale

Step 2: Read the source code at the provided file paths to understand how the string is used. This reveals the developer's intention and helps you choose the right translation (e.g., a verb for buttons, descriptive for labels). For instance, the key "Save" could be a verb (button action → "Speichern") or a noun (a save file → "Spielstand") — only the source code reveals which. Reading the source code is REQUIRED for finding a good translation. If usage data is unavailable, use all the context clues you have so far — developer comments, similar strings, appearance hints, and existing translations in other languages.

Some UI words are both noun and verb (e.g. "Bookmark", "Archive", "Save"), and the noun is the more common reading, so might be the one you fall back to by default. When the comment, code, or appearance information shows the string is a button or other action control, you MUST translate it as a verb, not a noun (or the appropriate part-of-speech according to the target language's style guide). For instance, a "Bookmark" button is the action "add a bookmark", not the object "a bookmark", hence it should be translated as a verb, and reading the source code and the appearance info gives you clarity over its usage. Give both labels of a toggle (e.g. the two sides of a ternary) the same part of speech — never one as a verb and the other as a noun. Follow the target-languages style-guide to determine what part-of-speech buttons, toggles, and labels should use.

Step 3: Gather available style and terminology input, then make style choices

Read and consider guidance from the following:

  • Explicit guidance in your instructions
  • Existing translations for the target locale
  • The locale-specific style guide

They cover different concerns, and the higher-priority sources are often incomplete — the lower-priority ones fill the gaps rather than being ignored:

  1. Explicit guidance in your instructions. Any terminology or style direction in the instructions you were given (how to translate a specific term, the app name, tone guidance, DNT list, etc.) is authoritative — follow it above all else.
  2. Existing translations for the target locale. Match their terminology, phrasing, register, tone, etc. so the app's translations stay consistent. These reflect choices already made for this project and take precedence over the style guide.
  3. The locale-specific style guide. Always read references/styleguide_{locale}.md (resolve it relative to the skill's base directory) when one exists for the target locale (e.g. styleguide_pt-BR.md, styleguide_zh-Hans.md—if the file doesn't exist, there isn't a style guide for that locale). Use it to inform your choices when specific guidance doesn't exist in your instructions or existing translations.

When these sources conflict, higher-priority items win: explicit instructions override existing translations, which override the style guide. Where none of them settles a question, default to informal/colloquial style.

Step 4: Formulate translation Consider:

  • Terminology: Match terms used in similar strings. If "Save" is translated as "Speichern" elsewhere, use it consistently. No matter the similar strings, make sure the part of speech of your target string is preserved: a noun sibling ("Bookmarks") is not a precedent for an action button that shares its stem ("Bookmark") — reuse the term, keep the part of speech the usage calls for.
  • Tone and formality: Decide on the style of your translation based on your choices in step 3
  • App names: Once you decide on how to translate an app name, make sure to to stick to this decision everywhere the app name is referenced.
  • Format specifiers: Understand what each specifier represents by reading the source code (e.g., %lld might be a count of items, files, or users).

Step 5: Determine if variation is needed Check whether the translation needs plural variation, device variation, or both.

  • Plural: If the string contains a numeric format specifier (%lld, %d, %u, etc.) paired with a countable noun, read references/plural-variations.md (resolve it relative to the skill's base directory). The context tool provides relevantPluralCases for your target locale—use all of them.
    • If the context tool also returned sourcePluralCasesToAdd, the source itself isn't plural-varied yet. Vary the source first in a separate StringCatalogEdit call before translating the target — references/plural-variations.md walks through this two-step flow.
  • Device: If the string references a device-specific interaction (tap vs. click) or mentions a device by name, read references/device-variations.md (resolve it relative to the skill's base directory)
  • Both: A string can need both — for example, "Tap to launch %lld spaceships" differs by device AND has a countable noun. Combine device and plural keys (e.g., device.iphone.plural.one), but keep device.other as a flat fallback string that covers both variations

Step 6: Insert translation Call StringCatalogEdit with the appropriate translation type. Translate the source value from sourceValues in Step 1 with the context you gathered. If the string is a String Set (marked isStringSet: true in context), provide natural alternatives in the target language using the stringSetTranslation parameter — these are not 1:1 translations but synonyms that express similar intent. For example, English ["order food in ${applicationName}", "get food in ${applicationName}"] → German ["Essen bestellen in ${applicationName}", "Essen holen auf ${applicationName}"]. Continue to the next string.

Repeat these 6 steps until all requested strings are translated.

Do not rush and cut corners; follow these 6 steps exactly for every string requested.

Tool Reference

StringCatalogContext

Returns context and the source language value for a given string. The sourceValues field contains the text that must be translated. Also includes comments, translations for other languages if present, and relevant plural case hints for the target locale if applicable. Curly apostrophes and quotes are escaped (e.g., \u2019 for curly apostrophe, \u201C for curly quote).

Inputs

ParameterTypeRequiredDescription
tabIdentifierStringYesWorkspace tab identifier
filePathStringYesPath to String Catalog
stringKeyStringYesString key to get context for
targetLocaleIdentifierStringYesLocale for translation (e.g., de, pt-PT)

Outputs

FieldTypeDescription
sourceValuesSourceValuesThe source language values to translate (see SourceValues type below)
shouldTranslateBoolWhether string should be translated (false = DO NOT TRANSLATE)
isStringSetBool?Whether this is a String Set (only present when true)
commentString?Developer comment from String Catalog
relevantPluralCases[String]?Plural cases for target locale (e.g., ["plural.one", "plural.other"]). Absent when the string doesn't require pluralization.
sourcePluralCasesToAdd[String]?Plural cases for the source locale. Present when the source string has a numerical format specifier but is not yet plural-varied. Absent when the source string doesn't require pluralization.
translations[LocalizationInfo]All existing translations across non-source locales
usageLocations[UsageLocation]?Source code locations where string is used
appearances[AppearanceInfo]?UI appearance hints (button, label, UI framework)
usageDataUnavailableString?Message when usage data can't be retrieved (e.g., "Build the project...")
similarStrings[SimilarStringInfo]Similar strings from other String Catalogs
supportedDevices[String]?Devices this app builds for (e.g., ["device.iphone", "device.mac"]). Only present when the app targets multiple device families.

Output Types

LocalizationInfo

The terminology choices for this string in other languages can be an indicator of what terminology to choose for this translation. The isVaried field is only present (and true) when the localization contains plural, device, or width variations; for plain translations it is omitted.

{
  "localeIdentifier": "de",
  "value": "Willkommen!"
}

When the localization is varied, value carries a human-readable description of the variation tree:

{
  "localeIdentifier": "he",
  "value": "plural.one: ...\nplural.other: ...",
  "isVaried": true
}
UsageLocation

Checking how the string is used in source code can provide important context on the terminology to choose (noun vs. verb, etc.)

{
  "fileURL": "file:///path/to/File.swift",
  "lineNumber": 42,
  "columnNumber": 15
}
AppearanceInfo

The way this string is presented in UI is a strong signal for part of speech to choose: translate a button or other action control as an action.

{
  "usageHint": "This string is used in a SwiftUI button"
}
SimilarStringInfo

Ensure consistent terminology, formality, and style by basing new translations off existing similar strings.

{
  "key": "save_button",
  "sourceDescription": "Save",
  "targetDescription": "Speichern"
}
SourceValues

The source language values that must be translated. Exactly one of value, setValues, or variationDescription will be non-null.

FieldTypeDescription
sourceLocaleIdentifierStringThe source locale identifier
valueString?Source text for simple strings
setValues[String]?Source values for string sets
variationDescriptionString?Variation tree for varied strings

StringCatalogEdit

Inserts or updates a translation in a String Catalog. Can handle simple strings, varied strings, and String Sets. If the string needs variation (e.g., plural forms), provide the templateTranslation or variationTranslation parameter. For String Sets (voice assistant commands), use stringSetTranslation. Prefer typographically correct quotes for the target language (e.g., „...“ for German, «...» for French). All curly quotes must be escaped (e.g., \u201E...\u201C for German „...“).

Critical: Translations must be in the correct target locale. Refer to your initial instructions to determine which locale applies. Do not infer a locale from examples in this document.

Inputs

ParameterTypeRequiredDescription
tabIdentifierStringYesWorkspace tab identifier
filePathStringYesPath to String Catalog
stringKeyStringYesString key to translate
targetLocaleIdentifierStringYesTarget locale (e.g., de, pt-PT)

Plus exactly one of the following (mutually exclusive):

ParameterTypeDescription
translationStringSimple string translation (no variations)
templateTranslationTemplateTranslationTemplate with substitutions for multiple plural nouns
variationTranslationVariationTranslationTop-level variations (device, width, or single plural noun)
stringSetTranslation[String]Array of values for String Sets

Translation Types

Simple Translation

For strings without variations:

{
  "stringKey": "welcome_message",
  "targetLocaleIdentifier": "de",
  "translation": "Willkommen in unserer App!"
}
Template Translation

For strings with multiple format specifiers + countable nouns:

{
  "stringKey": "usage_message",
  "targetLocaleIdentifier": "de",
  "templateTranslation": {
    "template": "iCloud+ wird von %#@arg1@ und %#@arg2@ verwendet.",
    "substitutions": [
      {
        "name": "arg1",
        "argNum": 1,
        "formatSpecifier": "lu",
        "variants": {
          "plural.one": "%arg Gerät",
          "plural.other": "%arg Geräte"
        }
      },
      {
        "name": "arg2",
        "argNum": 2,
        "formatSpecifier": "lu",
        "variants": {
          "plural.one": "%arg Mitglied",
          "plural.other": "%arg Mitglieder"
        }
      }
    ]
  }
}
Variation Translation

For strings with top-level plural, device, or width variations, or a single format specifier + countable noun:

Single plural noun:

{
  "stringKey": "item_count",
  "targetLocaleIdentifier": "pl",
  "variationTranslation": {
    "topLevelVariation": {
      "plural.one": "Masz %lld przedmiot",
      "plural.few": "Masz %lld przedmioty",
      "plural.many": "Masz %lld przedmiotów",
      "plural.other": "Masz %lld przedmiotu"
    }
  }
}

Device-only variations (no plurals):

{
  "stringKey": "action_hint",
  "targetLocaleIdentifier": "es",
  "variationTranslation": {
    "topLevelVariation": {
      "device.iphone": "Toca aquí",
      "device.mac": "Haz clic aquí",
      "device.other": "Pulsa aquí"
    }
  }
}

Device variations with single plural noun:

{
  "stringKey": "launch_button",
  "targetLocaleIdentifier": "fr",
  "variationTranslation": {
    "topLevelVariation": {
      "device.iphone.plural.one": "Touchez pour lancer %lld vaisseau spatial",
      "device.iphone.plural.other": "Touchez pour lancer %lld vaisseaux spatiaux",
      "device.mac.plural.one": "Cliquez pour lancer %lld vaisseau spatial",
      "device.mac.plural.other": "Cliquez pour lancer %lld vaisseaux spatiaux",
      "device.other": "Touchez pour lancer %lld vaisseaux spatiaux"
    }
  }
}

Device variations with substitutions (multiple plural nouns):

{
  "stringKey": "device_usage",
  "targetLocaleIdentifier": "de",
  "variationTranslation": {
    "topLevelVariation": {
      "device.iphone": "iCloud+ wird von %#@arg1_iphone@ und %#@users@ verwendet",
      "device.mac": "iCloud+ wird von %#@arg1_mac@ und %#@users@ verwendet",
      "device.other": "iCloud+ wird von %lld und %lld verwendet"
    },
    "substitutions": [
      {
        "name": "arg1_iphone",
        "argNum": 1,
        "formatSpecifier": "lld",
        "variants": {
          "plural.one": "%arg anderes iPhone",
          "plural.other": "%arg andere iPhones"
        }
      },
      {
        "name": "arg1_mac",
        "argNum": 1,
        "formatSpecifier": "lld",
        "variants": {
          "plural.one": "%arg anderer Mac",
          "plural.other": "%arg andere Macs"
        }
      },
      {
        "name": "users",
        "argNum": 2,
        "formatSpecifier": "lld",
        "variants": {
          "plural.one": "%arg Benutzer",
          "plural.other": "%arg Benutzer"
        }
      }
    ]
  }
}

Critical: See plural-variations.md for detailed rules.

Critical: Insert the entire variation structure, including already translated variants. This overwrites what was there before.

String Set Translation

For String Sets (voice assistant commands):

{
  "stringKey": "COMMAND_ORDER",
  "targetLocaleIdentifier": "de",
  "stringSetTranslation": ["Essen bestellen", "Essen holen", "Essen kaufen"]
}

Note: provide synonyms/alternatives, not direct 1:1 translations.

Type Definitions

TemplateTranslation:

FieldTypeRequiredDescription
templateStringYesTemplate with %#@name@ substitution references
substitutions[Substitution]YesArray of substitution definitions

VariationTranslation:

FieldTypeRequiredDescription
topLevelVariation{String: String}YesMaps variation paths to templates (e.g., "plural.one", "device.iphone")
substitutions[Substitution]?NoOptional substitutions referenced by templates

Substitution:

FieldTypeRequiredDescription
nameStringYesPlaceholder name (used as %#@name@ in template)
argNumIntYes1-indexed argument position
formatSpecifierStringYesFormat type without % (e.g., lld, @, u)
variants{String: String}YesMaps variation paths to values (use %arg as number placeholder)

Outputs

FieldTypeDescription
successBoolWhether translation was inserted
messageStringSuccess or error message

StringCatalogRead

This tool should only be used to verify your work.

Returns string keys grouped by translation state for the requested locale. Includes counts of all string keys grouped by translation state. Supports pagination. Curly apostrophes and quotes are escaped (e.g., \u2019 for curly apostrophe, \u201C for curly quote).

Inputs

ParameterTypeRequiredDefaultDescription
tabIdentifierStringYes—Workspace tab identifier
filePathStringYes—Path to String Catalog (relative or absolute)
targetLocaleIdentifierStringYes—Locale to check translations for (e.g., de, pt-PT)
requestedStateString?NonilState to retrieve: new, needs_review, translated, machine_translated. If omitted, only counts for all states are returned.
keyLimitIntNo50Maximum keys to return
offsetIntNo0Keys to skip (for pagination)

Outputs

Always returned:

FieldTypeDescription
newCountIntUntranslated strings
needsReviewCountIntStrings marked needs review
translatedCountIntHuman-translated strings
machineTranslatedCountIntMachine-translated strings

When requestedState is provided:

FieldTypeDescription
requestedStateStringThe requested state bucket
totalForRequestedStateIntTotal keys in state bucket before pagination
returnedCountIntKeys returned after pagination
keys[String]Array of string keys

A key can appear in multiple state buckets if variants have different states.


Critical Rules

  1. Use only String Catalog tools to access .xcstrings files. Never write to them directly.
  2. Translate one string at a time, following all 6 steps for each before moving to the next.
  3. Preserve format specifiers exactly as they appear in source (%1$lld, %@, etc.).
  4. Make explicit choices about translation style—a well-translated app has consistent style throughout. Always read the target locale's style guide when one exists and use it as the baseline; explicit instructions and existing translations take precedence over it wherever they apply.
  5. Keep app names consistent—when you translate them once, make sure to translate them everywhere.
  6. Complete the entire task—continue until all requested translations are done.
  7. Use typographically correct quotes and apostrophes for the target language (e.g., „...“ for German, «...» for French). All curly quotes must be escaped (e.g., \u201E...\u201C for German „...“), as well as apostrophes (e.g. \u2019 for curly apostrophe). NEVER XML-escape the ampersand: write a literal &, NOT &amp;. The same goes for all other HTML/XML entities — never write &lt;, &gt;, &quot;, or &apos;; write the literal <, >, ", ' characters instead. The String Catalog stores Unicode text, not XML, so any &amp; would ship verbatim into the app. Other non-ascii characters do not need extra escaping either. DO NOT blindly escape everything.
  8. Do NOT skip steps to save time, even when there are hundreds of strings. Each step exists to prevent translation errors that are harder to find and fix later. This process takes time, and that's ok. Don't skip work or cut corners to save time, rather focus on accuracy and completeness.
  9. Use the exact locale identifier from your instructions as the targetLocaleIdentifier in every tool call. Do NOT normalize, canonicalize, or expand it (e.g., if told zh-TW, use zh-TW — never zh-Hant-TW; if told pt-BR, use pt-BR — never pt-Latn-BR). The String Catalog uses these identifiers as-is, and mismatches will cause translations to be stored under the wrong locale.

Example

For each string key:

  1. Agent calls StringCatalogContext to get the source value, developer comments, similar strings, code locations, and plural cases.
  2. Agent reads the source code at the provided file paths to understand how the string is used (verb vs. noun, button vs. label).
  3. Agent reads the locale style guide (when one exists for the target locale), reviews existing translations for terminology and tone, and notes any explicit guidance in its instructions — then applies them with explicit instructions taking precedence over existing translations, and existing translations over the style guide.
  4. Agent formulates the translation, considering terminology consistency, tone, app names, and format specifiers.
  5. Agent determines whether variation is needed: plural variation (format specifiers + countable nouns), device variation (interaction verbs or device names + multiple supportedDevices), or both.
  6. Agent calls StringCatalogEdit to insert the translation for the requested target language.

Signals

GitHub stars
26
Forks
1
Last commit
Sep 2026

Ahel review

  • K5info
    obfuscation (in references/styleguide_bn.md)
  • K7info
    secret-appetite (in references/styleguide_ca.md)
  • K7info
    secret-appetite (in references/styleguide_en.md)
  • K7info
    secret-appetite (in references/styleguide_ja.md)
  • K7info
    secret-appetite (in references/styleguide_kk.md)
  • K5info
    obfuscation (in references/styleguide_kn.md)
  • K5info
    obfuscation (in references/styleguide_ml.md)
  • K5info
    obfuscation (in references/styleguide_or.md)

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Item type
skill
Key
translation
Source
github.com/artemnovichkov/xcode-skills