Skip to content

Internationalization

When using React as a frontend framework the use of i18next is mandatory.

In HortiView the fallback language is English. If your extension will only be available in English this will be selected by default. If you plan to localize your extension, for example for the latin-american market, you will have to provide translation files for each language.

Initializing with i18next

In your project create a file such as i18n.ts with the following content:

import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    lng: 'en',
    fallbackLng: 'en',
    debug: true,
    returnObjects: true,
    backend: {
      loadPath: '/locales/{{lng}}/translations.json',
      requestOptions: {
        cache: 'no-cache',
      },
    },
    interpolation: {
      escapeValue: false, // not needed for react as it escapes by default
    },
    returnNull: false,
  });

export default i18n;

What happens is that:

  1. Translations are loaded from the backend (see i18next-http-backend)
  2. User language is detected from the browser (see i18next-browser-languagedetector)
  3. i18next is initialized with a couple of options (see Configuration Options)

The no-cache options works as follows

The browser looks for a matching request in its HTTP cache. If there is a match, fresh or stale, the browser will make a conditional request to the remote server.

If the server indicates that the resource has not changed, it will be returned from the cache. Otherwise the resource will be downloaded from the server and the cache will be updated.

If there is no match, the browser will make a normal request, and will update the cache with the downloaded resource.

Translation files

Within your project translation files should be organized by language:

Structure to localizing extensions
./project
    ./locales
        ./unique_module_name.json    ---> defines the namespace
        ./en/translations.json       ---> contains translations for English
        ./es/translations.json       ---> contains translations for Spanish

As your extension is loaded alongside other extensions, namespacing translations is also mandatory. The easiest way to do that is to create a file that contains a name key and the namespace as its value:

unique_module_name.json
{
    "name": "unique_module_name",
}

Using the translation file and the namespace is straight forward:

i18n.tx
i18next.init({
  ns: ['my_ns', 'unique_module_name'],
  defaultNS: 'unique_module_name'
});
./locales/en/translations.json
{
    "farm": "Farm",
    "create": {
        "field": "Field",
        "block": "Block"
    }
}

Bringing both together would look like this:

Typical component using namespaced keys
i18next.t('name');                          // -> "unique_module_name"
i18next.t('create.field', { ns: 'my_ns' })  // -> "Field"
i18next.t('create.block', { ns: 'my_ns' })  // -> "Block"