{"_id":"@aminnairi/react-translate","_rev":"5-48cda964cf23153ee1162cf55ec695d3","name":"@aminnairi/react-translate","dist-tags":{"latest":"2.1.0"},"versions":{"0.1.0":{"name":"@aminnairi/react-translate","version":"0.1.0","_id":"@aminnairi/react-translate@0.1.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"dist":{"shasum":"b55671536a1a21f70b9e2da68a20971577c15fa5","tarball":"https://registry.npmjs.org/@aminnairi/react-translate/-/react-translate-0.1.0.tgz","fileCount":2,"integrity":"sha512-tvhFaa4FR3d7WnciD0vJ4d/z6relhL42MD9jr7W3Q/j4M8xjxn9KcKbQ3lGEQ4OICiNWgp0s+ZjfSDs/rjukNQ==","signatures":[{"sig":"MEQCIGtcd5klcEd03EjmOykyI0f6OCDlbd2G1963Iv5hlrlsAiBoyIzMM/V1ROEgxW5MA936jip18/DE6yxLo8jxpyB0FQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":4290},"type":"module","scripts":{"dev":"vite"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"_npmVersion":"10.9.2","description":"Type-safe translation for React","directories":{},"_nodeVersion":"23.3.0","dependencies":{"vite":"^6.0.3","@vitejs/plugin-react":"^4.3.4"},"_hasShrinkwrap":false,"devDependencies":{"@types/react":"^19.0.1","@types/react-dom":"^19.0.2"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/react-translate_0.1.0_1734286088668_0.31166712936274577","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aminnairi/react-translate","version":"0.2.0","_id":"@aminnairi/react-translate@0.2.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"dist":{"shasum":"bb2b2bd72eb60a90c0f1d79bc7ee570a01d7e18c","tarball":"https://registry.npmjs.org/@aminnairi/react-translate/-/react-translate-0.2.0.tgz","fileCount":3,"integrity":"sha512-Nx5oPydpibPy/z/4MaWbWnLU4Ore10KYnRvpQame6Go4Gp3mJsay8ta3tq3XbR55JZ+UJylzNQt5lpNykBpqKg==","signatures":[{"sig":"MEQCIFHqX5N1QZGGmZLoO2ExQ4clSIzdRy0PvneQ6tuBbKOkAiBnJwHniRfBbQCHH35GWZQ4FmomwaYM+hG/IduaW29eEQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":5995},"type":"module","gitHead":"c23330fefa8f6cf4698d47445f8a8e7bdbd7c890","scripts":{"dev":"vite"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"_npmVersion":"10.9.2","description":"Type-safe translation for React","directories":{},"_nodeVersion":"23.3.0","dependencies":{"vite":"6.0.3","@vitejs/plugin-react":"4.3.4"},"_hasShrinkwrap":false,"devDependencies":{"@types/react":"19.0.1","@types/react-dom":"19.0.2"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/react-translate_0.2.0_1734290977052_0.20818034113263528","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@aminnairi/react-translate","version":"1.0.0","license":"MIT","_id":"@aminnairi/react-translate@1.0.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"dist":{"shasum":"4ceee6917f7597a06f15fe0e2ad528a3fb22bf7f","tarball":"https://registry.npmjs.org/@aminnairi/react-translate/-/react-translate-1.0.0.tgz","fileCount":4,"integrity":"sha512-70prQ8AA1rw4tMstWr8ZzDs5g68RZMW3OsCMMVNleKcLl1iNXbVE78yNERNLjOCkwEVnk5Fe8s8vLK/Y85/ndw==","signatures":[{"sig":"MEYCIQD9wP7daRHIfnN++hih1S6anf9CcvhlEB1odItAE3sdHwIhAMJ+c/8Qia9Up6vIt+EgkmnWFJnF/klQ/ecYgfb9cwBH","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":7081},"type":"module","gitHead":"0bdc4a7682895e3c1f647791b8630792aa8287a6","scripts":{"dev":"vite"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"_npmVersion":"10.9.2","description":"Type-safe translation for React","directories":{},"_nodeVersion":"23.3.0","dependencies":{"vite":"6.0.3","@vitejs/plugin-react":"4.3.4"},"_hasShrinkwrap":false,"devDependencies":{"@types/react":"19.0.1","@types/react-dom":"19.0.2"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/react-translate_1.0.0_1734293100355_0.11006892013526093","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@aminnairi/react-translate","version":"2.0.0","keywords":["react","react.js","translate","translation","typescript","type","safe","provider","context","parameter"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/react-translate@2.0.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/react-translate#readme","bugs":{"url":"https://github.com/aminnairi/react-translate/issues"},"dist":{"shasum":"200a5b7313cca83f15da66e249efd739118467cf","tarball":"https://registry.npmjs.org/@aminnairi/react-translate/-/react-translate-2.0.0.tgz","fileCount":4,"integrity":"sha512-iv6ozvP5yVQ0EtVJC9R+5WpJg4CP1W+wC08BD5esDUBrLedMuFVS3Vwxc8zL4rSxr/NvnKxHsh4psmi6Ug8v2w==","signatures":[{"sig":"MEQCIAUPHAqe8VWMuM5mFmToDnyb3MU7SWyXVPnFzBurTenOAiAorjGFy/MTBYrIvhP9V/IxIu7carPvpgrqsRLnh8iZew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14742},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"cff9be1373629d1e26083346e3fc770aa8ceef45","private":false,"scripts":{"build":"npm run types && rolldown --config rolldown.config.ts","types":"tsc"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/react-translate.git","type":"git"},"_npmVersion":"11.10.1","description":"Type-safe translation for React.js","directories":{},"_nodeVersion":"25.7.0","_hasShrinkwrap":false,"devDependencies":{"rolldown":"^1.0.0-rc.7","typescript":"^5.9.3"},"peerDependencies":{"react":">=19","react-dom":">=19"},"_npmOperationalInternal":{"tmp":"tmp/react-translate_2.0.0_1773002273960_0.7179343688946482","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"private":false,"type":"module","name":"@aminnairi/react-translate","version":"2.1.0","description":"Type-safe translation for React.js","homepage":"https://github.com/aminnairi/react-translate#readme","license":"MIT","author":{"name":"Amin NAIRI","url":"https://github.com/aminnairi"},"main":"dist/index.js","types":"dist/index.d.ts","repository":{"url":"git+https://github.com/aminnairi/react-translate.git","type":"git"},"bugs":{"url":"https://github.com/aminnairi/react-translate/issues"},"scripts":{"types":"tsc","build":"npm run types && rolldown --config rolldown.config.ts"},"devDependencies":{"rolldown":"^1.0.0-rc.7","typescript":"^5.9.3"},"peerDependencies":{"react":">=19","react-dom":">=19"},"keywords":["react","react.js","translate","translation","typescript","type","safe","provider","context","parameter"],"gitHead":"753880a66728904f4aff760540cd10907adce83d","_id":"@aminnairi/react-translate@2.1.0","_nodeVersion":"25.7.0","_npmVersion":"11.10.1","dist":{"integrity":"sha512-lFf9qTFoKuIkv8sSFXpWiBflDul3lJnSuwGarT74fahQiCHxvLAbAcp/zxmu2AeVogXAWa+fR6I5nXKU9ixqhw==","shasum":"0a4b0ae656d774aa3a67fbac1e69fdc20361900c","tarball":"https://registry.npmjs.org/@aminnairi/react-translate/-/react-translate-2.1.0.tgz","fileCount":4,"unpackedSize":18825,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCZEMtNrezqh74qgM/3FOY9GHDf+5WWrzbQoJrM3rxOjQIhAO2SiZZ2k7X0Jt9rtJcIm5mnNe9wtg8o9kd0pRP8pthn"}]},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"directories":{},"maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-translate_2.1.0_1773007709382_0.8074687473145268"},"_hasShrinkwrap":false}},"time":{"created":"2024-12-15T18:08:08.557Z","modified":"2026-03-08T22:08:29.643Z","0.1.0":"2024-12-15T18:08:08.825Z","0.2.0":"2024-12-15T19:29:37.314Z","1.0.0":"2024-12-15T20:05:00.545Z","2.0.0":"2026-03-08T20:37:54.105Z","2.1.0":"2026-03-08T22:08:29.520Z"},"bugs":{"url":"https://github.com/aminnairi/react-translate/issues"},"author":{"name":"Amin NAIRI","url":"https://github.com/aminnairi"},"license":"MIT","homepage":"https://github.com/aminnairi/react-translate#readme","keywords":["react","react.js","translate","translation","typescript","type","safe","provider","context","parameter"],"repository":{"url":"git+https://github.com/aminnairi/react-translate.git","type":"git"},"description":"Type-safe translation for React.js","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"readme":"# @aminnairi/react-translate\n\n[![npm version](https://badgen.net/npm/v/@aminnairi/react-translate)](https://www.npmjs.com/package/@aminnairi/react-translate)\n[![types](https://badgen.net/npm/types/@aminnairi/react-translate)](https://www.npmjs.com/package/@aminnairi/react-translate)\n[![license](https://badgen.net/npm/license/@aminnairi/react-translate)](../../LICENSE)\n\nType-safe translation for React\n\n## Features\n\n- **🛡️ 100% Type-Safe**: Say goodbye to missing translations. TypeScript ensures every locale is defined for every translation at compile time.\n- **🏭 Tailored to Your App**: The `createTranslations` factory creates a bespoke Provider and Hooks specifically typed to your app's supported locales, preventing global state pollution.\n- **💬 Dynamic Interpolation**: Easily pass variables to your translations using plain TypeScript functions—no custom parsing or complex string interpolation syntax required.\n- **⚛️ React-Native Integration**: Built on top of React Context and Hooks for a seamless, idiomatic integration with your React applications.\n- **🪶 Zero Dependencies**: Lightweight and built directly on top of React's built-in APIs.\n\n## Prior Art\n\nWhile there are many excellent internationalization libraries in the React ecosystem, `@aminnairi/react-translate` takes a different approach by prioritizing **zero-dependency, native TypeScript type-safety** without the need for code generation or complex runtime string parsing.\n\nHere is how it compares to some of the most popular alternatives:\n\n- **[react-i18next](https://react.i18next.com/) / [i18next](https://www.i18next.com/)**: The industry standard. It is extremely feature-rich (async loading, pluralization, formatting) but comes with a larger bundle size. Achieving full type-safety for translation keys and interpolation arguments usually requires complex configuration or external CLI tools to parse JSON files into TypeScript types. Furthermore, it parses interpolation strings at runtime (e.g., `\"Hello {{name}}\"`) rather than using native TypeScript functions.\n- **[react-intl](https://formatjs.io/docs/getting-started/installation/) (FormatJS)**: A powerful library built on standard `Intl` APIs, excelling at complex date, number, and plural formatting. However, it relies heavily on the ICU MessageFormat syntax, which can be verbose, and like `react-i18next`, strict type-safety is not trivial out of the box without extra tooling.\n- **[typesafe-i18n](https://github.com/ivanhofer/typesafe-i18n)**: Shares the goal of strong type-safety. However, it requires running a background process or generator to compile TypeScript types from its custom format, introducing an additional build step to your workflow. `@aminnairi/react-translate` relies purely on TypeScript's inference.\n- **[next-intl](https://next-intl-docs.vercel.app/)**: An excellent choice if you are using Next.js, especially with the App Router and React Server Components. However, it is deeply coupled with Next.js paradigms and relies on ICU message formats, whereas `@aminnairi/react-translate` is framework-agnostic (works with standard React, React Native, Vite, etc.) and uses plain TypeScript functions.\n\nIf you need advanced features like asynchronous translation loading, complex pluralization rules out of the box, or if your translators prefer working directly with JSON files instead of TypeScript code, libraries like `react-i18next` or `react-intl` might be a better fit.\n\nHowever, if you want a **lightweight, zero-dependency, and instantly type-safe** solution where translations live as plain TypeScript code, `@aminnairi/react-translate` provides a much simpler and strictly typed developer experience.\n\n## Requirements\n\n- [Node](https://nodejs.org)\n- [NPM](https://npmjs.com)\n\n## Installation\n\n```bash\nnpm install @aminnairi/react-translate\n```\n\n## Usage\n\n### 1. Initialize Translations\n\nCreate a `translate.ts` file to set up your locales and generate the components and hooks you will need for your application. We use `createTranslations` to configure the library and extract the functions we need.\n\n```typescript\n// translate.ts\nimport { createTranslations, createLocalStorageAdapter } from \"@aminnairi/react-translate\";\n\nexport const {\n  LocaleProvider,\n  useTranslate,\n  useLocale,\n  defineTranslation\n} = createTranslations({\n  initialLocale: \"en\",\n  locales: [\n    \"en\",\n    \"fr\"\n  ],\n  storage: createLocalStorageAdapter(\"locale\")\n});\n```\n\n### 2. Define Translations\n\nUse `defineTranslation` to define type-safe translations safely outside of your components. This avoids re-creation on every render and ensures strong typing. **If you add a new locale to your configuration later, TypeScript will immediately flag every `defineTranslation` call as an error until you provide the missing translation.**\n\n```typescript\n// translations.ts\nimport { defineTranslation } from \"./translate\";\n\nexport const title = defineTranslation((emails: number) => ({\n  en: `Hello! You have ${emails} unread emails.`,\n  fr: `Bonjour ! Vous avez ${emails} emails non-lus.`\n}));\n```\n\n### 3. Provide the Context\n\nWrap your application in the `LocaleProvider` so that your components can access the current locale.\n\n```tsx\n// main.tsx\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { LocaleProvider } from \"./translate\";\nimport { App } from \"./app\";\n\nconst rootElement = document.getElementById(\"root\");\n\nif (!rootElement) {\n  throw new Error(\"Root element not found.\");\n}\n\nconst root = createRoot(rootElement);\n\nroot.render(\n  <React.StrictMode>\n    <LocaleProvider>\n      <App />\n    </LocaleProvider>\n  </React.StrictMode>\n);\n```\n\n### 4. Use in your Components\n\nUse the `useTranslate` hook to get the actual translated string according to your component's current locale. Use `useLocale` to read or change the current language.\n\n```tsx\n// app.tsx\nimport React, { useCallback } from \"react\";\nimport { useTranslate, useLocale } from \"./translate\";\nimport { title } from \"./translations\";\n\nexport function App() {\n  const { locale, setLocale } = useLocale();\n\n  const onEnglishSwitchButtonClick = useCallback(() => {\n    setLocale(\"en\");\n  }, [setLocale]);\n\n  const onFrenchSwitchButtonClick = useCallback(() => {\n    setLocale(\"fr\");\n  }, [setLocale]);\n\n  // Pass your defined translation function to the useTranslate hook\n  const translateTitle = useTranslate(title);\n\n  return (\n    <div>\n      <p>Current Locale: {locale}</p>\n      \n      <button onClick={onEnglishSwitchButtonClick}>\n        Switch to English\n      </button>\n      <button onClick={onFrenchSwitchButtonClick}>\n        Passer en français\n      </button>\n      \n      {/* Call the resulting function with your arguments */}\n      <h1>{translateTitle(456)}</h1>\n    </div>\n  );\n}\n```\n\n## Example\n\nYou can find a complete, working example in the [apps/example](../../apps/example) directory.\n\n## API\n\n### `createTranslations(options)`\n\nInitializes the translations for your app and returns the provider, hooks, and helpers tailored to your specific locales.\n\n- **Arguments**: \n  - `options.initialLocale`: The locale to use by default on initialization.\n  - `options.locales`: An array of all possible locales in your application.\n  - `options.storage` (Optional): An object with `get` and `set` methods to persist the locale. You can use the built-in `createLocalStorageAdapter(key)` for standard web projects.\n- **Returns**:\n  - `LocaleProvider`: A React component to wrap your application.\n  - `useLocale`: A hook to get and update the current locale.\n  - `defineTranslation`: A helper to define a typed translation function.\n  - `useTranslate`: A hook to apply a defined translation and get a function that returns the translated text for the current locale.\n\n### `LocaleProvider`\n\nA React Context Provider component to be placed at the root of your React tree. It stores the current locale state.\n\n- **Props**:\n  - `children`: The React components to render.\n\n### `useLocale()`\n\nA React Hook that returns the current locale state and a setter function.\n\n- **Returns**:\n  - `locale`: The currently active locale (e.g., `\"en\"` or `\"fr\"`).\n  - `setLocale`: A state setter function to update the current locale.\n\n### `defineTranslation(translate)`\n\nA helper function that returns the same function you pass to it, but enforces type-safety ensuring that all configured locales are present in the returned object. This is particularly useful for maintenance: adding a new locale to your app will trigger a compilation error for every translation that hasn't been updated yet.\n\n- **Arguments**:\n  - `translate`: A function that receives your dynamic inputs and returns an object containing keys for every locale, with the translated strings as values.\n\n### `useTranslate(translate)`\n\nA React Hook that connects your defined translation function to the current locale.\n\n- **Arguments**:\n  - `translate`: A translation function created by `defineTranslation`.\n- **Returns**: A function that accepts the same arguments as your `translate` function but directly returns the final translated string for the currently active locale.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](../../CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.\n\n## Code of Conduct\n\nWe are committed to providing a welcoming and inspiring community for all. Please read our [Code of Conduct](../../CODE_OF_CONDUCT.md).\n\n## Security\n\nPlease report any security vulnerabilities following our [Security Policy](../../SECURITY.md).\n\n## Changelog\n\nSee the [Changelog](../../CHANGELOG.md) for a list of changes.\n\n## License\n\n[MIT](../../LICENSE)\n\n","readmeFilename":"README.md"}