Runtime SDKs

Pseudo-localization

@transglot/pseudo: generate an accented, lengthened pseudo-locale from your source JSON to find hardcoded strings and layout overflow before you translate.

@transglot/pseudo turns source strings into a pseudo-locale: every letter is swapped for an accented look-alike, the text is lengthened, and the value is wrapped in brackets. Run your app in that locale and two kinds of bug stand out:

  • Text that is not accented is hardcoded and never went through your i18n layer.
  • A label that is clipped, loses its closing ]], or wraps badly will break when a longer language arrives.

Placeholders and markup are never changed, so the pseudo-locale still renders. The package is a library and a command-line tool, runs offline, and does not talk to transglot.

Install

npm install --save-dev @transglot/pseudo

It needs Node.js 20 or later and has no dependencies.

Command line

npx transglot-pseudo locales/en.json --out locales/en-XA.json

Given this source file:

locales/en.json
{
  "home": {
    "title": "Welcome back",
    "greeting": "Hello, {name}!"
  },
  "cart.items": "{count, plural, one {# item} other {# items}}",
  "save": "Save <b>now</b>"
}

it writes:

locales/en-XA.json
{
  "home": {
    "title": "[[Ŵéłçöɱáéíöüé ƀáçķ]]",
    "greeting": "[[Ĥéłáéíłö, {name}!]]"
  },
  "cart.items": "[[{count, plural, one {# item} other {# items}}]]",
  "save": "[[Šááéíṽé <b>ñöŵ</b>]]"
}
OptionDefaultEffect
<sourceFile>requiredA JSON file, flat or nested.
--out <file>stdoutWrite the result to a file instead of printing it, so the command also works in a pipeline.
--format json_nested or --format json_flatjson_nestedjson_nested transforms every string at any depth. json_flat transforms only the top-level values (strings and arrays of strings) and leaves nested objects untouched.
--expansion <n>0.4How much longer to make the text, as a fraction: 0.4 adds about 40%. 0 turns lengthening off.
--no-bracketsDo not wrap values in [[ and ]].
-h, --helpPrint usage.

The command exits 0 on success, 1 when the source file cannot be read or is not valid JSON, and 2 on an unknown option, an invalid value, or a missing source file.

Give the pseudo-locale a code no real user has. en-XA is the usual choice. Register it in your i18n setup like any other locale, switch to it, and click through your screens. Regenerate it when your source strings change; a CI step or pre-commit hook keeps it current.

What is never transformed

Only the readable text between placeholders and markup changes. These stay exactly as written:

KindExamples
HTML and XML tags<b>, </b>, <br/>, <a title="a>b">
Double-brace placeholders{{name}}
ICU plural, select and selectordinal blocks, kept whole including the text inside{count, plural, one {# item} other {# items}}
Single-brace placeholders{name}, {0}, {count, number}
printf-style placeholders%s, %d, %1$s, %.2f, %(name)s, %%
Colon parameters:name

Lengthening adds accented vowels inside the longest run of text, so a value that is only a placeholder is not padded.

Library

import { pseudoLocalize, pseudoLocalizeJson } from '@transglot/pseudo';

pseudoLocalize('Save changes');
// "[[Šáṽé çáéíöüĥáñğéš]]"

pseudoLocalize('Hi %s, you have :count items', { bracketOpen: '«', bracketClose: '»' });
// "«Ĥí %s, ýöüáéíöüáéí ĥáṽé :count íŧéɱš»"

const pseudo = pseudoLocalizeJson(JSON.parse(source), { nested: true, expansion: 0.3 });

pseudoLocalize(value, options?)

pseudoLocalize(value: string, options?: PseudoOptions): string transforms one string.

Prop

Type

pseudoLocalizeJson(data, options?)

pseudoLocalizeJson(data: unknown, options?: PseudoJsonOptions): unknown transforms a parsed JSON tree. Numbers, booleans and null pass through unchanged. It takes the same options as pseudoLocalize, plus:

Prop

Type

tokenize(input)

tokenize(input: string) splits a string into the ordered runs of transformable text and protected placeholders that pseudoLocalize works from. Each token is { type: 'text' | 'protected', value: string }.

On this page