i18n
Internationalization (i18n) is, except of a few _en
/_de
columns in the database, completely done in the frontend.
We use react-i18next, which is a powerful framework to provide translation functionality and more.
Language Detection
On the initial page load the language gets detected from the browser settings. English is the fallback language if none of our currently supported languages (English, German) could be detected. This language is then persisted into local storage. The language switcher in the navigation bar also persists the chosen language to local storage. On any further page load this persisted language is used.
Structure
We use a feature-based translation approach.
-
The translations live in the
/src/config/i18n
directory together with the respectivei18next
configuration. -
Each language has its own folder which holds all translations in files that are named like the features under
/src/features
. E.g. for theseeds
feature there is a translation file in/src/config/i18n/seeds.json
-
The feature's translation file should be structured like this:
{
"foo_component": {
"title": "Foo",
...
},
"bar_component": {
"title": "Bar",
...
},
"error_for_feature": "Sorry, ...",
"common_key_inside_the_feature": "A mere commoner",
...
}
As we see in the example:
- We use
snake_case
. - Hierarchy for components/sub-parts is recommended.
- We use
error_
,title_
, etc. prefixes (but you can also use hierarchy instead). - We don't abbreviate (
button
instead ofbtn
).
Furthermore:
- In case a translation does not fit into any feature there is an additional common namespace
common.json
defined. E.g. for general error messages. - Shared components like a button usually also want to render some text.
In this case the text should be translated inside the respective feature where the button is used.
E.g. the create seed button inside the
seeds
feature:
// seeds.json
{
// CreateSeed component
"create_seed": {
"button_create_seed": "Create Seed"
}
}
How To
If you want to translate a string, for example from the seeds
feature in the CreateSeed
component, follow this schema:
- a whole translation key is structured like this
<namespace>:<component>.<part>
function CreateSeed() {
// load the translation namespaces 'seeds' and 'common'
const { t } = useTranslation(["seeds", "common"]);
// this is just an example
const hasError = false;
// wrap in <Suspense> to wait for the data fetching of useTranslation
// use the t function to translate a key of namespace 'seeds' and 'common'
return (
<Suspense>
{hasError ? (
<div>{t("common:unknown_error")}</div>
) : (
<button>{t("seeds:create_seed.btn_create_seed")}</button>
)}
</Suspense>
);
}
Type Safe Keys
The translations are loaded from JSON modules to enable type safety.
For this there is a special file @types/i18next.d.ts
.
Because it is impossible to type based on the chosen language, only the types of the fallback language en
are defined.
Backend/Database
If multi-language entries are in the database or send by an endpoint, we use two fields ending with _en
and _de
.