{"_id":"@daanvandenbergh/i18nkit","_rev":"10-6560b75af61a65fbe071efb6bb2b0abe","name":"@daanvandenbergh/i18nkit","dist-tags":{"latest":"1.5.0"},"versions":{"1.0.0":{"name":"@daanvandenbergh/i18nkit","version":"1.0.0","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.0.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://github.com/daanvandenbergh/i18nkit#readme","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"075752118dc89fa09f37be24698ae0ad52dd2b1f","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.0.0.tgz","fileCount":35,"integrity":"sha512-8m2Y6zQTIPOIRaRG264Tmv5VKqcWD5TcrzuP6XObyp+/7Vdb4XClj6tQpLJ6xY4tUDPd3ix/a9qx6TdjqNykvA==","signatures":[{"sig":"MEYCIQCoAOkFM0lkC5a3ShUwHZ0oGSAJqHHmKNUuSxsZ4mplEAIhAOUHbH4Z2Ds8bR+z8CyYCAl6l11dfA4DldbZLGsJyJCd","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":177641},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"909801286385e0827dc123c8061cf4748bcbab15","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.0.0_1783521339287_0.8231242803726264","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@daanvandenbergh/i18nkit","version":"1.0.1","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.0.1","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://github.com/daanvandenbergh/i18nkit#readme","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"7191b3a6efd6a8e7bc143a8113e31e09d6eabe5c","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.0.1.tgz","fileCount":35,"integrity":"sha512-ff8Asif3uXEEJfUj7sRjkPLRN4uZ8gKY64DyDjNQFBdWBhUKWSy4RNS/L3aTEEkRFBuB2YWAcY5Pw8L8Fj266g==","signatures":[{"sig":"MEQCIEWO+E/7YT7Vip0k3P+KV4RYpmJOsDl25D1W147hPl+SAiA3iz0YNefvryhWB1e0oTC8fHeOgs+5r8i05UYgtRnfLQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":178033},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"1cf32f1f467d549d8291aecfdae54fa61fc5daf1","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.0.1_1783540682059_0.5371472021183792","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@daanvandenbergh/i18nkit","version":"1.0.2","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.0.2","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://github.com/daanvandenbergh/i18nkit#readme","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"041334541604a7a078b7507317c0c79302e327e5","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.0.2.tgz","fileCount":35,"integrity":"sha512-ydLsc/RXgb7WdVxVu2DA6DxlyktxHlU+teh34lGQRfpgWI05zG0Hnwva0acvsq4y1N/xRa8bn+bZwmxeLPUrLA==","signatures":[{"sig":"MEQCIA68WBylY+Mdy3hJW/+OoSLE3OdgIn0IBBKBkajAZ2lkAiASDKytrQNwfG5NVoeWKs9Nmw8KyZ4Z/MXFpT57BtOn6w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180511},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"5f64feb994e2d81770343b652c4cb01fd70e95a2","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/scribekit":"^1.0.0","@daanvandenbergh/claude-code-utils":"^1.0.1"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.0.2_1783675196868_0.5934292765350344","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@daanvandenbergh/i18nkit","version":"1.0.3","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.0.3","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://github.com/daanvandenbergh/i18nkit#readme","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"098178a1729f2f9db4ac6ee14e90812599172c7f","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.0.3.tgz","fileCount":35,"integrity":"sha512-d0ZAVniTbUxMI5qq7CGXeCrUSnMQUIQ/gzdl5rpf+0Ggeax+JzsYCNbUKT9+uN/PuJA+keJUuLcloBAry7mKng==","signatures":[{"sig":"MEYCIQCmZqRxffOKEsc80lLyhKed09jmBpUWnSryGj+Rykft6wIhAK8HveBnxqZTn7DXWvXsCjbqLapk0OsrrwWlaDntlDz/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180503},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"3a2809c1d9efd38fb2b117a25a0fea80c0565565","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.0.6","@daanvandenbergh/scribekit":"^1.7.2"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.0.3_1786459901475_0.7580192474354976","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@daanvandenbergh/i18nkit","version":"1.0.4","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.0.4","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://daanvandenbergh.github.io/i18nkit","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"47e070174532f47d3ac8c0c523d81aab053211e3","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.0.4.tgz","fileCount":35,"integrity":"sha512-9DIhjv0/D223/nhWITRxUxsSyntnBgObCxmt3MduEHTj9XPiVPFeR+g/L0nAz+Gzv1msTbkB/7B3G5KnLZKooA==","signatures":[{"sig":"MEQCIFnk14g+oJpfIV1X04CW2Laa3jkHkOUHmlFg3HfwdXveAiAXzzw9kP8lORAaEQKShlS9gSGkmdE38Jx0zmecsUSEGg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":184844},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"c8f3911c7e904b710e44c393d5e5fca67cc7b304","scripts":{"docs":"npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","docs:build":"npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/node":"^26.4.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.4.0","@daanvandenbergh/scribekit":"^2.0.0"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.0.4_1787954947913_0.06774422356664211","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@daanvandenbergh/i18nkit","version":"1.1.0","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.1.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://daanvandenbergh.github.io/i18nkit","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"f786481d8f292ff1743401e2f2d38ac966c9f8d0","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.1.0.tgz","fileCount":35,"integrity":"sha512-H0Lle8M9zvW6mQnJ2m6+nrGB01rCUslX7xvTutsV36772xPoh1gm9m+5tBRBajTv4WHe66I6RD/QbmMEUotXJA==","signatures":[{"sig":"MEQCIGAnvB4isoe+x776h7mWssUikpf29oKWce3TauaUP98HAiAJkcaYtLA7FNmU7b+2Jqb3A+LevsPen27s4GN+FYgYiw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIEuRQ4N0WRvVh7uEGD77K/1cFZisjS3JiER6Bw1CpOizAiB132rro4iTS+1OjUFD7DDhH8QSVYtb+AFjKIonebPhXw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":208204},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"61e4d9680a82e33c809039f84e7a51a4abf892d5","scripts":{"docs":"npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","docs:build":"npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/node":"^26.4.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.4.1","@daanvandenbergh/scribekit":"^2.0.0"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.1.0_1790335020652_0.3057758180932517","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@daanvandenbergh/i18nkit","version":"1.2.0","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.2.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://daanvandenbergh.github.io/i18nkit","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"c9f960c61406bc4be6178da6631e56db65042b61","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.2.0.tgz","fileCount":35,"integrity":"sha512-325i/FmPmJMRzE4h0/I0MFYPcSUVHNcNJegcYlkRo7ulyIqQSWPpSXQLbt4SFyzwPJ88dbtR/i2DiT6Odb/hrQ==","signatures":[{"sig":"MEQCIH92NTyFuA0hdgng+V+ebBGHqKCKyyMAo3sI8aRePUysAiABEfSzD5wVBzGYU7KdUeXZ749G4x8sWtsqG913Add3Aw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQD8bqMBXPWDofaLlaq9v+zbNo3t01UYvZxy6ERlHE1r/gIgNRZ7FzOqpjsZaGclSPYCVfi0Q6xOV827Ga+8S5EbuoY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":215262},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"095136d54de3223fa476990ce24f03f5084525c4","scripts":{"docs":"npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","docs:build":"npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/node":"^26.4.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.4.1","@daanvandenbergh/scribekit":"^2.0.0"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.2.0_1790423140543_0.27162273590621067","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@daanvandenbergh/i18nkit","version":"1.3.0","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.3.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://daanvandenbergh.github.io/i18nkit","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"827d994393b1aa6aac75aee2632b2c019407a2dd","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.3.0.tgz","fileCount":35,"integrity":"sha512-gYpvp4ZdL/e1FMB0hFl/4WLPX5Mrkp10f4qxUkzIoEGgKD5iNR7sQM+MqmAgCcnSHx/WPloMAQsOQ1ifE626QA==","signatures":[{"sig":"MEYCIQC1FxqcY6mv49tXzFgmidcYLGJTwVYllrXNxocLdjLuqAIhAKO1DTM5Wls50/sQ08FLC3xn8L1jHhSgb3piywIMrRWL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDTMkJqf6MdkUdhSQ3ZbgfmeRmejPENYbfu7Xrgd6MWZQIgFQK2GtgA0uSoj+xd56sdQAH4CUVIPQjNIUptu+Kfhco=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":215686},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"095136d54de3223fa476990ce24f03f5084525c4","scripts":{"docs":"npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","docs:build":"npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/node":"^26.4.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.4.1","@daanvandenbergh/scribekit":"^2.0.0"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.3.0_1790690891683_0.2661347072811655","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@daanvandenbergh/i18nkit","version":"1.4.0","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"author":{"name":"Daan van den Bergh"},"_id":"@daanvandenbergh/i18nkit@1.4.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"homepage":"https://daanvandenbergh.github.io/i18nkit","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"c7c3d852261d052b69a998b12db0541d9e2af93d","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.4.0.tgz","fileCount":35,"integrity":"sha512-wZ7Hne9f019UJqA+x7vdKfFEn01/pAV5uZ+5UDwqdFp0QpqU6syspte5piEhm+GRPRVHdsyOw7kuGhLiBWtCDg==","signatures":[{"sig":"MEUCIQDqR4UVcocjF+TeF+pvITNwAPxt+88H1zVG1/XTNMhtJQIgKNsfNNWlk8YGUGt6HYJ50KKX0ozv4NxbYtdlIxYhdqE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCDV0s7sEEJlM53B/IwMa9BSrFVCF3YU2cxKxY1gv/JIQIgJ81V/ERmdJDa9OTkvmpG07pfczfcXAiK+OGNpJwS+pw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":221573},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"095136d54de3223fa476990ce24f03f5084525c4","scripts":{"docs":"npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","docs:build":"npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/node":"^26.4.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.4.1","@daanvandenbergh/scribekit":"^2.0.0"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/i18nkit_1.4.0_1790702750401_0.20244187268499259","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"_id":"@daanvandenbergh/i18nkit@1.5.0","bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"dist":{"shasum":"29018456ccf86136c3b8fd04e87f706ceadc453a","tarball":"https://registry.npmjs.org/@daanvandenbergh/i18nkit/-/i18nkit-1.5.0.tgz","fileCount":35,"integrity":"sha512-cOI1Un2PdOryH6l1etqgW/qT6isD+y4Jv4RUaOJOtqClAzxEobsW7Yj5SROXLF0Oa+YzPNYC5EMhLU+j82Csxg==","signatures":[{"sig":"MEUCIQCImRzRE2W4IX39p2DwvGDrelgXR46sOcp4UgYpVSiMjAIgOJox4Ntb95unegSazsknYNrFHXCzHM2HkY/LfyzpCbM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDBX1m7wN4MOO7o69yOaowBgTUMVLUQIixq9Gx2Cs2uFgIgVz+i6Y+fBsVILTP+QNejzpYg5Y1rxYENoo2UGPuXoaE="}],"unpackedSize":223573},"main":"./dist/index.js","name":"@daanvandenbergh/i18nkit","type":"module","types":"./dist/index.d.ts","author":{"name":"Daan van den Bergh"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js"},"./styles.css":"./dist/react/styles.css","./package.json":"./package.json"},"gitHead":"095136d54de3223fa476990ce24f03f5084525c4","scripts":{"docs":"npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && cp src/react/styles.css dist/react/styles.css","typecheck":"tsc --noEmit","docs:build":"npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"version":"1.5.0","_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"homepage":"https://daanvandenbergh.github.io/i18nkit","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"_npmVersion":"11.12.1","description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","directories":{},"maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25","react":"^19","vitest":"^3.0.0","react-dom":"^19","typescript":"^5.6.0","@types/node":"^26.4.0","@types/react":"^19","@types/react-dom":"^19","@vitest/coverage-v8":"^3.2.7","@daanvandenbergh/claudekit":"^1.4.1","@daanvandenbergh/scribekit":"^2.0.0"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/i18nkit_1.5.0_1790885422244_0.32912838832414915"}}},"time":{"created":"2026-07-08T14:35:39.148Z","modified":"2026-10-01T20:10:22.503Z","1.0.0":"2026-07-08T14:35:39.445Z","1.0.1":"2026-07-08T19:58:02.212Z","1.0.2":"2026-07-10T09:19:57.017Z","1.0.3":"2026-08-11T14:51:41.617Z","1.0.4":"2026-08-28T22:09:08.064Z","1.1.0":"2026-09-25T11:17:00.763Z","1.2.0":"2026-09-26T11:45:40.639Z","1.3.0":"2026-09-29T14:08:11.763Z","1.4.0":"2026-09-29T17:25:50.500Z","1.5.0":"2026-10-01T20:10:22.348Z"},"bugs":{"url":"https://github.com/daanvandenbergh/i18nkit/issues"},"author":{"name":"Daan van den Bergh"},"homepage":"https://daanvandenbergh.github.io/i18nkit","keywords":["i18n","internationalization","localization","typescript","type-safe","react","translations"],"repository":{"url":"git+https://github.com/daanvandenbergh/i18nkit.git","type":"git"},"description":"Type-safe i18n for TypeScript - miss a translation and it's a compile error. Framework-agnostic core, optional React layer, and a Claude Code skill that sweeps for untranslated strings.","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"readme":"# i18nkit\n\n![i18nkit - type-safe i18n for TypeScript](.agentstore/scribekit-hero/readme/hero.png)\n\n**Universal, 100% type-safe i18n for TypeScript sites.** Declare your locales once and the compiler\nguarantees every string is translated into every language - add a locale and TypeScript lights up at\nevery translation you still owe.\n\n- **Type-safe by construction** - a translation is an object with one entry per locale. Miss one and it is a compile error, not a runtime `undefined`.\n- **Framework-agnostic core** - zero dependencies, no `next`, no `react`. Runs on the server, in the browser, and at the edge.\n- **Optional React layer** - a provider, hooks, an accessible `<LanguagePicker>`, and a locale-aware `<LocaleLink>` (decoupled from any router).\n- **Parameterized text** - per-locale builder functions keep word order correct in every language.\n- **Locale detection & URL routing** included - `Accept-Language` matching, cookie resolution, and \"default at bare URL, others under `/nl/…`\" helpers.\n\n```ts\n// Add \"de\" to your locales...\nconst i18n = new I18n({\n    locales: { en: { label: \"English\" }, nl: { label: \"Nederlands\" }, de: { label: \"Deutsch\" } },\n    default: \"en\",\n});\n\n// ...and every catalog missing a German string is now a compile error:\ni18n.defineTextCatalog({\n    title: { en: \"Customers\", nl: \"Klanten\" },\n    //     ~~~~~ Property 'de' is missing\n});\n```\n\n**[Read the documentation](https://daanvandenbergh.github.io/i18nkit)** - guides, tutorials, and the\nfull API reference.\n\n---\n\n## Install\n\n```bash\nnpm install @daanvandenbergh/i18nkit\n```\n\n`react` / `react-dom` are optional peer dependencies - you only need them if you use the `/react` entry.\n\n## Install the Claude Code skills\n\ni18nkit ships two agent skills:\n\n- **`i18nkit-sweep`** - sweeps your source for user-facing strings that bypass the type-safe system\n  (and, if you use `@daanvandenbergh/scribekit`, checks every post has a translation and its own\n  localized hero for each locale). Report-only by default; run it with `/i18nkit-sweep`.\n- **`i18nkit-add-locale`** - adds a new language: edits your `I18n` config, then compiler-drives the\n  translation of every catalog entry `tsc` flags (plus scribekit posts and hardcoded locale lists).\n  Run it with `/i18nkit-add-locale`.\n\nWire them into Claude Code with **symlinks**, so they track the installed package version - an\n`npm update` keeps the skills current, with no copy to re-sync:\n\n```bash\nmkdir -p .claude/skills\n# from your project root (assumes .claude/skills/ and node_modules/ share this root):\nfor skill in i18nkit-sweep i18nkit-add-locale; do\n    ln -s \"../../node_modules/@daanvandenbergh/i18nkit/skills/$skill\" \".claude/skills/$skill\"\ndone\n```\n\nIf your layout differs, point the links at an absolute target resolved by node:\n\n```bash\npkg=\"$(dirname \"$(node -p \"require.resolve('@daanvandenbergh/i18nkit/package.json')\")\")\"\nfor skill in i18nkit-sweep i18nkit-add-locale; do\n    ln -s \"$pkg/skills/$skill\" \"$(pwd)/.claude/skills/$skill\"\ndone\n```\n\nVerify with `ls .claude/skills/i18nkit-sweep/SKILL.md .claude/skills/i18nkit-add-locale/SKILL.md`, then\ninvoke `/i18nkit-sweep` or `/i18nkit-add-locale` in Claude Code. (The targets exist once you have run\n`npm install`.) Developing against a local checkout of this repo? Link the source folders instead:\n`ln -s ../../skills/$skill .claude/skills/$skill`.\n\n## Set up once\n\nCreate your locale set in one place and export the instance (and its `Locale` type):\n\n```ts\n// app/i18n.ts\nimport { I18n } from \"@daanvandenbergh/i18nkit\";\n\nexport const i18n = new I18n({\n    locales: {\n        en: { label: \"English\",    htmlLang: \"en\", locale: \"en-GB\" },\n        nl: { label: \"Nederlands\", htmlLang: \"nl\", locale: \"nl-NL\" },\n    },\n    default: \"en\",\n});\n\nexport type Locale = keyof typeof i18n.locales; // \"en\" | \"nl\"\n```\n\n`htmlLang` (for `<html lang>` / URL prefixes) and `locale` (for `Intl` formatting) each default to the\nlocale's key, so a BCP-47-keyed config can be as short as `en: { label: \"English\" }`.\n\n## Write translations\n\nAuthor catalogs through the instance. Every entry must cover every locale, checked at the call site:\n\n```ts\n// app/customers/text.ts\nimport { i18n } from \"../i18n\";\n\nexport const TX = i18n.defineTextCatalog({\n    title:   { en: \"Customers\", nl: \"Klanten\" },\n    // Parameterized text: a builder per locale keeps word order native to each language.\n    deleted: { en: (name: string) => `${name} deleted`, nl: (name: string) => `${name} verwijderd` },\n    range:   {\n        en: (from: number, to: number, total: number) => `Showing ${from}-${to} of ${total}`,\n        nl: (from: number, to: number, total: number) => `${from}-${to} van ${total} weergegeven`,\n    },\n});\n```\n\n## Resolve to strings\n\nBind a translator to a locale and call it:\n\n```ts\nconst translate = i18n.translator(\"nl\");\n\ntranslate(TX.title);            // \"Klanten\"\ntranslate(TX.deleted, \"Ada\");   // \"Ada verwijderd\"\ntranslate(TX.range, 1, 20, 137); // \"1-20 van 137 weergegeven\"\n```\n\nFor a one-off, skip the binding: `i18n.translate(TX.title, \"nl\")`. For a string that is the same in\nevery language, `i18n.uniform(\"Acme\")` builds a fully-covered entry. For a standalone parameterized\nstring, `i18n.defineText({ en: (n) => …, nl: (n) => … })` enforces coverage and infers the arguments.\n\n### French spacing\n\nFrench sets a no-break space before `; ! ?` and `:` and inside `« »`, so a question mark never wraps\nalone onto the next line. Every string resolved for a French locale (`htmlLang` primary subtag `fr`,\nso `fr-CA` counts) gets it automatically: a regular space before `;`, `!` or `?` becomes a narrow\nno-break space (U+202F), one before `:` or `»` and after `«` a no-break space (U+00A0) - the split the\nImprimerie nationale prescribes. Write the copy with plain spaces:\n\n```ts\ni18n.translate({ fr: \"Quelle plante pour moi ?\", /* ... */ }, \"fr\"); // \"Quelle plante pour moi\\u202F?\"\n```\n\nOnly an existing space is converted - `10:30`, `https://` and `«Oui»` are untouched - and text already\nusing no-break spaces is left as is. It runs on the final string, so it covers a parameterized entry's\ninterpolated values too. Opt out with `new I18n({ ..., frenchSpacing: false })`; for French copy built\noutside a catalog, call `frenchSpacing(value)` yourself.\n\n## Detect the locale (server / SSR)\n\nThe core is framework-agnostic: feed it whatever your framework gives you.\n\n```ts\n// Anywhere you have the request: a cookie value and/or the Accept-Language header.\n// A stored cookie choice wins; a first-time visitor (no cookie) gets their browser preference.\n// Both helpers fall back to `default` internally, so gate on the cookie's presence - don't\n// chain them with `??`/`||` (resolveLocale never returns nullish, so the fallback is dead code).\nconst locale = cookieValue\n    ? i18n.resolveLocale(cookieValue)                 // honor the stored choice\n    : i18n.matchAcceptLanguage(acceptLanguageHeader); // else the visitor's preference\n```\n\nFor example, in a Next.js Server Component:\n\n```ts\nimport { cookies, headers } from \"next/headers\";\nimport { i18n } from \"@/app/i18n\";\n\nexport async function activeLocale() {\n    const cookie = (await cookies()).get(i18n.cookie)?.value;\n    const accept = (await headers()).get(\"accept-language\");\n    return cookie ? i18n.resolveLocale(cookie) : i18n.matchAcceptLanguage(accept);\n}\n```\n\n## React\n\nWrap your app in the provider, passing the resolved locale and what to do when it changes (the provider\nwrites the locale cookie for you first, then calls `onChange` - do the navigation there):\n\n```tsx\n// app/providers.tsx\n\"use client\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport { I18nProvider } from \"@daanvandenbergh/i18nkit/react\";\nimport \"@daanvandenbergh/i18nkit/styles.css\"; // for <LanguagePicker>\nimport { i18n } from \"./i18n\";\n\nexport function Providers({ locale, children }) {\n    const router = useRouter();\n    const pathname = usePathname();\n    // URL-routed site (the default): the path prefix decides the locale, so navigate to the new\n    // locale's URL. Writing the cookie alone would NOT switch locale on an already-prefixed page.\n    return (\n        <I18nProvider\n            i18n={i18n}\n            locale={locale}\n            onChange={(next) => router.push(i18n.switchLocalePath(pathname, next))}\n        >\n            {children}\n        </I18nProvider>\n    );\n}\n```\n\n> **Cookie-only sites** (locale resolved purely from the cookie, no `/nl/…` URL prefixes) can skip\n> `switchLocalePath` and use `onChange={() => router.refresh()}` (or `location.reload()`): a re-render\n> re-reads the cookie. But with URL routing - the default `\"prefix-except-default\"`, and `\"prefix-all\"`\n> - the prefix in the URL wins over the cookie, so `<LanguagePicker>` switches locale only if `onChange`\n> navigates via `switchLocalePath`. A bare reload/refresh reloads the same prefixed URL and does nothing.\n> Under `strategy: \"domain\"` the target is another origin - see [One domain per locale](#one-domain-per-locale).\n\nRead and switch the locale with the hooks, and drop in the picker:\n\n```tsx\n\"use client\";\nimport { useTranslator, LanguagePicker } from \"@daanvandenbergh/i18nkit/react\";\nimport { TX } from \"./customers/text\";\n\nexport function Header() {\n    const translate = useTranslator();\n    return (\n        <header>\n            <h1>{translate(TX.title)}</h1>\n            <LanguagePicker />\n        </header>\n    );\n}\n```\n\nHooks: `useTranslator()` (a translator bound to the active locale), `useLocale()`, `useSetLocale()`,\n`useI18n()`. Pass your `Locale` union as a type argument for a precise return, e.g. `useLocale<Locale>()`.\n\n`<LanguagePicker>` is self-contained (open state, click-away, Escape, full ARIA) and shows the endonym\nlabels by default. Want flags? Pass the built-in `localeFlag` renderer - a set of ~38 inline-SVG flags in\none flat, rounded style, keyed by locale so `en`, `pt-BR`, `de-CH` all resolve correctly (unknown locales\njust show their label, never a broken image):\n\n```tsx\nimport { LanguagePicker, localeFlag } from \"@daanvandenbergh/i18nkit/react\";\n\n<LanguagePicker renderFlag={localeFlag} />;\n```\n\nOr supply your own with `renderFlag={(loc) => <MyFlag code={loc} />}`. Pass `locales={[\"nl\", \"de\"]}` to list only some locales (say, one whose domain is not live yet). Pass `hrefFor={(loc) => i18n.localeUrl(path, loc)}` to render each locale as a real `<a hreflang>` link to its version of the page - in the server HTML, so search engines find every language version. Style it entirely through the\n`--i18nkit-*` CSS custom properties on the shipped stylesheet.\n\n`<LocaleLink href=\"/pricing\">` renders `/nl/pricing` when `nl` is active. It renders a plain `<a>` by\ndefault, or pass your framework's link via `as`: `<LocaleLink href=\"/pricing\" as={NextLink} />`.\n\n## URL routing\n\nConfig-driven helpers for the \"default locale at bare URLs, others under `/<htmlLang>/…`\" scheme\n(configure `nonLocalizedPrefixes` and `origin` on the instance):\n\n```ts\ni18n.prefixFor(\"nl\");                       // \"/nl\"   (\"\" for the default)\ni18n.localizeHref(\"/pricing\", \"nl\");        // \"/nl/pricing\"\ni18n.localizeHref(\"/pricing\", \"en\");        // \"/pricing\"  (default = bare)\ni18n.switchLocalePath(\"/nl/pricing\", \"en\"); // \"/pricing\"\ni18n.stripLocalePrefix(\"/nl/pricing\");      // \"/pricing\"\ni18n.hreflangAlternates(\"/pricing\", \"nl\");  // { canonical, languages: { en, nl, \"x-default\" } }\n```\n\nPass `strategy: \"prefix-all\"` to prefix every locale, including the default -\n`i18n.localizeHref(\"/pricing\", \"en\")` then returns `\"/en/pricing\"`, so no locale lives at a bare URL.\nThe default, `\"prefix-except-default\"`, keeps the default locale at bare URLs as shown above.\n\n**URL casing.** If a locale's `htmlLang` carries a region or script subtag (`en-GB`, `pt-BR`,\n`zh-Hant`), the generated URL prefix is lowercased - `htmlLang: \"en-GB\"` produces `/en-gb/pricing`,\nthe conventional casing for URLs - while `htmlLangFor(locale)` still returns `\"en-GB\"` for the\n`<html lang>` attribute and hreflang keys. Segment parsing (`localeForSegment`, `stripLocalePrefix`)\nis case-insensitive, so an inbound `/en-GB/…` or `/en-gb/…` both resolve. Plain lowercase primary\nsubtags (`en`, `nl`, `de`) are unaffected.\n\n### Middleware\n\ni18nkit stays out of your request pipeline: the helpers above are framework-agnostic, so you write\nthe redirect in your framework's own middleware and compose it from the instance. Detection is just\n`resolveLocale` (cookie) and `matchAcceptLanguage` (header). Under `\"prefix-all\"` a bare URL maps to\nno locale, so middleware **must** redirect it to a prefix or the site 404s on `/`:\n\n```ts\n// Next.js middleware.ts - prefix-all\nimport { NextResponse, type NextRequest } from \"next/server\";\nimport { i18n } from \"./i18n\";\n\nexport function middleware(req: NextRequest) {\n    const { pathname } = req.nextUrl;\n    if (!i18n.isLocalizedPath(pathname)) return NextResponse.next();  // /api/* -> pass through\n    if (i18n.localeForSegment(pathname.split(\"/\")[1] ?? \"\")) return NextResponse.next(); // /en/x -> serve\n    // bare URL: detect (cookie, else Accept-Language) and redirect to that locale's prefix\n    const cookie = req.cookies.get(i18n.cookie)?.value;\n    const locale = cookie\n        ? i18n.resolveLocale(cookie)\n        : i18n.matchAcceptLanguage(req.headers.get(\"accept-language\"));\n    const url = req.nextUrl.clone();\n    url.pathname = i18n.localizeHref(pathname, locale);\n    return NextResponse.redirect(url);\n}\n```\n\nUnder `\"prefix-except-default\"` the same shape applies, minus the redirect - a bare URL is already the\ndefault locale's real page, so you serve it as-is (optionally reading the locale for SSR).\n\n### One domain per locale\n\n`strategy: \"domain\"` serves each locale from its own origin - `https://example.nl/pricing`,\n`https://example.de/pricing` - and public paths never carry a prefix. `domains` must name exactly one\nbare origin per locale, or mounts one under `/<htmlLang>` on another's host (below). A missing locale is\na compile error; construction throws on a malformed origin such as a trailing slash, a stray path or\nuppercase, and on two locales at the root of one host. There is no single\n`origin` in this mode: `i18n.origin` is the default locale's domain.\n\n```ts\nexport const i18n = new I18n({\n    locales: { nl: { label: \"Nederlands\" }, de: { label: \"Deutsch\" }, fr: { label: \"Français\" } },\n    default: \"nl\",\n    strategy: \"domain\",\n    domains: { nl: \"https://example.nl\", de: \"https://example.de\", fr: \"https://example.fr\" },\n    nonLocalizedPrefixes: [\"/api\"],\n});\n\ni18n.originFor(\"de\");                      // \"https://example.de\"\ni18n.localizeHref(\"/pricing\", \"de\");       // \"/pricing\"  (in-page links stay on the current domain)\ni18n.localeUrl(\"/pricing\", \"de\");          // \"https://example.de/pricing\"  (cross-domain links, canonicals, emails)\ni18n.localeUrl(\"/de/pricing\", \"fr\");       // \"https://example.fr/pricing\"  (a [lang]-tree prefix is dropped)\ni18n.switchLocalePath(\"/pricing\", \"fr\");   // \"https://example.fr/pricing\"  (absolute: another origin)\ni18n.hreflangAlternates(\"/pricing\", \"de\"); // canonical https://example.de/pricing; languages nl/de/fr on their\n                                           //   own domains; x-default https://example.nl/pricing\ni18n.localeForHost(\"www.Example.de:443\");  // \"de\"  (case, port and a leading www. are ignored)\ni18n.localeForHost(\"localhost:3000\");      // null\n```\n\n**One locale under a path on another's domain.** A locale without a domain of its own can be mounted\non another locale's host under its `htmlLang`: `en: \"https://example.nl/en\"`. Its public URLs then\ncarry that prefix and everything else follows - `prefixFor(\"en\")` is `\"/en\"`, `originFor(\"en\")` is\nthe shared `\"https://example.nl\"`, and `localizeHref`, `localeUrl`, `switchLocalePath` and\n`hreflangAlternates` build `/en/...` URLs on it. `localeForHost(\"example.nl\")` answers with the locale\nat the host's ROOT (`nl`); a middleware serves a `/en/...` path on that host from the `en` tree and\nredirects it anywhere else.\n\n```ts\ndomains: { nl: \"https://example.nl\", de: \"https://example.de\", en: \"https://example.nl/en\" },\n\ni18n.localizeHref(\"/pricing\", \"en\");   // \"/en/pricing\"\ni18n.localeUrl(\"/pricing\", \"en\");      // \"https://example.nl/en/pricing\"\ni18n.localeUrl(\"/\", \"en\");             // \"https://example.nl/en\"\n```\n\n`stripLocalePrefix`, `localeForSegment` and `isLocalizedPath` keep working, for an internal `[lang]`\nroute tree that a middleware rewrites bare paths into.\n\n**Dev / single-host fallback.** On `localhost` (or any host that is not one of the `domains`) the other\ndomains are unreachable, so serve the site like `\"prefix-except-default\"` there: `prefixFor`,\n`localizeHref` and `switchLocalePath` take `{ prefixed: true }`, which answers exactly as that strategy\nwould (`/de/pricing`, the default at bare paths). Choose it per request with `localeForHost`:\n\n```ts\n// middleware: the host decides the locale on a real domain; on localhost the path prefix does\nconst hostLocale = i18n.localeForHost(req.headers.get(\"host\"));\nconst prefixed = hostLocale === null;\n```\n\n**Switching language.** The picker's `onChange` must leave the current origin, so load the absolute\nURL with a full navigation - and fall back to the prefixed path where the domains are unreachable:\n\n```tsx\nonChange={(next) => {\n    const prefixed = i18n.localeForHost(window.location.host) === null; // localhost / preview\n    const target = i18n.switchLocalePath(pathname, next, { prefixed }) + window.location.search;\n    if (prefixed) {\n        router.push(target); // same host: soft navigation\n        router.refresh();\n    } else {\n        window.location.assign(target); // another domain: full page load\n    }\n}}\n```\n\n**No locale cookie.** The host decides the locale, and a cookie set on the domain being left never\nreaches the target domain, so under `\"domain\"` the provider does not write the locale cookie.\n\nA complete Next.js middleware for this mode (rewriting each domain's bare paths into one internal\n`app/[lang]/` tree, with the localhost fallback) is on the docs' URL routing page.\n\n## API reference\n\nEverything hangs off the `I18n` instance.\n\n| Member | Purpose |\n| --- | --- |\n| `new I18n({ locales, default, cookie?, origin?, nonLocalizedPrefixes?, strategy?, frenchSpacing? })` | Create the instance; infers the locale union from `locales`. `strategy` picks the URL scheme (`\"prefix-except-default\"` default, or `\"prefix-all\"`). |\n| `new I18n({ locales, default, strategy: \"domain\", domains, cookie?, nonLocalizedPrefixes?, frenchSpacing? })` | One origin per locale; `domains` maps every locale to its bare origin (or origin + `/<htmlLang>` to mount it on another locale's host). |\n| `.locales` / `.list` / `.default` / `.cookie` / `.origin` / `.domains` / `.strategy` / `.frenchSpacing` | Resolved locale map / ordered array / default locale / cookie name / origin (the default's domain under `\"domain\"`) / domain map (`\"domain\"` only) / strategy / whether French strings get no-break spacing (default `true`). |\n| `.defineTextCatalog(catalog)` | Author a catalog; every entry must cover every locale. |\n| `.defineText(text)` | Author one static or parameterized text with full coverage. |\n| `.uniform(value)` | A text that is the same string in every locale. |\n| `.translator(locale)` | A `Translator` bound to `locale` (French locales get [French spacing](#french-spacing)). |\n| `.translate(text, locale)` | Resolve one static text (no binding). |\n| `.resolveLocale(raw)` / `.matchAcceptLanguage(header)` | Detect the locale from a cookie value / `Accept-Language`. |\n| `.htmlLangFor(locale)` / `.intlLocaleFor(locale)` | BCP-47 `<html lang>` subtag / full `Intl` locale. |\n| `.prefixFor` / `.localizeHref` / `.switchLocalePath` / `.stripLocalePrefix` / `.localeForSegment` / `.isLocalizedPath` / `.hreflangAlternates` | URL routing helpers. The first three take `{ prefixed?: boolean }` (`\"domain\"` only: answer as `\"prefix-except-default\"` on one host). |\n| `.originFor(locale)` / `.localeUrl(href, locale)` | A locale's absolute origin / the absolute URL of a path in a locale. |\n| `.localeForHost(host)` | The locale whose domain serves a host (`\"domain\"`), or null. |\n\n`@daanvandenbergh/i18nkit/react`: `I18nProvider`, `useI18n`, `useLocale`, `useTranslator`, `useSetLocale`,\n`LanguagePicker`, `localeFlag`, `Flag`, `LocaleLink`. `@daanvandenbergh/i18nkit/styles.css`: the picker stylesheet.\n\n## Type-safety notes\n\n- **A missing translation is always a compile error.** This is the guarantee: grow your locale set and every incomplete `defineTextCatalog` / `defineText` fails to compile.\n- **Caveats.** Three things the types don't catch, all inherent to the design: a *typo'd, unknown* locale key (e.g. `de` when your set is `en`/`nl`) is silently accepted inside a `define*` call, because TypeScript's generic constraints don't run excess-property checks; one entry's per-locale builders may have *inconsistent argument signatures* (each arm is typed independently, and the call site is still checked against the intersection); and the React hooks default their type parameter to `string`, so pass your union (`useTranslator<Locale>()`) for the precise type. Missing keys - the case that matters for coverage - are always caught.\n","readmeFilename":"README.md"}