OpenSRP Web supports multi-language using react.i18next. This document is divided into 2 sections
- Usage - contains instructions on how people can configure MLS when using packages.
- Contributors - Targets developers with the intent of either adding packages that should support MlS or maintaining packages that already support MLS
Install the i18n and pkg-config packages.
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
// dispatch-configs.ts
import { setAllConfigs } from '@opensrp/pkg-config';
setAllConfigs({
languageCode: 'en',
projectCode: 'core',
});
// 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 <languageCode>-<projectCode>
. 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
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
This section targets developers interested in enhancing, supporting or maintaining MLS-enabled packages.
The @opensrp/i18n
package includes the translation resources as static strings, it also exposes an i18n 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.
To Update the translateable strings:
Check that the strings are wrapped by a the i18next
translator function t
.
Run the string extraction command.
./scripts/i18nExtraction.js extract <package-folder-names...> -l <locales...>
e.g
# 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.
To add a package with localization support, you only need 2 things.
- the new package should have a peerDependency on the
@opensrp/i18n
package - Add a
src/mls.ts
file with the below content.
import { useTranslation as useOrigTranslation, UseTranslationOptions } from '@opensrp/i18n';
export const namespace = '<package-folder-name>';
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.
Add it to @openspr/pkg-config
for proper typing.
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.
# in the repo root directory
./scripts/i18nExtraction.js extract -l <new-locale> -p <projectCode>
This should update the locales files in packages/i18n. Create a pr.
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
export function Component({ i18nNamespace }: { i18nNamespace: string }) {
const { t } = useTranslation(i18nNamespace);
}
//<Component i18nNamespace="fhir-inventory"> - reads locales from the fhir-inventory namespace
//<Component i18nNamespace="fhir-locations"> - reads locales from the fhir-locations namespace
To generate strings to a custom namespace, one can use the --output-namespace
flag e.g
# 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
.