i18n Pattern¶
Translation handling with i18next.
Structure¶
src/i18n/
├── keys.ts # Type-safe translation keys
├── english.ts # English translations
├── czech.ts # Czech translations
└── language.ts # i18next configuration
Translation Keys¶
// keys.ts
export const TranslationKeys = {
shared: {
button: {
save: "shared.button.save",
cancel: "shared.button.cancel",
delete: "shared.button.delete",
},
aria: {
close: "shared.aria.close",
menu: "shared.aria.menu",
},
},
markers: {
title: "markers.title",
created: "markers.created",
deleted: "markers.deleted",
},
} as const;
Translations¶
// english.ts
export const english = {
shared: {
button: {
save: "Save",
cancel: "Cancel",
delete: "Delete",
},
aria: {
close: "Close",
menu: "Open menu",
},
},
markers: {
title: "Markers",
created: "Marker created",
deleted: "Marker deleted",
},
};
Usage¶
const { t } = useTranslation();
// Simple
<Button>{t(TranslationKeys.shared.button.save)}</Button>
// With interpolation
t(TranslationKeys.validation.maxLength, { max: 200 })
// "Maximum length is {{max}} characters" → "Maximum length is 200 characters"
// Aria labels (mandatory for accessibility)
<IconButton aria-label={t(TranslationKeys.shared.aria.close)}>
<CloseIcon />
</IconButton>
Adding New Keys¶
- Add key to
keys.ts - Add translation to
english.ts - Add translation to
czech.ts - Use with
t(TranslationKeys.x.y)
Aria Labels¶
Every interactive element needs an aria-label using i18n:
// Add to keys.ts
shared: {
aria: {
deleteMarker: "shared.aria.deleteMarker",
},
}
// Add to english.ts
aria: {
deleteMarker: "Delete marker",
}
// Use in component
<IconButton aria-label={t(TranslationKeys.shared.aria.deleteMarker)}>
<DeleteIcon />
</IconButton>
Related¶
- Forms — Validation messages