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

localeproof

v0.1.0

Published

Check that the locales your iOS app ships match the locales your App Store listing promises: String Catalogs and knownRegions versus fastlane metadata and screenshots folders. Zero dependencies, fully offline.

Readme

localeproof

Check that the locales your iOS app ships match the locales your App Store listing promises.

localeproof compares what Xcode actually localizes — String Catalogs (*.xcstrings), project.pbxproj knownRegions, *.lproj folders, and InfoPlist.xcstrings — against the locale folders in a fastlane deliver metadata/ and screenshots/ tree. It maps Xcode identifiers onto App Store Connect codes (dede-DE), runs fully offline, and returns a non-zero exit code so it can gate CI.

Apple's WWDC26 Localization Q&A states that a missing String Catalog localization may not fall back to the source language. A French catalog that omitted one key can show 1 instead of the English 1 video. Per-surface locale parity is how you catch that before review, not after a one-star screenshot.

It is the fourth check in a submission-preflight set: metaproof lints metadata text, screenproof lints screenshot files, claimproof reads what those screenshots actually say, and localeproof checks that the locales on each of those surfaces agree.

  • App locales from *.xcstrings, knownRegions, and *.lproj (never Base).
  • Optional fastlane metadata/<locale>/ and screenshots/<locale>/ trees; absent surfaces are skipped and listed under skipped in JSON.
  • Xcode → App Store mapping against Apple's 50-code App Store Connect locale table.
  • InfoPlist.xcstrings coverage so purpose strings are not left in English.
  • Zero runtime dependencies. Fully offline. No network, no credentials, no telemetry.

Requirements

