Translate

Translate Xcode String Catalogs (.xcstrings)

A String Catalog holds every language in one JSON file that only Xcode should be editing by hand. KAERIS adds the new languages to that same file and leaves everything else in it as it was.

How a String Catalog is built

One file, strings, then the key, then localizations, then the language. Each language holds a stringUnit or variations for plurals. The file also carries comment, extractionState and shouldTranslate. A key with no entry for the source language is its own source text.

Source fileLocalizable.xcstrings
{
  "sourceLanguage" : "en",
  "strings" : {
    "Welcome back, %@!" : {
      "comment" : "Greeting on the home screen",
      "extractionState" : "manual",
      "localizations" : {
        "en" : { "stringUnit" : { "state" : "translated", "value" : "Welcome back, %@!" } }
      }
    },
    "%lld items" : {
      "localizations" : {
        "en" : {
          "variations" : {
            "plural" : {
              "one" : { "stringUnit" : { "state" : "translated", "value" : "%lld item" } },
              "other" : { "stringUnit" : { "state" : "translated", "value" : "%lld items" } }
            }
          }
        }
      }
    },
    "Cancel" : { }
  },
  "version" : "1.0"
}

What to watch for in .xcstrings translations

A real run: translated to German and checked

A real run, 27 October 2026, nothing edited by hand: kaeris translate Localizable.xcstrings --langs de with kaeris 0.2.35, 2.4 seconds, model openai/gpt-4.1-mini (the service's fallback model answered this job). Below, the German file it wrote and what kaeris check says about it.

German resultLocalizable.xcstrings
{
  "sourceLanguage": "en",
  "strings": {
    "Welcome back, %@!": {
      "comment": "Greeting on the home screen",
      "extractionState": "manual",
      "localizations": {
        "en": {
          "stringUnit": {
            "state": "translated",
            "value": "Welcome back, %@!"
          }
        },
        "de": {
          "stringUnit": {
            "state": "translated",
            "value": "Willkommen zurück, %@!"
          }
        }
      }
    },
    "%lld items": {
      "localizations": {
        "en": {
          "variations": {
            "plural": {
              "one": {
                "stringUnit": {
                  "state": "translated",
                  "value": "%lld item"
                }
              },
              "other": {
                "stringUnit": {
                  "state": "translated",
                  "value": "%lld items"
                }
              }
            }
          }
        },
        "de": {
          "variations": {
            "plural": {
              "one": {
                "stringUnit": {
                  "state": "translated",
                  "value": "%lld Artikel"
                }
              },
              "other": {
                "stringUnit": {
                  "state": "translated",
                  "value": "%lld Artikel"
                }
              }
            }
          }
        }
      }
    },
    "Cancel": {
      "localizations": {
        "de": {
          "stringUnit": {
            "state": "translated",
            "value": "Abbrechen"
          }
        }
      }
    }
  },
  "version": "1.0"
}
kaeris checkexit code 0
$ kaeris check --source Localizable.xcstrings --langs de
✓ de: complete & placeholder-safe
✓ all locales complete & placeholder-safe

What KAERIS does with it

Run it in three steps

  1. Install the CLI

    Run pip install kaeris.

  2. Translate the catalog

    Run kaeris translate Localizable.xcstrings --langs de,fr,ja. The catalog is updated in place.

  3. Check the result

    Upload the catalog on the free check page; it reads source and translations from the one file.

pip install kaeris
kaeris translate Localizable.xcstrings --langs de,fr,ja --verify

Questions

Does it overwrite my existing translations?

Languages already in the file, comments and extraction states are carried through untouched, as the CLI README says.

How are plurals handled?

Plural variations are expanded to the categories each language needs: English one/other becomes one/few/many/other in Russian and six forms in Arabic.

Is it free?

Yes, without an account: 10,000 characters per file and up to 8 languages per run. A free API key raises the file limit to 50,000 characters. Lifetime is $79 once, on your own OpenRouter key; monthly plans start at $19. Pricing

Can I run it from the terminal or in CI?

Yes. pip install kaeris, then kaeris translate Localizable.xcstrings --langs de,ja. See also iOS. The GitHub Action runs the same translation in a pull request. CLI docs · GitHub Action

Keep going

CLI reference · Check reference · Translate in the browser · Free check · Convert formats · Pseudo-localization

Other formats: JSON · YAML · .po · ARB · XLIFF · .strings · .properties · .resx · CSV · Markdown · Fluent · Android XML