# Multi-language Support (MLS) OpenSRP Web supports multi-language using [react.i18next](https://react.i18next.com/). This document is divided into 2 sections 1. Usage - contains instructions on how people can configure MLS when using packages. 2. Contributors - Targets developers with the intent of either adding packages that should support MlS or maintaining packages that already support MLS ## Usage Install the i18n and pkg-config packages. ```sh yarn add @opensrp/i18n @opensrp/pkg-config ``` Pass the required configs to pkg-config at the earliest point possible in your code. This should be before you import any other @opensrp packages ```typescript // dispatch-configs.ts import { setAllConfigs } from '@opensrp/pkg-config'; setAllConfigs({ languageCode: 'en', projectCode: 'core', }); ``` ```typescript // index.js import './dispatch-configs'; import ``` **Configuration** Opensrp-web packages uses 2 configuration options to fully configure MLS i.e. `languageCode` and `projectCode`. These 2 are the subtags that we use to form a languageTag which is formatted as `-`. They are supplied once during build, via the envs `REACT_APP_LANGUAGE_CODE` and `REACT_APP_PROJECT_CODE`. **N/B** The language should be internally available in the package of interest. If you are not sure of this check the [Adding a new language section](#adding-a-new-language) consider where you have a certain string e.g. `Plans` that should translate to `Plans` for a certain clinet instance but display as `Missions` in another instance of the same app. In other words how can we support different translation values for the same language This is where the `projectCode` comes in, helps further define what set of translations for a certain language should be used ## Contributors/Maintainers This section targets developers interested in enhancing, supporting or maintaining MLS-enabled packages. ### MLS architecture The `@opensrp/i18n` package includes the translation resources as static strings, it also exposes an [i18n](github.com/i18next/) instance that is preloaded with the string resources and a context provider that provisions the `i18n` instance to your render tree. The `i18n` package relies on `@opensrp/pkg-config` to get the initial configured language. ### Adding translatable strings To Update the translateable strings: Check that the strings are wrapped by a the `i18next` translator function `t`. Run the string extraction command. ```sh ./scripts/i18nExtraction.js extract -l ``` e.g ```sh # in the repo root directory ./scripts/i18nExtraction.js extract app react-utils -l en sw ``` This should parse the code, get all translatable strings and updated the respective json locale files in `packages/i18n`. From here, you can use your favourite translation tools to get the translations as json files, add them to the right folders in `packages/i18n`, and then shoot us a PR. ### Adding a Localized package To add a package with localization support, you only need 2 things. 1. the new package should have a peerDependency on the `@opensrp/i18n` package 2. Add a `src/mls.ts` file with the below content. ```typescript import { useTranslation as useOrigTranslation, UseTranslationOptions } from '@opensrp/i18n'; export const namespace = ''; export const useTranslation = (ns?: string, options?: UseTranslationOptions) => { return useOrigTranslation(ns ? ns : namespace, options); }; ``` This hook abstraction helps add the namespace scope to the `@opensrp/i18n.useTranslation` , the alternative would require you to pass the namespace to every useTranslation call in a package. ## Adding a new language Add it to `@openspr/pkg-config` for proper typing. ```typescipt export const supportedLanguageCodes = ['en', 'sw', 'fr', 'ar', 'th', 'vi'] as const; export const supportedProjectCode = ['eusm', 'core'] as const; ``` Then run the extraction command while necessary locale or/and the projectCode options. ```sh # in the repo root directory ./scripts/i18nExtraction.js extract -l -p ``` This should update the locales files in packages/i18n. Create a pr. ## De-duping translations using namespaces. Consider a case where you have component A that has translatable strings. You would then like to use this component in 2 different cases within the same app where in each case the strings should be translated differently. One way to handle this situation is to extract the labels into props. One can then provide 2 HOC wrapper components each supplying its own set of translation for the labels. Whilst this is easy enough it does not scale well for huge components that make use of several util functions that also have their own translatable strings. An alternative way and how we do it, is to allow passing a `namespace` string prop to the component. This props instructs the component on where to look up translations per component instance. Here is an example in code ```typescript export function Component({ i18nNamespace }: { i18nNamespace: string }) { const { t } = useTranslation(i18nNamespace); } // - reads locales from the fhir-inventory namespace // - reads locales from the fhir-locations namespace ``` To generate strings to a custom namespace, one can use the `--output-namespace` flag e.g ```sh # in the repo root directory ./scripts/i18nExtraction.js extract fhir-location-management -l en --output-namespace fhir-service-points ``` This would extract strings in the fhir-location-management package to a custom namespace `fhir-service-points` that can then be used as a `namespace prop`.