{"_id":"@alturacode/lingua","_rev":"2-f423274c7661a199a29ccaabed21a720","name":"@alturacode/lingua","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@alturacode/lingua","version":"0.1.0","keywords":["i18n","translations","internationalization","lazy","fallback","locale"],"license":"MIT","_id":"@alturacode/lingua@0.1.0","maintainers":[{"name":"gerardojbaez","email":"g@gerardobaez.com"}],"dist":{"shasum":"9d6a06e2ccff7a6fd62b96bc886bb306abf42d8f","tarball":"https://registry.npmjs.org/@alturacode/lingua/-/lingua-0.1.0.tgz","fileCount":8,"integrity":"sha512-Ru044/hfbLK+pUdOI/xASXyvhg40PEvj2neAD0z+bvJ4wLPrwiZ2jfDwTIPv3/7hBzzhd+J0Inl2T55JyGe8pA==","signatures":[{"sig":"MEUCIGa/iEkQFxMxsvKka1fywDslxKBSVKf9lLb1gExgXoKFAiEA9HUK35eEktMaUrWdd2cO1arGCNg1Qw5K3Y/uaKgclCM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17725},"main":"./dist/index.cjs","type":"module","_from":"file:alturacode-lingua-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"gerardojbaez","email":"g@gerardobaez.com"},"_resolved":"/private/var/folders/bq/hn_32rl50xz_h42dns80hkjm0000gn/T/541ed683e0de77148aee36db9f9369bf/alturacode-lingua-0.1.0.tgz","_integrity":"sha512-Ru044/hfbLK+pUdOI/xASXyvhg40PEvj2neAD0z+bvJ4wLPrwiZ2jfDwTIPv3/7hBzzhd+J0Inl2T55JyGe8pA==","_npmVersion":"10.9.2","description":"Framework-agnostic i18n with lazy per-locale modules and a typed fallback.","directories":{},"_nodeVersion":"23.8.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^3.0.5","typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/lingua_0.1.0_1785739432544_0.08066750171824744","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@alturacode/lingua","version":"0.1.1","description":"Framework-agnostic i18n with lazy per-locale modules and a typed fallback.","keywords":["i18n","translations","internationalization","lazy","fallback","locale"],"license":"MIT","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"devDependencies":{"tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^3.0.5"},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest"},"_id":"@alturacode/lingua@0.1.1","_integrity":"sha512-4GD6dtQbdCmet7RcPbM0IMJjE8x+KDCkNZ8Draby0pvIX3uuXZOWyyHhT4Qc3K4MFYYX34WQdFCt/m3gGaK4xw==","_resolved":"/private/var/folders/bq/hn_32rl50xz_h42dns80hkjm0000gn/T/73e024ff54ec6530136a07328038d690/alturacode-lingua-0.1.1.tgz","_from":"file:alturacode-lingua-0.1.1.tgz","_nodeVersion":"23.8.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-4GD6dtQbdCmet7RcPbM0IMJjE8x+KDCkNZ8Draby0pvIX3uuXZOWyyHhT4Qc3K4MFYYX34WQdFCt/m3gGaK4xw==","shasum":"a920c7d5bb4d51fa182530d6803bf7e67e8484b6","tarball":"https://registry.npmjs.org/@alturacode/lingua/-/lingua-0.1.1.tgz","fileCount":9,"unpackedSize":22737,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIELzkVyJF/YCcR5VQ0lsi0XPBruEARnIwFpLvmcxy93XAiB2v7i5e4ol4idOj6C/LvVhUtjnwfPpOvjz1dzdolKaTQ=="}]},"_npmUser":{"name":"gerardojbaez","email":"g@gerardobaez.com"},"directories":{},"maintainers":[{"name":"gerardojbaez","email":"g@gerardobaez.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/lingua_0.1.1_1785772987561_0.25258584318000477"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-03T06:43:52.362Z","modified":"2026-08-03T16:03:07.881Z","0.1.0":"2026-08-03T06:43:52.725Z","0.1.1":"2026-08-03T16:03:07.732Z"},"license":"MIT","keywords":["i18n","translations","internationalization","lazy","fallback","locale"],"description":"Framework-agnostic i18n with lazy per-locale modules and a typed fallback.","maintainers":[{"name":"gerardojbaez","email":"g@gerardobaez.com"}],"readme":"# @alturacode/lingua\n\nFramework-agnostic, lazy i18n for TypeScript apps. The core of the\n[Lingua](https://github.com/alturacode/lingua) project — zero runtime\ndependencies.\n\nTranslate a component or feature by shipping a fully-typed `fallback` (your base\nlocale) and lazily `import()`-ing partial overrides for other locales — only\nwhen they are actually requested.\n\n```ts\nimport {\n    createComponentTranslations,\n    loadTranslations,\n} from '@alturacode/lingua';\n\nconst translations = createComponentTranslations(en, {\n    es: () => import('./faq.es'),\n});\n\nconst t = await loadTranslations(translations, locale);\n```\n\n## Install\n\n```bash\npnpm add @alturacode/lingua\n```\n\nFor React, also install [`@alturacode/lingua-react`](https://github.com/alturacode/lingua-react).\n\n## The idea\n\nDefine translations as plain objects. The base locale file exports the complete,\ntyped shape:\n\n```ts\n// faq.en.ts\nexport default {\n    title: 'Frequently asked questions',\n    items: [{ question: 'How does billing work?', answer: 'You pay upfront.' }],\n};\n```\n\nOther locales only override what differs, and are checked against the base:\n\n```ts\n// faq.es.ts\nimport type en from './faq.en';\n\nexport default {\n    title: 'Preguntas frecuentes',\n    items: [\n        {\n            question: '¿Cómo funciona el cobro?',\n            answer: 'Pagas por adelantado.',\n        },\n    ],\n} satisfies typeof en;\n```\n\nBundle them with a loader map at module scope:\n\n```ts\n// faq.ts\nimport { createComponentTranslations } from '@alturacode/lingua';\nimport en from './faq.en';\n\nexport const translations = createComponentTranslations(en, {\n    es: () => import('./faq.es'),\n});\n```\n\n`createComponentTranslations` freezes the bundle, so the same constant can be\nsafely shared across consumers. At request time the fallback is deep-merged with\nthe locale's overrides, so keys a locale doesn't override are inherited from the\nfallback.\n\n## Dynamic translations with functions\n\nLocale files are regular TypeScript, so a translation value can be anything —\nincluding a **function**. No interpolation syntax, no ICU strings, no `sprintf`,\nno escaping workarounds. Interpolated translations are just functions with\nfully typed parameters:\n\n```ts\n// faq.en.ts\nexport default {\n    welcome: (name: string) => `Welcome, ${name}!`,\n};\n```\n\nConsumers call the function directly — the returned type is exactly `string`:\n\n```ts\nconst t = await loadTranslations(translations, locale);\n\nt.welcome('Ada'); // \"Welcome, Ada!\"\n```\n\nLocale overrides are checked against the base signature, so parameters stay\ntyped and consistent across every locale:\n\n```ts\n// faq.es.ts\nexport default {\n    welcome: (name: string) => `¡Bienvenido, ${name}!`,\n} satisfies typeof en;\n```\n\nBecause the value is just JavaScript, you can branch, pluralize, and use the\nfull standard library — including `Intl`:\n\n```ts\nexport default {\n    people: (count: number) => (count === 1 ? '1 person' : `${count} people`),\n    price: (amount: number) =>\n        new Intl.NumberFormat('en', {\n            style: 'currency',\n            currency: 'USD',\n        }).format(amount),\n    publishedAt: (date: Date) =>\n        new Intl.DateTimeFormat('en', { dateStyle: 'long' }).format(date),\n};\n```\n\nEach locale can bring its own implementation (e.g. different plural rules) as\nlong as the signature matches the fallback.\n\n## API\n\n### `createComponentTranslations<T>(fallback: T, loaders: Loaders<T>): Translations<T>`\n\nBundles the fallback with its lazy loaders and freezes the result.\n\n### `loadTranslations<T>(options: TranslationOptions<T>, locale: string): Promise<T>`\n\nResolves the translations for `locale`, deep-merging the fallback with the\nlocale module.\n\nReturns the fallback when there is no loader for the locale or when the module\nfails to load — a missing or broken locale never throws.\n\n### `deepMerge<T>(target: T, source: unknown): T`\n\nRecursively merges `source` into `target`. Plain objects merge key by key;\narrays, scalars, and functions are replaced; `undefined` source values are\nskipped. This is the merge used by `loadTranslations` and is exported for custom\nloaders.\n\n### Types\n\n```ts\ntype Loader<T> = () => Promise<{ default: Partial<T> }>;\ntype Loaders<T> = Record<string, Loader<NoInfer<T>>>;\ninterface TranslationOptions<T> {\n    fallback: T;\n    loaders: Loaders<T>;\n}\ntype Translations<T> = Readonly<TranslationOptions<T>>;\n```\n\n## How merging works\n\n| Fallback key           | Locale override        | Result                               |\n| ---------------------- | ---------------------- | ------------------------------------ |\n| `{ nested: { a: 1 } }` | `{ nested: { b: 2 } }` | `{ nested: { a: 1, b: 2 } }`         |\n| `{ steps: [1] }`       | `{ steps: [2] }`       | `{ steps: [2] }` (array replaced)    |\n| `{ greet: fnA }`       | `{ greet: fnB }`       | `{ greet: fnB }` (function replaced) |\n| `{ title: 'x' }`       | `{ title: undefined }` | `{ title: 'x' }` (undefined skipped) |\n\n## License\n\nMIT — see [LICENSE](../LICENSE).\n","readmeFilename":"README.md"}