Translations
This document describes how we handle translations in the Registration Backend.
Overview
We use a pragmatic approach where translation keys that are used in the databse are stored in a central translation file in our codebase. The i18n generation tool can then automatically discover and generate translation files, enabling us to serve translated content dynamically without doing manual mapping.
How It Works
We store translation keys in the database. The
translations.server.ts file tells
the i18n generation tool which keys are used in the DB, so it can generate
translation files for them.
1. When translation Keys will be stored in DB, add them to translations file
Add all database translation keys in
app/database/translations.server.ts
so the i18n generation tool knows about them:
export const translations = [
i18n`PERSONALIZATION_ACCOUNT_PURPOSE_TITLE`,
i18n`PERSONALIZATION_ACCOUNT_PURPOSE_SUBTITLE`,
i18n`PERSONALIZATION_ACCOUNT_PURPOSE_LABEL_FACT_CHECK`,
];
This tells the i18n package: "these keys are used in our codebase, please generate translations for them."
2. Generate Translation Files
The generate-i18n script:
- Scans all TypeScript files for valid translation keys
- Finds keys in
translations.server.ts - Updates hash in
app/i18n.ts - Generates JSON files with actual translated text:
public/locales/{hash}/{language}/common.json
3. Serves translated text
At runtime, translates the keys when serving API responses:
import { translate } from "~/translate.server.js";
import { getLanguage } from "~/helpers/get-language.js";
export async function loader({ request }: Route.LoaderArgs) {
const lang = getLanguage(request);
const { t } = await translate(lang);
// Fetch data containing translation keys
const stepData = stepsData["account-purpose"];
// Translate keys to actual text based on user's language
return success({
title: t(stepData.title), // EN: "What do you plan to use..."
subtitle: t(stepData.subtitle), // DE: "Definieren Sie Ihre Ziele..."
});
}
Configuration
Supported languages are configured in app/i18n.ts:
export const config = {
supportedLngs: ["de", "en"],
fallbackLng: "en",
} satisfies I18nConfig;
Why This Approach?
This is the most pragmatic approach for our use case:
- No manual mapping: Translation keys in codebase are automatically discovered by the i18n tool
- Easy to serve: Just fetch from DB and translate keys at runtime
Alternative Approaches
Other approaches we considered:
1. Copy Translations from Monolith
Get all locales/translations from the monolith and store them in our codebase.
2. Use Prefixed Keys with Custom Script
Use a prefix like PERSONALIZATION_DATABASE_XXX for all DB keys and create a
custom script that filters these out and creates translation files on the fly.
3. Store Translated Text in DB
Store actual translated text in the database