npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@transglot/pseudo

v0.1.0

Published

Zero-dependency pseudo-localization library + CLI: turn a source i18n file into a pseudo-locale to catch hardcoded strings and layout/overflow bugs before translating. Preserves ICU/i18next/printf placeholders, colon params, HTML tags, and ICU plural bloc

Readme

@transglot/pseudo

Zero-dependency pseudo-localization library + CLI. It turns a source i18n file into a pseudo-locale, an obviously-transformed, length-expanded, bracket-wrapped version of your strings, so you can catch two classes of bug before you spend a cent on real translation:

  • Hardcoded strings. Anything that renders in plain, un-accented Latin text in the pseudo-locale was never sent through i18n. It jumps out instantly.
  • Layout / overflow breakage. German and Finnish routinely run 30–40% longer than English. The pseudo-locale expands every string and wraps it in [[ … ]] brackets, so clipped labels, truncated buttons, and broken flexbox are visible at a glance: the brackets show you exactly where a string starts and ends.

Example:

"Save changes"  →  "[[Šáṽé áéíçĥáñğéš]]"
"Hello {name}"  →  "[[Ĥéłłö {name}]]"

This is a first-class feature almost no localization platform ships. It costs nothing to run and pays for itself the first time it catches an un-i18n'd string in a screenshot review.

Why the accents and brackets

  1. Accented look-alikes (a→á, e→é, o→ö, A→Á, …) keep the text readable while making transformed vs. untransformed text unmistakable. If a label reads Sáṽé it went through i18n; if it reads Save it is hardcoded.
  2. Length expansion (default +40%) pads the readable text with repeated accented vowels (never random noise) to simulate longer languages so you see overflow now, not after launch.
  3. [[ … ]] bracket wrapping makes truncation obvious at the edges: if you only see [[Šáṽé áéíçĥ with no closing ]], the string is being clipped.

What is never transformed

Getting this right is the whole point. Only human-readable text is pseudo-ized; every interpolation token and markup construct is preserved byte-for-byte:

| Kind | Example | Preserved as | |------|---------|--------------| | ICU / i18next placeholder | Hello {name} | {name} | | ICU simple arg | {count, number} | {count, number} | | i18next double-brace | Hi {{firstName}} | {{firstName}} | | ICU plural / select / selectordinal | {count, plural, one {# item} other {# items}} | whole block | | printf (incl. positional) | %s, %d, %1$s, %.2f, %% | verbatim | | Colon params (Laravel/Symfony) | Welcome :name | :name | | HTML / XML tags | Click <b>here</b> | <b>, </b> |

Prose that merely looks like a token is left alone: 100% done keeps its %, 12:30 is not read as a :param, https://x.io is not either, and a bare a < b is not mistaken for a tag.

CLI

Installed as the transglot-pseudo bin.

transglot-pseudo <sourceFile> [--out <file>] [--format json_nested|json_flat]
                    [--expansion 0.4] [--no-brackets]

| Option | Default | Meaning | |--------|---------|---------| | --out <file> | stdout | Write the pseudo-locale to a file instead of printing it. | | --format | json_nested | json_nested recurses into nested objects; json_flat transforms only top-level values (keys may contain dots). | | --expansion <n> | 0.4 | Length-expansion ratio. 0 disables padding. | | --no-brackets | (on) | Do not wrap values in [[ … ]]. | | -h, --help | | Show usage. |

Non-string leaves (numbers, booleans, null) pass through unchanged.

Example

# nested source (e.g. en.json) → pseudo-locale file
transglot-pseudo locales/en.json --out locales/en-XA.json

# pipe to stdout, no expansion, to eyeball placeholder handling
transglot-pseudo locales/en.json --no-brackets --expansion 0

Given locales/en.json:

{
  "app": {
    "greeting": "Hello {name}, welcome back!",
    "messages": "You have {count, plural, one {# message} other {# messages}}"
  }
}

you get:

{
  "app": {
    "greeting": "[[Ĥéłłö {name}, ŵéłçöáéíöüáéíɱé ƀáçķ!]]",
    "messages": "[[Ýöü áéíöĥáṽé {count, plural, one {# message} other {# messages}}]]"
  }
}

Library

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

pseudoLocalize('Hello {name}');
// → "[[Ĥéłłö {name}]]"

pseudoLocalize('Save', { brackets: false, expansion: 0 });
// → "Šáṽé"

pseudoLocalizeJson(
  { title: 'Save', nav: { home: 'Home' } },
  { expansion: 0 },
);
// → { title: "[[Šáṽé]]", nav: { home: "[[Ĥöɱé]]" } }

API

  • pseudoLocalize(value: string, options?): pseudo-localize one string.
  • pseudoLocalizeJson(data: unknown, options?): walk a parsed JSON i18n tree (flat or nested) and transform every string leaf.
  • tokenize(value: string): the underlying splitter into text (transformed) and protected (verbatim) runs; exported for inspection/testing.

options:

{
  expansion?: number;     // default 0.4; 0 disables padding
  brackets?: boolean;     // default true
  bracketOpen?: string;   // default '[['
  bracketClose?: string;  // default ']]'
  nested?: boolean;       // pseudoLocalizeJson only; default true
}

Adding the pseudo-locale to your app

  1. Generate it alongside your source locale and give it a locale code that no real user has. The CLDR convention is en-XA (a private-use pseudo region):

    transglot-pseudo locales/en.json --out locales/en-XA.json
  2. Register en-XA as a locale in your i18n framework, exactly like any other:

    // i18next
    i18n.init({
      resources: {
        en:      { translation: en },
        'en-XA': { translation: enXA },
      },
    });
  3. Switch your app's language to en-XA (a query param, a dev-menu toggle, or ?lng=en-XA) and click through your screens. Now:

    • any text that is not accented is a hardcoded string, so go i18n it;
    • any label that is clipped, missing its closing ]], or wrapping badly has a layout/overflow bug at the expanded length real translations will hit.

Regenerate the pseudo-locale whenever your source strings change (a good CI or pre-commit step) so it never drifts from en.json.

Develop / run from source

Not published to any registry yet. To use it from a checkout of this repo:

cd packages/pseudo
npm install
npm run build            # emits dist/ (library + bin)
node dist/cli.js locales/en.json --out locales/en-XA.json

Gate the package:

npm run typecheck        # tsc --noEmit
npm test                 # vitest
npm run build            # tsc → dist/

License

MIT