UNPKG

@rustling-pines/typesafe-locale-generator

Version:

A TypeScript-based tool to generate locale JSON files for i18n frameworks with type safety. It ensures missing translations are caught during development, reducing errors and streamlining localization. Fully customizable input/output paths, compatible wit

304 lines (229 loc) β€’ 9.62 kB
# `TypeSafe Locale Generator` A TypeScript-based tool to generate locale JSON files for i18n frameworks with type safety. It ensures missing translations are caught during development, reducing errors and streamlining localization. Fully customizable input/output paths, compatible with frameworks like React, Angular, and Vue. ## Features - πŸ›‘ **Type Safety:** Validates translation keys and locales at development time to catch issues early. - βœ… **Consistency Across Locales:** Ensures all keys are present in every locale, identifying missing translations during development. - 🌍 **Customizable Paths:** Configure input and output directories easily using environment variables. - 🌐 **Localized JSON Output:** Generates JSON files for each locale, ready to use with libraries like `react-i18next`, `ngx-translate`. - 🧩 **Interpolation Support:** Supports placeholders like `{name}` and `{count}`, enabling dynamic runtime replacements. - πŸš€ **Automation:** Automatically generates locale files during the build step, streamlining the workflow. - ✨ **One-Place Key Updates:** Modify a key once, and updates propagate across all locale files. - 🀝 **Helper for Translation Libraries:** Works as a helper for libraries like `i18n`, adding type safety to eliminate runtime errors. - ⏳ **Lazy Loading Support:** Translations are stored externally, enabling on-demand loading to reduce initial load times. - πŸ“‰ **Reduced Bundle Size:** Excludes translation definitions from the final bundle, keeping your app lightweight. - πŸ“‚ **Framework-Agnostic:** Compatible with React, Angular, Vue, and other TypeScript-based frameworks. ## Installation Install the package as a **dev dependency**: ```bash npm install typesafe-locale-generator --save-dev ``` ## Default Project Structure ### Input Directory > βœ… Feel free to **create as many Message files as you want**. These files **will not be bundled** or sent to the browser. > The **Input Directory**, where translations are defined, is used solely during the build process. ```bash client-app/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ translations/ β”‚ β”‚ β”œβ”€β”€ messages/ β”‚ β”‚ β”‚ β”œβ”€β”€ goodbye.msg.ts β”‚ β”‚ β”‚ β”œβ”€β”€ login.msg.ts β”‚ β”‚ β”‚ └── welcome.msg.ts β”‚ β”‚ β”‚ └── ... β”‚ β”‚ └── index.ts β”œβ”€β”€ package.json └── tsconfig.json ``` ### Output > πŸš€ Auto Generated during build process ```bash client-app/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ i18n/ β”‚ β”‚ β”œβ”€β”€ locales/ β”‚ β”‚ β”‚ β”œβ”€β”€ en-us.json β”‚ β”‚ β”‚ β”œβ”€β”€ fr.json β”‚ β”‚ β”‚ └── de.json β”‚ β”‚ β”‚ └── ... ``` #### Optional: Customize Input and Output Directories via Environment Variables (.env) You can customize the input and output paths for the locale files by setting the following variables in a .env file at the root of your project: *This flexibility allows you to integrate the package into projects with varying directory structures.* ```bash # Specify the location of the input file TRANSLATIONS_INPUT_FILE=src/translations/index.ts # Specify the output directory for the generated locale files LOCALES_OUTPUT_DIRECTORY=src/i18n/locales ``` > By default, the package will use the following locations if these variables are not set: β€’ *Input File*: **src/translations/index.ts** β€’ *Output Directory*: **src/i18n/locales** ## Usage ### Step 1: Add *generate-locales* Command to Your Build Script in package.json ```bash "scripts": { "build": "generate-locales && react-scripts build", } ``` ### Step 2: Define Translation Keys and Locales > You can define translations for each message in a dedicated folder for cleaner organization, or directly in the index.ts file for smaller projects. #### Approach 1: Define Translations Directly You can directly provide the translations in the `index.ts` file: **File: `src/translations/index.ts`** ```typescript import { ITranslations } from "@rustling-pines/typesafe-locale-generator"; // 1. Define the locales for your application (JSON files will be generated for each locale). export const locales = ['en-us', 'fr', 'de', 'es', 'jp'] as const; // 2. Derive Type from the locales array export type Locales = typeof locales[number]; // 3. Use the derived Locales type to enforce type safety in translations. export const translations: ITranslations<Locales>[] = [ { key: 'WELCOME_MESSAGE', 'en-us': 'Welcome, {name}!', fr: 'Bienvenue, {name}!', de: 'Willkommen, {name}!', in: 'ΰ€Έΰ₯ΰ€΅ΰ€Ύΰ€—ΰ€€ ΰ€Ήΰ₯ˆ, {name}!', jp: '{name} γ•γ‚“γ€γ‚ˆγ†γ“γοΌ', }, { key: 'LOGIN', 'en-us': 'Login', fr: 'Connexion', de: 'Anmelden', es: 'Iniciar sesiΓ³n', jp: 'ログむン', }, // ... ]; ``` #### **Approach 2: Define Translations in Separate Files** For better organization, you can keep translations in a `messages` folder (**or any folder of your choice**) and import them into `index.ts`: **This approach is especially useful for large projects with extensive translations.** **File: `src/translations/index.ts`** ```typescript import { ITranslations } from "@rustling-pines/typesafe-locale-generator"; import { WelcomeMessage } from "./messages/welcome.msg"; import { GoodbyeMessage } from "./messages/goodbye.msg"; import { LoginMessage } from "./messages/login.msg"; // 1. Define the locales for your application (JSON files will be generated for each locale). export const locales = ['en-us', 'fr', 'de', 'es', 'jp'] as const; // 2. Derive Type from the locales array export type Locales = typeof locales[number]; // 3. Use the derived Locales type to enforce type safety in translations. export const translations: ITranslations<Locales>[] = [ WelcomeMessage, GoodbyeMessage, LoginMessage, ]; ``` **File: `src/translations/messages/wleome.msg.ts`** ```typescript import { ITranslations } from "@rustling-pines/typesafe-locale-generator"; import { Locales } from ".."; // {name} - placeholder export const WelcomeMessage: ITranslations<Locales> = { key: 'WELCOME_MESSAGE', 'en-us': 'Welcome, {name}!', fr: 'Bienvenue, {name}!', de: 'Willkommen, {name}!', in: 'ΰ€Έΰ₯ΰ€΅ΰ€Ύΰ€—ΰ€€ ΰ€Ήΰ₯ˆ, {name}!', jp: '{name} γ•γ‚“γ€γ‚ˆγ†γ“γοΌ', }; ``` **File: `src/translations/messages/goodbye.msg.ts`** ```typescript import { ITranslations } from "@rustling-pines/typesafe-locale-generator"; import { Locales } from ".."; export const GoodbyeMessage: ITranslations<Locales> = { key: 'GOODBYE', 'en-us': 'Goodbye', fr: 'Au revoir', de: 'Auf Wiedersehen', es: 'AdiΓ³s', jp: 'γ•γ‚ˆγ†γͺら', }; ``` **File: `src/translations/messages/login.msg.ts`** ```typescript import { ITranslations } from "@rustling-pines/typesafe-locale-generator"; import { Locales } from ".."; export const LoginMessage: ITranslations<Locales> = { key: 'LOGIN', 'en-us': 'Login', fr: 'Connexion', de: 'Anmelden', es: 'Iniciar sesiΓ³n', jp: 'ログむン', }; ``` ### Example of a Missing Locale Error If a locale is missing for a translation, TypeScript will throw a compile-time error. For instance, removing `jp` from the `LoginMessage` in `login.msg.ts`: ```typescript export const LoginMessage: ITranslations<Locales> = { key: 'LOGIN', 'en-us': 'Login', fr: 'Connexion', de: 'Anmelden', es: 'Iniciar sesiΓ³n', // jp: 'ログむン', // ❌ Missing 'jp' }; ``` **Error Message:** ```typescript Type '{ key: "LOGIN"; "en-us": string; fr: string; de: string; es: string; }' is missing the following properties from type 'ITranslations<Locales>': jp ``` ### ⚠ Important This package is designed to generate JSON files from your translation definitions for **lazy loading** at runtime. The generated JSON files will not be bundled with your application, ensuring a smaller bundle size and efficient loading of translation files only when needed. ### ❗ Warning **Do not reference** the translations folder or its files directly in your project outside of the **src/translations/index.ts** Including these files elsewhere will cause them to be bundled into the application, defeating the purpose of lazy loading and increasing the bundle size. ### Example of Generated Output Files > Below are examples of the JSON files generated for each locale. These files are structured for use with translation libraries like i18n. #### **File: `public/i18n/locales/en-us.json`** ```json { "WELCOME": "Welcome {name}", "LOGIN": "Login", "GOODBYE": "Goodbye" } ``` #### **File: `public/i18n/locales/fr.json`** ```json { "WELCOME": "Bienvenue {name}", "LOGIN": "Connexion", "GOODBYE": "Au revoir" } ``` #### **File: `public/i18n/locales/de.json`** ```json { "WELCOME": "Willkommen {name}", "LOGIN": "Anmelden", "GOODBYE": "Auf Wiedersehen" } ``` #### **File: `public/i18n/locales/es.json`** ```json { "WELCOME": "Bienvenido {name}", "LOGIN": "Iniciar sesiΓ³n", "GOODBYE": "AdiΓ³s" } ``` #### **File: `public/i18n/locales/jp.json`** ```json { "WELCOME": "{name} γ•γ‚“γ€γ‚ˆγ†γ“γοΌ", "LOGIN": "ログむン", "GOODBYE": "γ•γ‚ˆγ†γͺら" } ``` --- ## Requirements - **Node.js**: Version >=14.x - **TypeScript**: Version >=4.1 ## Contributing Contributions are welcome! If you’d like to report a bug or suggest a feature, feel free to open an issue or submit a pull request. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.