{"id":13580067,"url":"https://github.com/ivanhofer/typesafe-i18n","last_synced_at":"2026-01-09T23:03:13.566Z","repository":{"id":36972273,"uuid":"324985811","full_name":"ivanhofer/typesafe-i18n","owner":"ivanhofer","description":"A fully type-safe and lightweight internationalization library for all your TypeScript and JavaScript projects.","archived":false,"fork":false,"pushed_at":"2024-10-27T20:18:21.000Z","size":24759,"stargazers_count":2248,"open_issues_count":30,"forks_count":76,"subscribers_count":9,"default_branch":"main","last_synced_at":"2024-10-29T14:50:55.710Z","etag":null,"topics":["angular","angular2","i18n","internationalization","javascript","lightweight","localization","nodejs","react","solid","svelte","typescript","vue"],"latest_commit_sha":null,"homepage":"https://github.com/ivanhofer/typesafe-i18n","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivanhofer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["ivanhofer"]}},"created_at":"2020-12-28T10:50:32.000Z","updated_at":"2024-10-29T07:22:48.000Z","dependencies_parsed_at":"2023-11-11T07:06:06.788Z","dependency_job_id":"f19c44b6-e035-40c4-bb55-5be557786d90","html_url":"https://github.com/ivanhofer/typesafe-i18n","commit_stats":{"total_commits":1494,"total_committers":33,"mean_commits":45.27272727272727,"dds":0.2349397590361446,"last_synced_commit":"318c9042fddf179bde6775bbead9a37fc557ad2a"},"previous_names":[],"tags_count":310,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-i18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-i18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-i18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-i18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivanhofer","download_url":"https://codeload.github.com/ivanhofer/typesafe-i18n/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250477812,"owners_count":21437049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular2","i18n","internationalization","javascript","lightweight","localization","nodejs","react","solid","svelte","typescript","vue"],"created_at":"2024-08-01T15:01:46.792Z","updated_at":"2026-01-09T23:03:13.550Z","avatar_url":"https://github.com/ivanhofer.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ivanhofer","https://img.shields.io/static/v1?label=Sponsor\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026link=https://github.com/sponsors/ivanhofer"],"categories":["TypeScript","**Awesome React** [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)","Table of contents","📦 Libraries","**1. Libraries**"],"sub_categories":["React","Angular","JavaScript / TypeScript","Web"],"readme":"# :earth_africa: typesafe-i18n\n\n**A fully type-safe and lightweight internationalization library for all your TypeScript and JavaScript projects.**\n\n\u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/typesafe-i18n-demo.gif\" width=\"100%\"\u003e\n\n[![npm version](https://badgen.net/npm/v/typesafe-i18n)](https://www.npmjs.com/package/typesafe-i18n)\n![GitHub Top Language](https://img.shields.io/github/languages/top/ivanhofer/typesafe-i18n)\n[![bundle size](https://badgen.net/bundlephobia/minzip/typesafe-i18n)](https://github.com/ivanhofer/typesafe-i18n#sizes)\n![types included](https://badgen.net/npm/types/typesafe-i18n)\n[![bump version \u0026 publish to npm](https://github.com/ivanhofer/typesafe-i18n/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/ivanhofer/typesafe-i18n/actions/workflows/release.yml)\n[![Generic badge](https://img.shields.io/badge/discord-support-slateblue.svg)](https://discord.gg/T27AHfaADK)\n[![Sponsor this project](https://img.shields.io/static/v1?label=Sponsor\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026link=https://github.com/sponsors/ivanhofer)](https://github.com/sponsors/ivanhofer)\n\n\u003cp align=\"center\"\u003e\n   \u003ca href=\"https://github.com/ivanhofer\" title=\"Created by Ivan Hofer (1995-2023)\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/ivan-hofer-rounded.png\" alt=\"Ivan Hofer\" width=\"25%\" /\u003e\n   \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n   \u003ci\u003eCreated by Ivan Hofer (1995-2023)\u003c/i\u003e\n\u003c/p\u003e\n\n## Advantages\n\n:baby_chick: [lightweight](#sizes) (~1kb)\\\n:ok_hand: [easy to use syntax](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#syntax)\\\n:running: [fast and efficient](#performance)\\\n:safety_vest: [prevents you from making mistakes](#typesafety) (also in [plain JavaScript projects](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#jsdoc))\\\n:construction_worker: [creates boilerplate code](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#folder-structure) for you\\\n:speech_balloon: [supports plural rules](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#plural)\\\n:date: allows [formatting of values](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/formatters) e.g. locale-dependent date or number formats\\\n:left_right_arrow: supports [switch-case statements](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#switch-case) e.g. for gender-specific output\\\n:arrow_down: option for [asynchronous loading of locales](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#asynchronous-loading-of-locales)\\\n:books: supports multiple [namespaces](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#namespaces)\\\n:stopwatch: supports SSR (Server-Side Rendering)\\\n:handshake: can be used for [frontend, backend and API](#usage) projects\\\n:mag: [locale-detection](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/detectors) for browser and server environments\\\n:arrows_counterclockwise: [import](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/importer) and [export](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/exporter) translations from/to files or services\\\n:no_entry: no external dependencies\n\n\u003c!-- list of supported emojis on GitHub: https://github.com/ikatyang/emoji-cheat-sheet --\u003e\n\n### Interactive Live Demo\n\nClick [here](https://codesandbox.io/s/typesafe-i18n-demo-qntgqy?file=/index.ts) to see an interactive demo of `typesafe-i18n` showing some key aspects of the type-checking capabilities of this internationalization library.\n\n### Works with\n\n\u003cdiv align=\"center\"\u003e\n   \u003ca title=\"TypeScript\" href=\"#usage\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/typescript.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"Svelte\" href=\"https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-svelte\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/svelte.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"React\" href=\"https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-react\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/react.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"Vue.js\" href=\"https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-vue\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/vuejs.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"Angular\" href=\"https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-angular\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/angular.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"SolidJS\" href=\"https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-solid\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/solidjs.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"Node.js\" href=\"https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-node\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/nodejs.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n   \u003ca title=\"JavaScript\" href=\"#usage\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/icons/javascript.svg\" height=\"70\" hspace=\"10\"\u003e\n   \u003c/a\u003e\n\u003c/div\u003e\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Table of Contents\n\n- [**Get started**](#get-started) - how to add `typesafe-i18n` to your project\n- [**Usage**](#usage) - how to implement different use-cases\n- [**Typesafety**](#typesafety) - how to get the best typesafety features\n- [**Syntax**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#syntax) - how to use the translation functions\n- [**Dictionary**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#dictionary) - how to structure your translations\n- [**Namespaces**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#namespaces) - how to optimize loading of your translations\n- [**Formatters**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/formatters) - how to format dates and numbers\n- [**Switch-Case**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#switch-case) - how to output different words depending on an argument\n- [**Locale-detection**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/detectors) - how to detect an user's locale\n- [**Utility functions**](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/utils) - useful utility functions\n- [**Integrations**](#integration-with-other-services) - how to integrate other i18n services\n- [**Sizes**](#sizes) - how much does `typesafe-i18n` add to your bundle size\n- [**Performance**](#performance) - how efficient is `typesafe-i18n` implemented\n- [**Sponsors**](#sponsors) - how to help this project grow\n- [**FAQs**](#faqs) - how to get your questions answered\n\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Get started\n\n1. :keyboard: Run the setup process and **automatically detect** the config needed\n   ```bash\n   npx typesafe-i18n --setup-auto\n   ```\n   or **manually configure** `typesafe-i18n` by answering a few questions\n   ```bash\n   npx typesafe-i18n --setup\n   ```\n   \u003e It didn't work? See [here](#installing-typesafe-i18n-fails) for possible troubleshooting.\n\n2. :eyes: Take a look at the generated files and it's [folder-structure](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#folder-structure) after running `npm run typesafe-i18n` (or `npx typesafe-i18n`)\n\n3. :open_book: Explore the assets\n   \u003e `typesafe-i18n` offers a lot. Just press `cmd + F` to search on this page or see the [table of contents](#table-of-contents) that will link you to more specific subpages with more details.\n\n4. :star: Star this project on [GitHub](https://github.com/ivanhofer/typesafe-i18n)\n   \u003e Thanks! This helps the project to grow.\n\n\\\n*Having trouble setting up `typesafe-i18n`? Reach out to us via [Github Discussions](https://github.com/ivanhofer/typesafe-i18n/discussions) or on [Discord](https://discord.gg/T27AHfaADK).*\n\n\n### manual installation\n\n```bash\nnpm install typesafe-i18n\n```\n\n### changelog\n\nThe changelog of this project can be found [here](https://github.com/ivanhofer/typesafe-i18n/blob/main/CHANGELOG.md)\n\n#### migrations\n\n - to version `5.x.x`: see the [`release post`](https://github.com/ivanhofer/typesafe-i18n/discussions/227)\n - to version `4.x.x`: see the [`release post`](https://github.com/ivanhofer/typesafe-i18n/discussions/169)\n - to version `3.x.x`: see the [`release post`](https://github.com/ivanhofer/typesafe-i18n/discussions/163)\n\n### Long-term goals\n\nCurious about what comes next? See [this discussion](https://github.com/ivanhofer/typesafe-i18n/discussions/324) to learn more about the plans for the future of this project.\n\n#### Contributions\n\nIf you would like to get involved within this project, take a look at this [discussion](https://github.com/ivanhofer/typesafe-i18n/discussions/323).\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Usage\n\n\u003e The package can be used inside JavaScript and TypeScript applications. You will get a lot of benefits by running the [generator](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generator) since it will create a few wrappers to provide you with full typesafety.\n\nYou can use `typesafe-i18n` in a variety of project-setups:\n\n - [Angular](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-angular) applications\n - [Node.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-node) apis, backends, scripts, ...\n - [React / Next.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-react) applications\n - [Solid.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-solid) applications\n - [Svelte / SvelteKit / Sapper](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-svelte) applications\n - [Vue.js / Nuxt.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-vue) applications\n - [Browser (via CDN)](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/browser/example) projects\n - [other frameworks](#other-frameworks)\n\n### Other frameworks\n\nAll you need is inside the [generated](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#folder-structure) file `i18n-util.ts`. You can use the functions in there to create a small wrapper for your application.\n\n\u003e Feel free to open a new [discussion](https://github.com/ivanhofer/typesafe-i18n/discussions) if you need a guide for a specific framework.\n\n\n### Custom usage\n\nSee [here](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#usage) if you want to learn how you can use `typesafe-i18n` to implement your own specific use-case.\n\n\n### Browser Support\n\nThe library should work in all **modern browsers**. It uses some functionality from the [`Intl` namespace](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl). You can see the list of supported browsers [here](https://caniuse.com/intl-pluralrules). If you want to support older browsers that don't include these functions, you would need to include a polyfill like [intl-pluralrules](https://formatjs.io/docs/polyfills/intl-pluralrules/).\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Typesafety\n\nIf you want to get the best typesafety features, you will need to use the [`generator`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generator) in order to create types and boilerplate code for you\n\nHere you can see some examples where `typesafe-i18n` can help you:\n\n#### typesafe auto-completion for all your defined locales\n![typesafe locales completion](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/01_typesafe-locales-completion.png)\n\n#### typesafe auto-completion for all available translations\n![typesafe translation key completion](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/02_typesafe-key-completion.png)\n\n#### you will get an error if you forget to pass arguments\n![typesafe number of arguments](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/03_typesafe-nr-of-arguments.png)\n\n#### you will get an error if you pass the wrong type arguments\n![typesafe arguments 1](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/04_typesafe-arguments.png)\n![typesafe arguments 2](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/04_typesafe-arguments-2.png)\n\n#### you will get an error if you forgot to add a translation in a locale\n![typesafe keys in translations](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/05_typesafe-keys-in-translations.png)\n\n#### you will get an error when a translation is missing an argument\n![typesafe arguments in translation](https://raw.githubusercontent.com/ivanhofer/typesafe-i18n/main/assets/06_typesafe-arguments-in-translation.png)\n\n\nThe `typesafe-i18n` package allows us to be 100% typesafe for our translation functions and even the translations for other locales itself. The [`generator`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generator) outputs TypeScript definitions based on your base locale.\n\n\u003e You will also benefit from full typesafe JavaScript code via [JSDoc-annotations](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#jsdoc).\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Integration with other services\n\n`typesafe-i18n` comes with an API that allows other services to read and update translations. You can connect other services by using the [`importer`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/importer) and [`exporter`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/exporter) functionality.\n\nThere also exists an official plugin for [Inlang](https://inlang.com/). It allows you to use `typesafe-i18n` together with the tooling Inlang provides. You can find it [here](https://github.com/ivanhofer/inlang-plugin-typesafe-i18n).\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Sizes\n\nThe footprint of the `typesafe-i18n` package is smaller compared to other existing i18n packages. Most of the magic happens in development mode, where the generator creates TypeScript definitions for your translations. This means, you don't have to ship the whole package to your users. The only two parts, that are needed in production are:\n\n- string-parser: detects variables, formatters and plural-rules in your localized strings\n- translation function: injects arguments, formats them and finds the correct plural form for the given arguments\n\nThese parts are bundled into the [core functions](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#usage). The sizes of the core functionalities are:\n\n- [i18nString](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#i18nString): 948 bytes gzipped\n- [i18nObject](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#i18nObject): 1089 bytes gzipped\n- [i18n](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#i18n): 1119 bytes gzipped\n\nApart from that there can be a small overhead depending on which utilities and wrappers you use.\n\nThere also exists a useful wrapper for some frameworks:\n- [`typesafe-i18n` angular-service](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-angular): 1230 bytes gzipped\n- [`typesafe-i18n` react-context](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-react): 1602 bytes gzipped\n- [`typesafe-i18n` solid-context](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-solid): 1403 bytes gzipped\n- [`typesafe-i18n` svelte-store](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-svelte): 1342 bytes gzipped\n- [`typesafe-i18n` vue-plugin](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-vue): 1256 bytes gzipped\n\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Performance\n\nThe package was optimized for performance:\n - **the amount of network traffic is kept small**\\\n   The translation functions are [small](#sizes). Only the locales that are used are [loaded](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#asynchronous-loading-of-locales)\n - **no unnecessary workload**\\\n   Parsing your translation file for variables and formatters will only be performed when you access a translation for the first time. The result of that parsing process will be stored in an optimized object and kept in memory.\n - **fast translations**\\\n\tPassing variables to the [translation function](#usage) will be fast, because its treated like a simple string concatenation. For formatting values, a single function is called per [formatter](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/formatters).\n\nIf you use `typesafe-i18n` you will get a smaller bundle compared to other i18n solutions. But that doesn't mean, we should stop there. There are some possible optimizations planned to [decrease the bundle size even further](https://github.com/ivanhofer/typesafe-i18n/discussions/89).\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## Sponsors\n\n[Become a sponsor :heart:](https://github.com/sponsors/ivanhofer) if you want to support my open source contributions.\n\n\u003cp align=\"center\"\u003e\n   \u003ca href=\"https://cdn.jsdelivr.net/gh/ivanhofer/sponsors/sponsorkit/sponsors.svg\" title=\"ivanhofer's sponsors\"\u003e\n      \u003cimg src=\"https://cdn.jsdelivr.net/gh/ivanhofer/sponsors/sponsorkit/sponsors.svg\" alt=\"ivanhofer's sponsors\" /\u003e\n   \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n   Thanks for sponsoring my open source work!\n\u003c/p\u003e\n\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\u003c!-- ------------------------------------------------------------------------------------------ --\u003e\n\n## FAQs\n\n---\nDou you still have some questions? Reach out to us via [Github Discussions](https://github.com/ivanhofer/typesafe-i18n/discussions) or on [Discord](https://discord.gg/T27AHfaADK).\n\n---\n### Calling `LL.key()` renders an empty string\n\nYou probably forgot to [load the locale](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#loading-locales) first before using it. Calling `loadLocaleAsync('en')` or `loadAllLocales()` will fix it.\n\n---\n### Installing `typesafe-i18n` fails\n\nRunning the `npx` command with a `npm` version `\u003c7.0.0` will probably fail because it [**will not include** `peerDependencies`](https://github.blog/2020-10-13-presenting-v7-0-0-of-the-npm-cli).\n\nYou could try installing it locally via:\n\n```bash\nnpm install typesafe-i18n\n```\n\nand then run  the setup-command from within the `node_modules` folder via:\n\n```bash\n./node_modules/typesafe-i18n/cli/typesafe-i18n.mjs --setup-auto\n```\n\n\u003e here is the original issue with some additional information: [#142](https://github.com/ivanhofer/typesafe-i18n/issues/142)\n\n---\n### I added a new translation to my locale file, but TypeScript gives me the Error `Property 'XYZ' does not exist on type 'TranslationFunctions'`\n\nMake sure to run the [generator](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generator) after you make changes to your base translation file. The generator will [generate and update the types](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#folder-structure) for you.\n\n---\n### I don't use TypeScript, can I also use `typesafe-i18n` inside JavaScript applications?\n\nYes, you can. See the [usage](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#usage) section for instructions. Even if you don't use TypeScript you can still improve from some typesafety features via [JSDoc-annotations](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#jsdoc).\n\n---\n### I added a new translation to my locale file, but the generator will not create new types\n\nThe [generator](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generator) will only look for changes in your base locale file. Make sure to always update your base locale file first, in order to get the correct auto-generated types. If you want to [change your base locale file](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#baselocale), make sure to give it the type of `BaseTranslation`. All other locales should have the type of `Translation`. E.g. if you set your base locale to italian, you would need to do it like this:\n\n - set your base locale to italian (`it`) in ´.typesafe-i18n.json`:\n   ```json\n   {\n      \"baseLocale\": \"it\"\n   }\n   ```\n\n - define the type of your base locale as `BaseTranslation`\n   ```typescript\n   // file 'src/i18n/it/index.ts'\n   import type { BaseTranslation } from '../i18n-types'\n\n   const it: BaseTranslation = {\n      WELCOME: \"Benvenuto!\"\n   }\n\n   export default it\n   ```\n\n - define the type of your other locales as `Translation`\n   ```typescript\n   // file 'src/i18n/en/index.ts'\n   import type { Translation } from '../i18n-types'\n\n   const en: Translation = {\n      WELCOME: \"Welcome!\"\n   }\n\n   export default en\n   ```\n\n---\n### The generator keeps overriding my changes I make to the i18n-files\n\nThe [generator](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generator) creates some helpful wrappers for you. If you want to write your own wrappers, you can disable the generation of these files by setting the [`generateOnlyTypes`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator#generateonlytypes) option to `true`.\n\n---\n### Is `typesafe-i18n` supported by `i18n-ally`?\n\nYes, you can configure `i18n-ally` like [this](https://github.com/lokalise/i18n-ally/issues/678#issuecomment-947338325). There is currently also an open [`PR`](https://github.com/lokalise/i18n-ally/pull/681) that will add official support for `typesafe-i18n`.\n\n---\n### How can I access a translation dynamically?\n\nWhen you want to dynamically access a translation, you can use the usual JavaScript syntax to access a property via a variable (`myObject[myVariable]`).\n\n1. define your translations\n```ts\n// i18n/en.ts\nimport type { BaseTranslation } from '../i18n-types'\n\nconst en: BaseTranslation = {\n   category: {\n      simple: {\n         title: 'Simple title',\n         description: 'I am a description for the \"simple\" category',\n      },\n      advanced: {\n         title: 'Advanced title',\n         description: 'I am a description for the \"advanced\" category',\n      }\n   }\n}\n\nexport default en\n```\n\n2. use it in your components\n\n```html\n\u003cscript lang=\"ts\"\u003e\n   // Component.svelte\n\n   import LL from '$i18n/i18n-svelte'\n   import type { Translation } from '$i18n/i18n-types'\n\n   // ! do not type it as `string`\n   // by restricting the type, you don't loose the typesafety features\n   export let category: keyof Translation['category'] = 'simple'\n\u003c/script\u003e\n\n\u003ch2\u003e{$LL.category[category].title()}\n\n\u003cp\u003e\n   {$LL.category[category].description()}\n\u003cp\u003e\n```\n\n---\n\n### How can I have translated validation messages?\n\nValidation libraries like `zod`, `yup`, `joi` etc. usually provide a way to define custom error messages. You can use `typesafe-i18n` to translate these messages.\n\nBut you need to create the validation schema dynamically, after you have initialized the `LL` object ([]`i18nObject`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#i18nObject)).\n\nYou can do that like this by passing the `LL` object to a function that returns the validation schema:\n\n```ts\nimport { z } from 'zod'\nimport type { TranslationFunctions } from './i18n/i18n-types'\n\nexport const createLoginSchema = (LL: TranslationFunctions) =\u003e z.object({\n    email: z.string().min(1, LL.validation.emptyField()).email(LL.validation.invalidEmail()),\n    password: z.string().min(1, LL.validation.emptyField()),\n})\n```\n\n---\n\n### How do I render a component inside a Translation?\n\nBy default `typesafe-i18n` at this time does not provide such a functionality. Basically you will need to write a function that splits the translated message and renders a component between the parts. You can define your split characters yourself but you would always need to make sure you add them in any translation since `typesafe-i18n` doesn't provide any typesafety for these characters (yet).\n\n - [example for `react`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-react#how-do-i-render-a-component-inside-a-translation)\n - [example for `svelte`](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-svelte#how-do-i-render-a-component-inside-a-translation)\n\n---\n\n### How can I use my base translation as a fallback for other locales?\n\nWith the strong typesafety features, you'll know if a locale is missing a translation. But in rare cases you might want to use your base translation as a fallback for other locales.\n\nSee the [next FAQ entry](#i-have-two-similar-locales-only-a-few-translations-are-different-but-i-dont-want-to-duplicate-my-translations). The same concept can be applied to prefill your translations with the base translation and then just override the parts that are translated.\n\n\u003e You'll loose the some sort of typesafety with that approach since you can't know which parts are translated and which are not. Using the base translation as a fallback is not recommended because your UI will contain two different locales which might confuse your users.\n\n### I have two similar locales (only a few translations are different) but I don't want to duplicate my translations\n\nYour locale translation files can be any kind of JavaScript object. So you can make object-transformations inside your translation file. The only restriction is: in the end it has to contain a default export with type `Translation`. You could do something like this:\n\n - create your `BaseTranslation`\n   ```typescript\n   // file 'src/i18n/en/index.ts'\n   import type { BaseTranslation } from '../i18n-types'\n\n   const en: BaseTranslation = {\n      WELCOME: \"Welcome to XYZ\",\n      // ... some other translations\n\n      COLOR: \"colour\"\n   }\n\n   export default en\n   ```\n\n - create your other translation that overrides specific translations\n   ```typescript\n   // file 'src/i18n/en-US/index.ts'\n   import type { Translation } from '../i18n-types'\n   import en from '../en' // import translations from 'en' locale\n\n   const en_US: Translation = {\n      ...en as Translation, // use destructuring to copy all translations from your 'en' locale\n\n      COLOR: \"color\" // override specific translations\n   }\n\n   export default en_US\n   ```\n\n   \u003e If you are using nested translations, you should use the provided `extendDictionary` function that uses [`just-extend`](https://github.com/angus-c/just#just-extend) under the hood.\n   \u003e ```ts\n   \u003e import { extendDictionary } from '../i18n-utils'\n   \u003e import en from '../en' // import translations from 'en' locale\n   \u003e\n   \u003e const en_US = extendDictionary(en, {\n   \u003e    labels: {\n   \u003e       color: \"color\" // override specific translations\n   \u003e    }\n   \u003e })\n   \u003e\n   \u003e export default en_US\n   \u003e ```\n\n---\n### For certain locales I don't want to output a variable, but due to the strict typing I have to specify it in my translation\n\nThe generated types are really strict. It helps you from making unintentional mistakes. If you want to opt-out for certain translations, you can use the `any` keyword.\n\n - create your `BaseTranslation` with a translation containing a parameter\n   ```typescript\n   // file 'src/i18n/en/index.ts'\n   import type { BaseTranslation } from '../i18n-types'\n\n   const en: BaseTranslation = {\n      HELLO: \"Hi {name}!\",\n   }\n\n   export default en\n   ```\n\n - create another locale without that parameter by disabling the strict type checking with  `as any`\n   ```typescript\n   // file 'src/i18n/de/index.ts'\n   import type { Translation } from '../i18n-types'\n\n   const de: Translation = {\n      HELLO: \"Hallo!\" as any // we don't want to output the 'name' variable\n   }\n\n   export default de\n   ```\n\n\u003e WARNING! the usage of 'any' can introduce unintentional mistakes in future. It should only be used when really necessary and you know what you are doing.\n\nA better approach would be to create a custom formatter e.g.\n\n - create your translation and add a formatter to your variable\n   ```typescript\n   // file 'src/i18n/en/index.ts'\n   import type { BaseTranslation } from '../i18n-types'\n\n   const en: BaseTranslation = {\n      HELLO: \"Hi {name|nameFormatter}!\",\n   }\n\n   export default en\n   ```\n\n   ```typescript\n   // file 'src/i18n/de/index.ts'\n   import type { Translation } from '../i18n-types'\n\n   const de: Translation = {\n      HELLO: \"Hallo {name|nameFormatter}!\"\n   }\n\n   export default de\n   ```\n\n - create the formatter based on the locale\n   ```typescript\n   // file 'src/i18n/formatters.ts'\n   import type { FormattersInitializer } from 'typesafe-i18n'\n   import type { Locales, Formatters } from './i18n-types'\n   import { identity, ignore } from 'typesafe-i18n/formatters'\n\n   export const initFormatters: FormattersInitializer\u003cLocales, Formatters\u003e = (locale: Locales)    =\u003e {\n\n      const nameFormatter =\n         locale === 'de'\n            // return an empty string for locale 'de'\n            ? ignore // same as: () =\u003e ''\n            // return the unmodified parameter\n            : identity // same as: (value) =\u003e value\n\n      const formatters: Formatters = {\n         nameFormatter: nameFormatter\n      }\n\n      return formatters\n   }\n   ```\n\n---\n### Why does the translation function return a type of `LocalizedString` and not the type `string` itself?\n\nWith the help of `LocalizedString` you could enforce texts in your application to be translated. Lets take an Error message as example:\n\n```typescript\nconst showErrorMessage(message: string) =\u003e alert(message)\n\nconst createUser = (name: string, password: string) =\u003e {\n   if (name.length === 0) {\n      showErrorMessage(LL.user.create.nameNotProvided())\n      return\n   }\n\n   if (isStrongPassword(password)) {\n      showErrorMessage('Password is too weak')\n      return\n   }\n\n   // ... create user in DB\n}\n```\n\nIn this example we can pass in any string, so it can also happen that some parts of your application are not translated. To improve your i18n experience a bit we can take advantage of the `LocalizedString` type:\n\n```typescript\nimport type { LocalizedString } from 'typesafe-i18n'\n\nconst showErrorMessage(message: LocalizedString) =\u003e alert(message)\n\nconst createUser = (name: string, password: string) =\u003e {\n   if (name.length === 0) {\n      showErrorMessage(LL.user.create.nameNotProvided())\n      return\n   }\n\n   if (isStrongPassword(password)) {\n      showErrorMessage('Password is too weak') // =\u003e ERROR: Argument of type 'string' is not assignable to parameter of type 'LocalizedString'.\n      return\n   }\n\n   // ... create user in DB\n}\n```\n\nWith the type `LocalizedString` you can restrict your functions to only translated strings.\n\n---\n\n### Tests are not running with `Jest`\n\nUnfortunately there are some open issues in the [`Jest`](https://jestjs.io/) repository regarding modern package export formats so `jest` doesn't know where to load files from.\n\nYou need to manually tell `jest` where these files should be loaded from, by defining [`moduleNameMapper`](https://jestjs.io/docs/configuration#modulenamemapper-objectstring-string--arraystring) inside your `jest.config.js`:\n\n```js\n// jest.config.js\nmodule.exports = {\n   moduleNameMapper: {\n      \"typesafe-i18n/angular\": \"typesafe-i18n/angular/index.cjs\",\n      \"typesafe-i18n/react\": \"typesafe-i18n/react/index.cjs\",\n      \"typesafe-i18n/solid\": \"typesafe-i18n/solid/index.cjs\",\n      \"typesafe-i18n/svelte\": \"typesafe-i18n/svelte/index.cjs\",\n      \"typesafe-i18n/vue\": \"typesafe-i18n/vue/index.cjs\",\n      \"typesafe-i18n/formatters\": \"typesafe-i18n/formatters/index.cjs\",\n      \"typesafe-i18n/detectors\": \"typesafe-i18n/detectors/index.cjs\",\n   }\n};\n```\n\n\u003e here is the original issue with some additional information: [#140](https://github.com/ivanhofer/typesafe-i18n/issues/140)\n\n---\n\n### With Node.JS the `Intl` package does not work with locales other than 'en'\n\nNode.JS, by default, does not come with the full [`intl`](https://nodejs.org/api/intl.html) support. To reduce the size of the node installment it will only include 'en' as locale. You would need to add it yourself. The easiest way is to install the `intl` package\n\n```bash\n\u003e npm install intl\n```\n\nand then add following lines on top of your `src/i18n/formatters.ts` file:\n\n```typescript\nconst intl = require('intl')\nintl.__disableRegExpRestore()\nglobalThis.Intl.DateTimeFormat = intl.DateTimeFormat\n```\n\nThen you should be able to use formatters from the `Intl` namespace with all locales.\n\n\u003e Note: this is an older approach to the problem. You should not need this when using Node.js version \u003e 16.\n\n\u003c!-- TODO: check if this is now fixed in node version 16 --\u003e\n\n---\n\n### \"Cannot find module\" in yarn monorepo setup\n\nYarn uses a strange way to install dependencies in a monorepo setup. The issue lays in the \"hoisting\" of packages (see [this issue](https://github.com/yarnpkg/yarn/issues/7572)). Therefore it might be that the `typesafe-i18n` dependencies cannot be found.\n\nChanging the workspace config in package.json will fix the issue:\n\n```diff\n-  \"workspaces\": [\n-    \"apps/*\",\n-    \"packages/*\"\n-  ],\n+  \"workspaces\": {\n+    \"packages\": [\n+      \"apps/*\",\n+      \"packages/*\"\n+    ],\n+    \"nohoist\": [\n+      \"**/typesafe-i18n\",\n+      \"**/typesafe-i18n/**\"\n+    ]\n+  },\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanhofer%2Ftypesafe-i18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivanhofer%2Ftypesafe-i18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanhofer%2Ftypesafe-i18n/lists"}