View on GitHub

Stringproof

Catch damaged translations before they ship.

What’s it for?

Translation output can parse cleanly and still be broken. Placeholders disappear, scripts mix, prompt instructions leak into values, and encoding damage survives review.

Stringproof compares every Apple .strings target with its source and reports the damage. It is deterministic, local, and read-only: it validates translations but never translates or repairs them.

Sourceen.lproj/Localizable.strings
/* A compact source catalog for the Stringproof demonstration. */
"welcome.user" = "Welcome, %@.";
"items.count" = "{count} items";
"summary.title" = "Résumé";
"onboarding.prompt" = "Welcome";
"continue.action" = "Continue";
"reading.action" = "Read now";
"settings.open" = "Open Settings";
Corrupted targetja.lproj/Localizable.strings
/* Each defect is intentional and exercised by the real CLI. */
"welcome.user" = "ようこそ。";
"items.count" = "{total}件";
"summary.title" = "Résumé";
"onboarding.prompt" = "Please translate the following English text into Japanese:";
"continue.action" = "ようこそ 更多";
"reading.action" = "Read now";
"target.only" = "対象のみ";
Stringproof findingsexit 1
ja.lproj/Localizable.strings:1 [key.missing] ja settings.open: key is present in source catalog but missing from target
ja.lproj/Localizable.strings:2 [placeholder.missing] ja welcome.user: missing placeholder(s): %@
ja.lproj/Localizable.strings:3 [placeholder.added] ja items.count: added placeholder(s): {total}
ja.lproj/Localizable.strings:3 [placeholder.missing] ja items.count: missing placeholder(s): {count}
ja.lproj/Localizable.strings:4 [encoding.mojibake] ja summary.title: contains strong mojibake indicator(s): é
ja.lproj/Localizable.strings:5 [model.residue] ja onboarding.prompt: contains translation prompt or model-control residue
ja.lproj/Localizable.strings:6 [script.unexpected] ja continue.action: known cross-script fragment(s): 更多
ja.lproj/Localizable.strings:7 [translation.unchanged] ja reading.action: matches lexical source text and may be untranslated
ja.lproj/Localizable.strings:8 [key.unexpected] ja target.only: key is absent from source catalog
One damaged catalog produces nine findings.

Use it

stringproof check ios --source-locale en

or return stable JSON

stringproof check ios --source-locale en --json

Stringproof finds Apple .strings catalogs, pairs each target with its source by relative path, and checks their keys and values.

1,000 keys / 2 locales: ~60 ms. 10,000 keys: ~230 ms / 2 locales; ~1.6 s / 11 locales.

  • 0 is clean.Successful human output is silent.
  • 1 means findings.Syntax, keys, placeholders, encoding, prompt residue, script contamination, and suspicious unchanged text are covered.
  • 2 means the check could not run.Invalid invocation, unreadable input, and internal failures go to stderr.

Limits. Stringproof can show that catalogs line up and that known corruption patterns are absent. It cannot show that a translation is correct, natural, or appropriate. It may flag a legitimate unchanged name, miss source text after a small alteration, or miss an entirely absent target catalog.

The first version supports Apple .strings only. It uses no credentials, network access, model calls, configuration, or repair mode. View Stringproof on GitHub ↗