{"_id":"@021.is/spine-i18n","_rev":"2-ee6f87de4207047cda83ddf56fb120e3","name":"@021.is/spine-i18n","dist-tags":{"latest":"0.4.3"},"versions":{"0.4.0":{"name":"@021.is/spine-i18n","version":"0.4.0","license":"MIT","_id":"@021.is/spine-i18n@0.4.0","maintainers":[{"name":"edvone","email":"edvard@edvone.dev"}],"homepage":"https://github.com/021is/spine-ts#readme","bugs":{"url":"https://github.com/021is/spine-ts/issues"},"bin":{"spine-i18n":"dist/adapters/driving/cli.js"},"dist":{"shasum":"a49b7fb62f81ccbc3daf2b0036308f7824888ec9","tarball":"https://registry.npmjs.org/@021.is/spine-i18n/-/spine-i18n-0.4.0.tgz","fileCount":19,"integrity":"sha512-qLVUeHhOPUZWxzONMmoG7a+lHN2aS92CwY8MjJUwAkQFpltdj3haH//XthpteezEn/J/uVOR59DTIDJVxzPKEg==","signatures":[{"sig":"MEUCIQC6Kr8/zram8OuI4WzF4ViCSwLgMlBpSf3bMQ0nYjlNzAIgI2pNcrglcgCzKEyrgCtXutr5bYq0iW5YsjuyRQlH+qQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35809},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./fs":{"types":"./dist/adapters/driven/fs.d.ts","import":"./dist/adapters/driven/fs.js"},"./r2":{"types":"./dist/adapters/driven/r2.d.ts","import":"./dist/adapters/driven/r2.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"}},"gitHead":"e4434c6f59db8f586e0f05ffdb3b88a96e68e67b","scripts":{"build":"tsup src/index.ts src/react.tsx src/server.ts src/adapters/driven/r2.ts src/adapters/driven/fs.ts src/adapters/driving/cli.ts --format esm --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"edvone","email":"edvard@edvone.dev"},"repository":{"url":"git+https://github.com/021is/spine-ts.git","type":"git","directory":"packages/i18n"},"_npmVersion":"10.9.4","description":"i18n runtime for every product. Hexagonal: domain + ports + R2/fs/memory adapters. CLDR plurals. React + server runtimes. Modeled on a proven Kotlin locale library.","directories":{},"_nodeVersion":"22.22.0","dependencies":{"cac":"^6.7.14","kleur":"^4.1.5","@021.is/spine-errors":"^0.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/react":"^19.2.15","@aws-sdk/client-s3":"^3.668.0"},"peerDependencies":{"react":">=18","@aws-sdk/client-s3":">=3"},"peerDependenciesMeta":{"react":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/spine-i18n_0.4.0_1779981951974_0.6014638442336466","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@021.is/spine-i18n","version":"0.4.3","license":"MIT","description":"i18n runtime for every product. Hexagonal: domain + ports + R2/fs/memory adapters. CLDR plurals. React + server runtimes. Modeled on a proven Kotlin locale library.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","bin":{"spine-i18n":"dist/adapters/driving/cli.js"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./r2":{"types":"./dist/adapters/driven/r2.d.ts","import":"./dist/adapters/driven/r2.js"},"./fs":{"types":"./dist/adapters/driven/fs.d.ts","import":"./dist/adapters/driven/fs.js"}},"scripts":{"build":"tsup src/index.ts src/react.tsx src/server.ts src/adapters/driven/r2.ts src/adapters/driven/fs.ts src/adapters/driving/cli.ts --format esm --dts --clean","typecheck":"tsc --noEmit"},"dependencies":{"@021.is/spine-errors":"^0.4.3","cac":"^6.7.14","kleur":"^4.1.5"},"peerDependencies":{"react":">=18","@aws-sdk/client-s3":">=3"},"peerDependenciesMeta":{"react":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"devDependencies":{"@aws-sdk/client-s3":"^3.668.0","@types/react":"^19.2.15"},"repository":{"type":"git","url":"git+https://github.com/021is/spine-ts.git","directory":"packages/i18n"},"publishConfig":{"access":"public"},"_id":"@021.is/spine-i18n@0.4.3","gitHead":"b9f9333776fa55686cc2feb05f851aa6000b0e05","bugs":{"url":"https://github.com/021is/spine-ts/issues"},"homepage":"https://github.com/021is/spine-ts#readme","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-L49OtuSvGLZR206WHaX2BF47aFpUzllswMhLf0chnk6LO2Xuto7nHTuQFgRK6FLzUmhm848Ys01sZ8ZWvYyjqA==","shasum":"d15253482a8719e2864fc70d8a2df4bf3721e5af","tarball":"https://registry.npmjs.org/@021.is/spine-i18n/-/spine-i18n-0.4.3.tgz","fileCount":20,"unpackedSize":37802,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAFeyThwbIO8UGeDBI16OWAwansDQ9HN9YfBLPDFz1MsAiEAijmXjdteXKIUF8K2Po3O4p3tqPaQZsSvvXqGhjS/IFA="}]},"_npmUser":{"name":"edvone","email":"edvard@edvone.dev"},"directories":{},"maintainers":[{"name":"edvone","email":"edvard@edvone.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/spine-i18n_0.4.3_1779995241106_0.8268859376422961"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-28T15:25:51.743Z","modified":"2026-05-28T19:07:21.419Z","0.4.0":"2026-05-28T15:25:52.116Z","0.4.3":"2026-05-28T19:07:21.257Z"},"bugs":{"url":"https://github.com/021is/spine-ts/issues"},"license":"MIT","homepage":"https://github.com/021is/spine-ts#readme","repository":{"type":"git","url":"git+https://github.com/021is/spine-ts.git","directory":"packages/i18n"},"description":"i18n runtime for every product. Hexagonal: domain + ports + R2/fs/memory adapters. CLDR plurals. React + server runtimes. Modeled on a proven Kotlin locale library.","maintainers":[{"name":"edvone","email":"edvard@edvone.dev"}],"readme":"# @021.is/spine-i18n\n\nHexagonal i18n: domain + ports + R2/fs/memory adapters + React + server runtime. CLDR plural forms via `Intl.PluralRules`. Modeled on a proven Kotlin locale library.\n\n## Catalog format\n\n```json\n{\n  \"namespaces\": {\n    \"auth\": {\n      \"signin.title\": { \"other\": \"Sign in\" },\n      \"messages.unread\": { \"one\": \"1 message\", \"other\": \"{count} messages\" }\n    }\n  }\n}\n```\n\nOne JSON file per locale at `src/i18n/<bcp47>.json` (e.g., `en.json`, `de.json`, `pt-BR.json`).\n\n## Use — server (Next RSC, metadata, JSON-LD)\n\n```ts\nimport { getServerLocaleRuntime } from \"@021.is/spine-i18n/server\";\nimport { makeFsCatalogStore } from \"@021.is/spine-i18n/fs\";\n\nconst store = makeFsCatalogStore(\"./src/i18n\");\nconst runtime = await getServerLocaleRuntime(store, \"de\", \"en\");\n\nruntime.t(\"auth.signin.title\");                      // \"Anmelden\"\nruntime.t(\"auth.messages.unread\", { count: 1 });     // \"1 message\" (uses 'one' form)\nruntime.fmtNumber(1234.5);                            // \"1.234,5\" (German locale)\nruntime.fmtCurrency(99, \"EUR\");                       // \"99,00 €\"\nruntime.fmtDate(new Date(), { dateStyle: \"long\" });   // \"30. Mai 2026\"\n```\n\n## Use — React client\n\n```tsx\nimport { LocaleProvider, useT } from \"@021.is/spine-i18n/react\";\n\n<LocaleProvider initial={runtime}>\n  <App />\n</LocaleProvider>\n\nfunction SignInButton() {\n  const t = useT();\n  return <button>{t(\"auth.signin.title\")}</button>;\n}\n```\n\n## Locale negotiation\n\n```ts\nimport { negotiateLocale, makeLocale } from \"@021.is/spine-i18n\";\n\nconst locale = negotiateLocale({\n  urlLocale: \"de\",                                // 1st priority\n  cookieLocale: cookies.get(\"locale\"),            // 2nd\n  acceptLanguage: req.headers.get(\"accept-language\"), // 3rd\n  supported: [makeLocale(\"en\"), makeLocale(\"de\"), makeLocale(\"pt-BR\")],\n  fallback: makeLocale(\"en\"),\n});\n```\n\n## Catalog stores\n\n| | Use | Subpath |\n|---|---|---|\n| `makeR2CatalogStore` | Production — R2 is the source of truth, shared across all consumer apps | `@021.is/spine-i18n/r2` |\n| `makeFsCatalogStore` | Dev mode, CI tests | `@021.is/spine-i18n/fs` |\n| `makeMemoryCatalogStore` | Unit tests, in-process embedded use | `@021.is/spine-i18n` |\n\n## Key parity (mandatory)\n\nThe `spine/i18n-key-parity` rule in `@021.is/spine-lint` enforces that every `t(\"namespace.key\")` call exists in EVERY catalog loaded under `src/i18n/`. Prevents the silent production bug where one language renders the raw key text.\n","readmeFilename":"README.md"}