i18n
Internationalization is a key feature in HP TV+, where we handle two main concepts:
- Dictionary translations for each possible language.
- App Directionallity (ltr and rtl).
The i18n is in charge of this feature, but we also have some extra info and utilities that worth mention.
Architecture
Besides the Control setup for the dictionary as explained in the i18n Service documentation HP TV+ handle the i18n in the following order:
- Initiallise the i18n/localization based on the segment and selected language at application startup
- Store the status (while loading and once loaded), including the locales and directionallity value within a React Context
- Defines a global utility to get the locales from the i18n Service to be used inside the Context but also in the DTOs inside the
appInitilization - Defined a global utility to do interpolation of any string using the
{{key}}interpolation string
Dictionary Key usage inside Components
Server Components/DTO/Utils
import { getI18n } from '@/utils/boot/appInitialization';
const i18n = await getI18n();
const loginKey = i18n.login;
Client Components
Alternative 1: single key
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
return <div>{i18nContext.locales.watchNow}</div>;
Alternative 2: single key
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
const { getLocale } = i18nContext;
const watchNowText = useMemo(() => getLocale('watchNow'), [getLocale]);
return <div>{watchNowText}</div>;
Alternative 3: multiple keys
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
const { getLocales } = i18nContext;
const [key1, key2, key3, key4, key5, key6] = useMemo(
() => getLocales(['key1', 'key2', 'key3', 'key4', 'key5', 'key6']),
[getLocales],
);
return <div>{key2}</div>;
Alternative 4: multiple keys as an object
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
const { getLocalesObj } = i18nContext;
const { key1, key2, key3, key4, key5, key6 } = useMemo(
() => getLocalesObj(['key1', 'key2', 'key3', 'key4', 'key5', 'key6']),
[getLocales],
);
return <div>{key2}</div>;
Interpolation
Interpolation works by passing a context of the items that contains the template within it.
eg. Given the string S{{season}}E{{episode}}: {{title}} and the data model
{
...
id: "uuid",
season: 3,
episode: 5,
title: "Episode Title",
}
The result would be S3E5: Episode Title.
String interpolation is available through any of the functions exposed. This is done by passing a tuple instead of a string, where the first element is the key and the second element is the context to be used.
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
const { getLocale, getLocales, getLocalesObj } = i18nContext;
const movie = {
season: 3,
episode: 5,
title: 'Episode title',
};
// Given key2: 'S{{season}}E{{episode}}: {{title}}'
// returns 'S3E5: Episode Title'
const key2 = useMemo(() => getLocale(['key2', movie]), [getLocales]);
// returns 'S3E5: Episode Title'
const [key1, key2] = useMemo(
() => getLocales(['key1', ['key2', movie]]),
[getLocales],
);
// returns 'S3E5: Episode Title'
const { key1, key2 } = useMemo(
() => getLocalesObj(['key1', ['key2', movie]]),
[getLocales],
);
The function that handles interpolation for all of the above, transform, is also available and can be used directly
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
const { transform } = i18nContext;
const movie = {
season: 3,
episode: 5,
title: 'Episode title',
};
// Given key2: 'S{{season}}E{{episode}}: {{title}}'
// returns 'S3E5: Episode Title'
const title = transform('key_2', movie);
transform’s first argument should be a dictionary key, and will attempt to retrieve it based on the current set of locales. If the key is not found on the current dictionary set, it then assumes that the string is the value to be interpolated. This allows transform to work directly with the template value.
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';
const i18nContext = useContext(I18nContext);
const { useLocale transform } = i18nContext;
const movie = {
season: 3,
episode: 5,
title: 'Episode title',
};
// returns 'S{{season}}E{{episode}}: {{title}}'
const key2 = useMemo(
() => getLocale('key2'),
[getLocales],
);
// returns 'S3E5: Episode Title'
const title = transform(key2, movie);
Dictionary Keys
In order to handle the existing dictionary keys for the data fetch, inside appInitilization there's a method called getI18n which will return all the existing keys based on a defined dictionary inside src/utils/boot/dictionary.ts.
That list is used for both initiallization and data type validation, if you need to use any extra keys, you need to add to that list before using it.