Internationalization (i18n) is a crucial aspect of modern web development. This article explores how to implement a type-safe i18n solution using the typed-locale library in a React application.
Introduction to typed-locale
typed-locale is a lightweight, type-safe internationalization library designed to work with TypeScript. It provides an API for managing translations with type safety for both keys and variables.
Setting Up the Project
Let's create a new React project using Vite with TypeScript:
npm create vite@latest my-i18n-app -- --template react-ts
cd my-i18n-app
npm install
Now, install typed-locale:
npm install typed-locale
Defining Translations
Create a new file called translations.ts
in the src
folder:
// src/translations.ts
import { InferTranslation, plural } from 'typed-locale';
export const en = {
greeting: 'Hello, {{name}}!',
itemCount: plural({
none: 'You have no items.',
one: 'You have one item.',
other: 'You have {{count}} items.',
}),
nav: {
home: 'Home',
about: 'About',
contact: 'Contact',
},
} as const;
export type Translation = InferTranslation<typeof en>;
export const fr: Translation = {
greeting: 'Bonjour, {{name}} !',
itemCount: plural({
none: 'Vous n'avez aucun article.',
one: 'Vous avez un article.',
other: 'Vous avez {{count}} articles.',
}),
nav: {
home: 'Accueil',
about: 'À propos',
contact: 'Contact',
},
};
Creating the Translator
Now, let's create a custom hook to use our translations. Create a new file called useTranslator.ts
:
// src/useTranslator.ts
import { createTranslatorFromDictionary } from "typed-locale";
import { useMemo } from "react";
import { en, fr, Translation } from "./translations";
const dictionary = { en, fr };
export const useTranslator = (locale: keyof typeof dictionary) => {
return useMemo(
() =>
createTranslatorFromDictionary<Translation>({
dictionary,
locale,
defaultLocale: "en",
}),
[locale]
);
};
Using the Translator in Components
Now, let's use our translator in a React component. Update your App.tsx
:
// src/App.tsx
import React, { useState } from "react";
import { useTranslator } from "./useTranslator";
const App: React.FC = () => {
const [locale, setLocale] = useState<"en" | "fr">("en");
const [itemCount, setItemCount] = useState(0);
const translator = useTranslator(locale);
return (
<div>
<select
value={locale}
onChange={(e) => setLocale(e.target.value as "en" | "fr")}
>
<option value="en">English</option>
<option value="fr">Français</option>
</select>
<nav>
<ul>
<li>{translator((t) => t.nav.home)}</li>
<li>{translator((t) => t.nav.about)}</li>
<li>{translator((t) => t.nav.contact)}</li>
</ul>
</nav>
<h1>{translator((t) => t.greeting, { name: "World" })}</h1>
<p>{translator((t) => t.itemCount, { count: itemCount })}</p>
<button onClick={() => setItemCount(itemCount + 1)}>Add Item</button>
<button onClick={() => setItemCount(Math.max(0, itemCount - 1))}>
Remove Item
</button>
</div>
);
};
export default App;
Type Safety Features
typed-locale provides several type safety features:
Autocomplete for translation keys: The IDE provides autocomplete suggestions for all available translation keys.
Type checking for variables: TypeScript catches incorrect variable usage:
// This will cause a TypeScript error
translator((t) => t.greeting, { wrongVariable: "World" });
Nested translations: The type system understands nested translations, allowing
translator(t => t.nav.home)
.Pluralization: The
itemCount
translation demonstrates how typed-locale handles pluralization, automatically selecting the correct plural form based on thecount
value.
Technical Advantages
The type-safe approach using typed-locale offers several technical advantages:
Compile-time error detection: Translation-related errors are caught during compilation rather than at runtime.
Improved developer experience: Autocomplete for translation keys enhances productivity.
Refactoring support: TypeScript's refactoring tools work seamlessly with the translation keys.
Prevents key typos: The callback approach eliminates string typos in translation keys.
Small bundle size: At 1kB, typed-locale has minimal impact on application size.
Framework agnostic: While this example uses React, typed-locale can be used with any JavaScript framework or vanilla JS.
Conclusion
Implementing i18n with a type-safe approach using typed-locale provides a robust solution for managing translations in TypeScript projects. By leveraging the type system, developers can create more reliable internationalized applications while maintaining code quality and productivity.
Top comments (0)