Localization
Overview
For localization, we use react-i18next. To add a new locale, create a new folder in src/assets/locales within the root directory of the client application. All files created inside src/assets/locales/**/*.[locale].yaml will be automatically converted to JSON, added to public/locales/[locale]/translation.json, and loaded by i18next depending on the locale set for the user.
There is no need to modify configuration files or specify newly added files. When the application starts in watch mode, a watcher also starts, which automatically recompiles the final translation.json if any changes are detected.
Common Translations Plugin
Another innovation is the use of $common translations. We aim to declare keys for every single string in our application, allowing us to easily override the value for any string displayed in the system without modifying the code. However, this can sometimes be excessive and result in overly large localization files.
To address this issue, we added the common-translations-plugin to i18next. This plugin allows defining "common translations" for a specific subsection or the entire application. However, we recommend being cautious with defining translations for the entire application and limiting them to direct translations, such as "dateOfBirth: Date of Birth," and avoiding indirect translations, like "email: Primary Email."
To define a "common translation," you need to add a $common section at any level, for example:
$common:
name: Name
users:
$common:
name: User's name
company:
name: Company name
demographics:
name: User's full name
In this case (with respect of priorities):
- For the key "users.demographics.name," "User's full name" will be used.
- For all areas starting with "users" and ending with "company.name," "Company name" will be used.
- For all areas starting with "users" and ending with "name," "User's name" will be used.
- For all areas, any key ending in "name" will use "Name".
The search algorithm and priorities can best be illustrated with a diagram based on the example search for "users.details.name":
`users.details.name` exists?
├── YES: return `users.details.name`
|
└── NO: `users.details.$common` exists?
├── YES: users.details.$common.name exists?
│ ├── YES: return `users.details.$common.name`
│ └── NO: >> GOTO NO
|
└── NO: `users.$common` exists?
├── YES: `users.$common.details.name` exists?
│ ├── YES: return `users.$common.details.name`
│ └── NO: `users.$common.name exists?
│ ├── YES: return `users.$common.name`
│ └── NO: GOTO NO
|
└── NO: `$common` exists?
├── YES: `$common.users.details.name` exists?
│ ├── YES: return `$common.users.details.name`
│ └── NO: `$common.details.name` exists?
│ ├── YES: return `$common.details.name`
│ └── NO: `$common.name` exists?
│ ├── YES: return `$common.name`
│ └── NO: >> GOTO NO
|
└── NO: return key