{"_id":"@3sln/bab","_rev":"2-d7701b74d85adcc7bd5c272b716479b2","name":"@3sln/bab","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@3sln/bab","version":"0.1.0","keywords":["i18n","l10n","localization","translation","plural"],"author":{"name":"Ray Stubbs"},"license":"MIT","_id":"@3sln/bab@0.1.0","maintainers":[{"name":"ray.3sln","email":"contact+npm@3sln.com"}],"homepage":"https://github.com/3sln/bab#readme","bugs":{"url":"https://github.com/3sln/bab/issues"},"dist":{"shasum":"09c0446cffbab5a07d33a54dab61e86641698eed","tarball":"https://registry.npmjs.org/@3sln/bab/-/bab-0.1.0.tgz","fileCount":4,"integrity":"sha512-npN6fxcYuB8uUOImVdnu1aPoKzWt3t2s3F4ZVI30dTGi0BWXWt79hEnkBkYNs4WTFLZO8GhxIfqNQl7+qKK8dA==","signatures":[{"sig":"MEYCIQCPUk8leK/+9mI5twDDSi+dC8J2M39Cy9uejxe1RuMv2gIhAOilvc3oxd5tcgvwkpDuWG+mleerVAEjLSWmKKPbT9Dt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@3sln%2fbab@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":13765},"main":"index.js","type":"module","exports":{".":"./index.js","./package.json":"./package.json"},"gitHead":"225b1ccc1dbc555525ec33fd22364b0bbf979a47","scripts":{"test":"bun test","format":"prettier -w \"**/*.js\"","test:watch":"bun test --watch"},"_npmUser":{"name":"ray.3sln","email":"contact+npm@3sln.com"},"repository":{"url":"git+https://github.com/3sln/bab.git","type":"git"},"_npmVersion":"10.9.8","description":"A small, pluggable localization library.","directories":{},"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"prettier":"^3.6.2"},"_npmOperationalInternal":{"tmp":"tmp/bab_0.1.0_1785856931848_0.457026592250112","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@3sln/bab","version":"0.2.0","description":"A small, pluggable localization library.","main":"index.js","type":"module","author":{"name":"Ray Stubbs"},"repository":{"type":"git","url":"git+https://github.com/3sln/bab.git"},"license":"MIT","exports":{".":"./index.js","./package.json":"./package.json"},"workspaces":["extract"],"keywords":["i18n","l10n","localization","translation","plural"],"publishConfig":{"access":"public"},"scripts":{"test":"bun test","test:watch":"bun test --watch","format":"prettier -w \"**/*.js\""},"devDependencies":{"prettier":"^3.6.2"},"_id":"@3sln/bab@0.2.0","gitHead":"9252aad7bae60c48f2130fd6155f5ee7d600935b","bugs":{"url":"https://github.com/3sln/bab/issues"},"homepage":"https://github.com/3sln/bab#readme","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-/uzeiRn2Neo8UMzMUJTrPfj7vFWnoYHFELnezMQrWZb8EAfW7+EM8ZZ7Sq9EqD7YjVwjYVc/IlkNkIXLxu3U9w==","shasum":"34201d2ffd54856f9edc4dd8f1c67078d089ba8b","tarball":"https://registry.npmjs.org/@3sln/bab/-/bab-0.2.0.tgz","fileCount":4,"unpackedSize":14406,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@3sln%2fbab@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCXV6M4G+FDotfRymDeC9URKx91zkoHk/AH/jac4B3fTQIgVTANEWPf0yIEgm7wQlJ0kz8dgZ3BFST048LJSL1qa+U="}]},"_npmUser":{"name":"ray.3sln","email":"contact+npm@3sln.com"},"directories":{},"maintainers":[{"name":"ray.3sln","email":"contact+npm@3sln.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bab_0.2.0_1786077976399_0.2244705244241536"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T15:22:11.666Z","modified":"2026-08-07T04:46:16.930Z","0.1.0":"2026-08-04T15:22:11.977Z","0.2.0":"2026-08-07T04:46:16.548Z"},"bugs":{"url":"https://github.com/3sln/bab/issues"},"author":{"name":"Ray Stubbs"},"license":"MIT","homepage":"https://github.com/3sln/bab#readme","keywords":["i18n","l10n","localization","translation","plural"],"repository":{"type":"git","url":"git+https://github.com/3sln/bab.git"},"description":"A small, pluggable localization library.","maintainers":[{"name":"ray.3sln","email":"contact+npm@3sln.com"}],"readme":"# @3sln/bab\n\nA small, pluggable localization library. No dependencies — plurals and number\nformatting go through `Intl`, which the runtime already has.\n\n```sh\nnpm install @3sln/bab\n```\n\n```javascript\nimport { createTranslator } from '@3sln/bab';\n\nconst tr = createTranslator({ locale: 'es', catalogue: messages });\n\nconst s = tr('My string');\nconst x = tr('{#} days', { count: n }).singular('{#} day');\nconst scoped = tr.scope('my-domain');\n```\n\n## The source text is the key\n\nThere is no `t('home.header.title')` indirection to keep in sync with a\ncatalogue. What you write is what an untranslated build renders, so a missing\ntranslation degrades to correct English rather than to a dotted path — and\nadding a language never means touching the call sites.\n\n## A translation is a String\n\n`tr(...)` returns a `String` object. It interpolates, concatenates, compares\nwith `==`, has `.length`, and hands to a DOM node exactly like a primitive:\n\n```javascript\nconst s = tr('Sign out');\n`${s}`            // 'Sign out'\ns.toUpperCase()   // 'SIGN OUT'\nbutton(s)         // works\n```\n\nIt carries `.singular()` so a plural can be refined after the fact, which is\nwhat lets the plural case read as one expression rather than a three-argument\n`ngettext`. `.singular()` returns a *new* translation — a String's value is\nfixed at construction.\n\n## Plurals\n\nThe plural msgid is written first and is the catalogue key; `.singular()`\nsupplies the source language's other half, used when nothing is translated yet:\n\n```javascript\ntr('{#} days', { count: 1 }).singular('{#} day')  // '1 day'\ntr('{#} days', { count: 3 }).singular('{#} day')  // '3 days'\n```\n\nSelection goes through `Intl.PluralRules`, so a locale with more than two\ncategories is a catalogue concern rather than an API one. A Russian catalogue\nsupplies `one` / `few` / `many` / `other` for the same call site; if it only has\n`other`, that's what renders rather than nothing.\n\n`{#}` is the count, formatted for the locale (`1.234` in `de`). `{name}` is\n`params.name`. An unknown placeholder is left as written, so a typo is visible\ninstead of silently deleting text.\n\n## Lookup is pluggable\n\nA catalogue is anything with `lookup(scope, id, category)` returning a string,\nor `undefined` to fall back to the source text — a bare function will do.\n\n| Catalogue | Use |\n| --- | --- |\n| `SourceCatalogue` | The default: renders source text. An untranslated build. |\n| `ObjectCatalogue` | A plain messages object, which is what a compiled `.json` bundle is. |\n| `ChainCatalogue` | Try each in turn — per-page overrides over a base bundle, or machine translation behind human translations. |\n\n```javascript\n{\n  '': { 'Sign out': 'Cerrar sesión' },                        // unscoped\n  player: { '{#} days': { one: '{#} día', other: '{#} días' } },\n}\n```\n\nA scope falls back to the unscoped bucket, so a string shared across scopes is\ntranslated once.\n\n## Missing messages\n\n`onMissing(scope, id)` fires once per message — a coverage hook for development.\nProduction stays silent and renders the source text: a missing translation is\nnever worth a blank page.\n\n## Extraction\n\nBecause the msgid is the source text, extraction is never a step you have to\nrun before the app works — it is how you find out what there is to translate.\n[`@3sln/bab-extract`](extract) reads the source with a parser and follows\ntranslators through imports and bindings, so a scope fixed in `src/i18n.js` is\nknown at a call site three modules away:\n\n```sh\nnpx @3sln/bab-extract 'src/**/*.{js,jsx}' -x '**/*.test.js' -o messages.json\n```\n\nIt writes a bab catalogue, or a gettext POT for the platforms that speak it,\nand it is a separate package so that bab itself stays dependency-free.\n\n## Releasing\n\nBumping `version` in `package.json` on `main` opens a draft release. Publishing\nthat draft creates the tag and publishes to npm, after re-running the tests and\nchecking the tag matches `package.json` — a version bump alone tags nothing.\n","readmeFilename":"README.md"}