Internationalization
The starter comes with a basic internationalization setup. It uses expo-localization and i18next to provide a simple way to translate your app.
Adding a new language
Mainly the demo app supports two languages: English and Arabic (RTL). You can add more languages by adding the translation files in the src/translations
folder and adding the language code to the src/lib/i18n/resources.ts
file.
import ar from '@/translations/ar.json';import en from '@/translations/en.json';
export const resources = { en: { translation: en, }, ar: { translation: ar, },};
export type Language = keyof typeof resources;
Using translations in your app
The i18n core module provides a set of utility functions to help you use translation in your app.
useTranslation
hook fromreact-i18next
: to get the translation function.
import React from 'react';import { useTranslation } from 'react-i18next';
import { Text } from '@/components/ui';
export const Foo = () => { const { t } = useTranslation(); return ( <> <Text className="text-center">{t('settings.language')}</Text> </> );};
or as Text
component comes with translation support, you can easily use it as follows:
import React from 'react';
import { Text } from '@/components/ui';
export const Foo = () => { return <Text className="text-center" tx="settings.language" />;};
useSetLanguage
hook: to change the current language. This hook returns the selected language and a function to change the language.
Additionally, the useSetLanguage
hook will save the selected language in device storage using MMKV
and will be used as the default language when the app is opened again As well as adding some extra config for RTL languages while updating the selected language.
import type TranslateOptions from 'i18next';import i18n from 'i18next';import memoize from 'lodash.memoize';import { useCallback } from 'react';import { I18nManager, NativeModules, Platform } from 'react-native';import { useMMKVString } from 'react-native-mmkv';import RNRestart from 'react-native-restart';
import { storage } from '../storage';import type { Language, resources } from './resources';import type { RecursiveKeyOf } from './types';
type DefaultLocale = typeof resources.en.translation;export type TxKeyPath = RecursiveKeyOf<DefaultLocale>;
export const LOCAL = 'local';
export const getLanguage = () => storage.getString(LOCAL); // 'Marc' getItem<Language | undefined>(LOCAL);
export const translate = memoize( (key: TxKeyPath, options = undefined) => i18n.t(key, options) as unknown as string, (key: TxKeyPath, options: typeof TranslateOptions) => options ? key + JSON.stringify(options) : key);
export const changeLanguage = (lang: Language) => { i18n.changeLanguage(lang); if (lang === 'ar') { I18nManager.forceRTL(true); } else { I18nManager.forceRTL(false); } if (Platform.OS === 'ios' || Platform.OS === 'android') { if (__DEV__) NativeModules.DevSettings.reload(); else RNRestart.restart(); } else if (Platform.OS === 'web') { window.location.reload(); }};
export const useSelectedLanguage = () => { const [language, setLang] = useMMKVString(LOCAL);
const setLanguage = useCallback( (lang: Language) => { setLang(lang); if (lang !== undefined) changeLanguage(lang as Language); }, [setLang] );
return { language: language as Language, setLanguage };};
Robust translation
Typescript support
To make the translation for your app more robust and easy to maintain, we have added TypeScript support for the translation function. This ensures that you are using the correct translation key and provide auto-completion for the translation keys. Here is an example of the error you will get if you are using the wrong translation key.
Eslint rules
We have also added ESLint rules to ensure that your translation file resources are always up to date. These rules will run as a pre-commit hook whenever you attempt to commit changes to the translation files.
We utilize eslint-plugin-i18n-json alongside a customized validation script for i18next. This script helps sort keys, ensures that all resource files are identical and prevents any missing translations.