Node.js 24 or newer, and zero runtime dependencies. The published package ships compiled JavaScript, so npx localeproof and npm install just work with no build step on your side. (The GitHub Action and local development run the TypeScript sources directly on Node's native type stripping.)

Install

npm install -g localeproof

Or run it without installing:

npx localeproof

Or add it to a project:

npm install --save-dev localeproof

Usage

localeproof [options]

| Option | Description | | --- | --- | | --app <dir> | App source root (default .). Scans *.xcstrings, *.pbxproj knownRegions, *.lproj, InfoPlist.xcstrings; skips .git, .build, DerivedData, Pods, Carthage, node_modules, .worktrees. | | --metadata <dir> | fastlane deliver metadata/ folder (default ./fastlane/metadata if it exists). | | --screenshots <dir> | fastlane deliver screenshots/ folder (default ./fastlane/screenshots if it exists). | | --config <file> | localeproof.json (rule severities, locales.extra, locales.ignore). | | --strict | Exit non-zero on warnings as well as errors. | | --json | Print the report as JSON. | | --quiet | Hide clean locales and info findings. | | --no-color | Disable ANSI color (also respects NO_COLOR). | | -h, --help | Show help. | | -v, --version | Show the version. |

Exit codes: 0 clean, 1 lint errors (or warnings under --strict), 2 usage or config error.

A missing --app scan (no catalogs, no pbxproj, no lproj) exits 2 with No app localizations found under <dir>. Metadata and screenshots directories are optional; rules that need an absent surface are skipped.

Example

$ localeproof --app . --metadata fastlane/metadata --screenshots fastlane/screenshots

localeproof  /path/to/MyApp

✖ es
    error   infoplist-locale-missing           InfoPlist.xcstrings is missing locale "es" that the app ships.
            Add "es" to InfoPlist.xcstrings so purpose strings are not left in the source language.
    error   app-locale-without-metadata        App ships "es" (→ es-ES) but metadata/es-ES/ is missing.
            Create metadata/es-ES/.
✖ zh
    warning app-locale-unmappable              App locale "zh" does not map to an App Store Connect code.
            Use a scripted Chinese code (zh-Hans / zh-Hant) or drop the unmappable locale.
✖ en_us
    warning unknown-locale-code                metadata/en_us/ is not a known App Store locale.
            Rename metadata/en_us/ to a code from Apple's locale table, or add it to locales.extra / locales.ignore.
✖ (project)
    warning source-language-mismatch           String Catalogs disagree on sourceLanguage (en, fr).
            Set every catalog's sourceLanguage to the same value.

Summary: 2 errors, 3 warnings — FAIL

What it checks

| Rule | Default | Fires when | | --- | --- | --- | | app-locale-without-metadata | error | An app locale maps to App Store code X and metadata/X/ is absent (metadata dir given). | | app-locale-without-screenshots | error | Same for screenshots/X/ (screenshots dir given). | | metadata-locale-without-app | warning | metadata/X/ exists but no app locale maps to X. | | screenshots-locale-without-app | warning | Same for screenshots. | | metadata-screenshots-mismatch | warning | X is in one of metadata/screenshots but not the other (both dirs given). | | unknown-locale-code | warning | A metadata/screenshots folder is not in APP_STORE_LOCALESlocales.extra and not in locales.ignore. Warning (not error) so one typo is not reported three times as an error; metaproof and screenproof already error on the same folder. | | app-locale-unmappable | warning | An app locale with xcodeToAppStore = null (for example eu or zh with no script). | | infoplist-locale-missing | error | InfoPlist.xcstrings exists and an app (Localizable) locale is absent from it — the purpose-string-in-English bug. | | infoplist-catalog-missing | info | The app has at least two locales and no InfoPlist.xcstrings. | | catalog-locale-partial | warning | A locale is present in some .xcstrings catalogs but not all. | | source-language-mismatch | warning | Catalogs disagree on sourceLanguage. | | catalog-unreadable | error | A *.xcstrings file is unreadable or not valid JSON. Names the file; does not crash. |

Each finding is { rule, severity, locale, surface, message, hint }. Hints name the exact folder to create or the catalog to add the locale to.

Xcode → App Store mapping

xcodeToAppStore(id):

  1. Exact match if id is already one of Apple's 50 App Store Connect codes.
  2. Language-only remap (below) when Xcode ships a language without a region.
  3. Regional variants that are not themselves App Store codes (de-AT, fr-CH, es-419), zh without a script, and anything else map to null (unmappable). Base is never a locale.

| Xcode | App Store | | --- | --- | | en | en-US | | de | de-DE | | es | es-ES | | fr | fr-FR | | nl | nl-NL | | ar | ar-SA | | pt | pt-BR | | nb | no | | bn | bn-BD | | gu | gu-IN | | kn | kn-IN | | ml | ml-IN | | mr | mr-IN | | or | or-IN | | pa | pa-IN | | sl | sl-SI | | ta | ta-IN | | te | te-IN | | ur | ur-PK |

Identity (the Xcode id is already the App Store code): ca, cs, da, el, fi, he, hi, hr, hu, id, it, ja, ko, ms, no, pl, ro, ru, sk, sv, th, tr, uk, vi, plus every hyphenated code in Apple's table (en-GB, zh-Hans, pt-PT, …).

The 50-code table is Apple's App Store Connect API locale-shortcode list as of 2026-09-02, matching the App Store localizations reference and fastlane FastlaneCore::Languages::ALL_LANGUAGES.

Non-locale folders skipped on the deliver surfaces: review_information, trade_representative_contact_information, default.

Configuration

Pass --config <file>, or drop a localeproof.json in your working directory and localeproof loads it automatically. Every key is optional and merges over the defaults.

{
  "rules": {
    "infoplist-catalog-missing": "warning",
    "metadata-locale-without-app": "off"
  },
  "locales": {
    "extra": ["en-IN"],
    "ignore": ["it"]
  }
}
  • rules: set a rule to error, warning, info, or off. An unknown id is a config error rather than a setting that is accepted and never applied; an obvious near miss is named in the message.
  • locales.extra: additional valid locale codes.
  • locales.ignore: locale folders to skip entirely.

See examples/localeproof.json.

GitHub Action

- uses: vsolano9/[email protected]
  with:
    app: .
    metadata: fastlane/metadata
    screenshots: fastlane/screenshots
    strict: "false"

The exact tag keeps CI reproducible. The moving @v0 tag points to the same v0.1.0 release for users who prefer automatic compatible updates. The action sets up Node 24 and runs localeproof, so a failed check blocks the workflow. See action.yml.

Programmatic API

import { lint, renderHuman, exitCode } from "localeproof";

const report = await lint({
  app: ".",
  metadata: "fastlane/metadata",
  screenshots: "fastlane/screenshots",
});
console.log(renderHuman(report));
process.exit(exitCode(report, false));

lint(paths, config?) returns a LintReport with per-locale findings, skipped rule ids, and severity counts. defaultConfig(), mergeConfig(), and loadConfig() build the config; renderHuman(), renderJson(), and exitCode() format and gate it. xcodeToAppStore() is the mapping used by the linter.

Limitations

  • localeproof does not read .strings file content, keyword copy, screenshot pixels, or screenshot claims. Use metaproof for metadata text, screenproof for screenshot files, and claimproof for what those screenshots say.
  • A zh Xcode locale with no script is reported as unmappable rather than guessed as zh-Hans.
  • Screenshot locale folders count only if they contain at least one .png / .jpg / .jpeg.

Validation

npm run lint    # typecheck + tests
npm test        # node --test
npm run build   # compile the published package

Changelog

Release history is in CHANGELOG.md.

Roadmap

  • [ ] --fix to scaffold missing metadata/<locale>/ folders.
  • [ ] App Store Connect API cross-check of live listing locales.

Contributing

Issues and pull requests are welcome. Keep the tool dependency-free, add a failing test before a change, and run npm run lint (typecheck plus tests) before opening a PR.

Maintainer

localeproof and its siblings metaproof, screenproof, and claimproof are built and maintained by Victor Solano, who runs them against twelve App Store apps before every submission.

License

MIT. See LICENSE. This is an independent tool. It is not affiliated with or endorsed by Apple or fastlane; "App Store" and "iOS" are trademarks of Apple Inc., used here only to describe compatibility.