From c361687103648dff60aa594f57a44d05fa59cfee Mon Sep 17 00:00:00 2001 From: Jakub Grzywacz Date: Mon, 11 Nov 2024 18:18:36 +0100 Subject: [PATCH] feat: introduce createLanguageDetector (#29) Introduce createLanguageDetector to provide greater flexibility with language detection. This change means the current detector (ReactNativeLanguageDetector) will be deprecated in favor of the more adaptable option. --- README.md | 19 ++++++++-- example/App.tsx | 6 ++- src/languageDetector.ts | 84 +++++++++++++++++++++++++++++++++-------- 3 files changed, 89 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 1a14f65..366f9b2 100644 --- a/README.md +++ b/README.md @@ -129,19 +129,32 @@ setLanguage("en-US"); ## i18next This library is fully compatible with [i18next](https://www.i18next.com/). -To use it with i18next, you need to use `ReactNativeLanguageDetector` before init function: +To use it with i18next, create a language detector by using `createLanguageDetector` with the specified `options`: ```ts -import { ReactNativeLanguageDetector } from 'react-native-localization-settings'; +import { createLanguageDetector } from 'react-native-localization-settings'; + +const languageDetector = createLanguageDetector({}); i18next - .use(ReactNativeLanguageDetector) + .use(languageDetector) .use(initReactI18next) .init({ // ... }); ``` +### Options + +```ts +type LanguageDetectorOptions = { + cacheCurrentLanguage?: boolean; // default: false - sets current detected language + async?: boolean; // default: false - uses getLanguageAsync (set to true on old architecture) +}; +``` + +### Changing language + Then, if you want to create custom in-app language selector, you should be able to change the language (along with the settings per-app language) using standard i18next function: diff --git a/example/App.tsx b/example/App.tsx index b6bedde..55b79a0 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -3,13 +3,15 @@ import { StyleSheet, View, Text, Button } from 'react-native'; import i18next from 'i18next'; import { initReactI18next, useTranslation } from 'react-i18next'; import { + createLanguageDetector, getLanguage, getLanguageAsync, - ReactNativeLanguageDetector, } from 'react-native-localization-settings'; +const languageDetector = createLanguageDetector(); + i18next - .use(ReactNativeLanguageDetector) + .use(languageDetector) .use(initReactI18next) .init({ resources: { diff --git a/src/languageDetector.ts b/src/languageDetector.ts index e9ec0ba..6686aef 100644 --- a/src/languageDetector.ts +++ b/src/languageDetector.ts @@ -1,25 +1,79 @@ -import { getLanguage, setLanguage } from './api'; +import { getLanguage, getLanguageAsync, setLanguage } from './api'; + +interface I18nLanguageDetectorModule { + type: 'languageDetector'; + init?(): void; + detect(): string | readonly string[] | undefined; + cacheUserLanguage?(lang: string): void; +} +interface I18nLanguageDetectorAsyncModule { + type: 'languageDetector'; + async: true; + init?(): void; + detect( + callback: (lng: string | readonly string[] | undefined) => void | undefined + ): void | Promise; + cacheUserLanguage?(lng: string): void | Promise; +} + +type LanguageDetectorOptions = { + cacheCurrentLanguage?: boolean; + async?: boolean; +}; + +/** + * @deprecated Use createLanguageDetector instead + */ +export const ReactNativeLanguageDetector: I18nLanguageDetectorModule = { + type: 'languageDetector', + init: () => {}, + detect: () => getLanguage(), + cacheUserLanguage: (lang: string) => setLanguage(lang), +}; /** - * i18next language detector + * I18next language detector generator + * @param options - detector options + * @returns I18nLanguageDetectorModule | I18nLanguageDetectorAsyncModule * @example * Usage: + * const languageDetector = createLanguageDetector(options); * i18next - * .use(ReactNativeLanguageDetector) + * .use(languageDetector) * .init({ * ... * }); */ -export const ReactNativeLanguageDetector: I18nLanguageDetectorModule = { - type: 'languageDetector', - init: () => {}, - detect: () => getLanguage(), - cacheUserLanguage: (lng: string) => setLanguage(lng), -}; +export const createLanguageDetector = ( + options?: LanguageDetectorOptions +): I18nLanguageDetectorModule | I18nLanguageDetectorAsyncModule => { + const { cacheCurrentLanguage = false } = options || {}; + let skipNextCache = false; -interface I18nLanguageDetectorModule { - type: 'languageDetector'; - init?(): void; - detect(): string | readonly string[] | undefined; - cacheUserLanguage?(lng: string): void; -} + let languageDetector: + | I18nLanguageDetectorModule + | I18nLanguageDetectorAsyncModule = { + type: 'languageDetector', + init: () => { + skipNextCache = true; + }, + detect: () => getLanguage(), + cacheUserLanguage: (lang: string) => { + if (cacheCurrentLanguage === false && skipNextCache) { + skipNextCache = false; + return; + } + setLanguage(lang); + }, + }; + if (options?.async) { + languageDetector = { + ...languageDetector, + async: true, + detect: (callback) => { + getLanguageAsync().then(callback); + }, + }; + } + return languageDetector; +};