如何国际化 React Native Expo App?
How to internationalize a React Native Expo App?
制作 React App International 时推荐使用哪种技术?
我正在考虑编写以下代码:
- 创建 React 上下文“语言”
- 创建不同的模块,导出包含每种语言“pt”、“en”、“fr”、“sp”、“it”的所有字符串的映射...
- 尝试使用语言上下文提供程序提供的方法从 AsyncStorage 加载默认语言到我的启动画面。
- 如果没有找到,根据他的位置获取用户语言。
对我来说这是有道理的,但可能还有其他更简单的方法可以达到同样的目的,感觉更专业。
// This is my Languages Context
import React, { createContext, useState } from "react";
import * as Localization from "expo-localization";
import { VALID_LANGUAGES } from "../../utils/languages/constants"; // "English", "Spanish", "Portuguese"...
import languages from "../../languages"; // en, sp, pt, ... modules with all texts (key, value)
import AsyncStorage from "../../lib/async-storage/AsyncStorage";
const LanguagesContext = createContext(null);
export default LanguagesContext;
export function LanguagesProvider({ children }) {
const [language, setLanguage] = useState(undefined);
const loadLanguage = async () => {
// Get the default language from async storage
const language = await AsyncStorage.getData("language");
// TODO - if undefined -> get user location and use the corresponding lang
// TODO - if not supported, use english
if (!language) {
setLanguage(VALID_LANGUAGES[0]);
}
};
const changeLanguage = async (language) => {
// Update state
setLanguage(language);
// Save language in async storage
await AsyncStorage.storeData("language", language);
};
return (
<LanguagesContext.Provider
value={{ language, loadLanguage, changeLanguage }}
>
{children}
</LanguagesContext.Provider>
);
}
export { LanguagesProvider };
我正在做的是在我的闪屏组件中使用方法“loadLanguage”,它可以确保应用程序在呈现任何内容之前就可以使用。你觉得这个技术怎么样?
如何在应用程序中使用文本? 我想在 Context 提供程序中创建另一个方法 getAppTexts(),只是为了 return 来自我的语言模块(“en”、“it”、“pt”、...)
有库和示例吗?
谢谢。
首先你只需初始化一个新的 react-native 项目
$ npx react-native init rn_example_translation
我想创建一个 src 文件夹来放置我们所有的 JS 代码,所以我们在下面的项目根目录中修改 index.js,如下所示:
import {AppRegistry} from 'react-native';
import App from './src/App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
管理翻译
我们将使用 'i18n-js' 模块翻译应用程序,因此我们安装它:
$ npm install --save i18n-js
然后创建一个文件 'i18n.js' 包含:
import {I18nManager} from 'react-native';
import i18n from 'i18n-js';
import memoize from 'lodash.memoize';
export const DEFAULT_LANGUAGE = 'en';
export const translationGetters = {
// lazy requires (metro bundler does not support symlinks)
en: () => require('./assets/locales/en/translations.json'),
fr: () => require('./assets/locales/fr/translations.json'),
};
export const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key),
);
export const t = translate;
export const setI18nConfig = (codeLang = null) => {
// fallback if no available language fits
const fallback = {languageTag: DEFAULT_LANGUAGE, isRTL: false};
const lang = codeLang ? {languageTag: codeLang, isRTL: false} : null;
const {languageTag, isRTL} = lang ? lang : fallback;
// clear translation cache
translate.cache.clear();
// update layout direction
I18nManager.forceRTL(isRTL);
// set i18n-js config
i18n.translations = {[languageTag]: translationGetters[languageTag]()};
i18n.locale = languageTag;
return languageTag;
};
然后像下面这样创建空的翻译文件:
'./src/assets/locales/en/translations.json' and './src/assets/locales/fr/translations.json'
现在我们可以翻译法语和英语的应用程序 JS 字符串,如下所示:
i18n.t('Hello world!')
在应用程序中切换语言环境
现在我们将设置一个 React 上下文来保持当前的用户语言,并设置一个开关来为用户提供更改语言的选项。翻译字符串很酷,但翻译后的字符串必须与用户语言匹配。
为了在应用程序中保持当前用户语言与反应上下文,我们需要在上下文文件夹中创建一个文件 'LocalisationContext.js':
import React from 'react';
const LocalizationContext = React.createContext();
export default LocalizationContext;
现在在你的 App.js
import React, {useEffect, useCallback} from 'react';
import {StyleSheet} from 'react-native';
import * as i18n from './i18n';
import LocalizationContext from './context/LocalizationContext';
import HomeScreen from './HomeScreen';
const App: () => React$Node = () => {
const [locale, setLocale] = React.useState(i18n.DEFAULT_LANGUAGE);
const localizationContext = React.useMemo(
() => ({
t: (scope, options) => i18n.t(scope, {locale, ...options}),
locale,
setLocale,
}),
[locale],
);
return (
<>
<LocalizationContext.Provider value={localizationContext}>
<HomeScreen localizationChange={handleLocalizationChange} />
</LocalizationContext.Provider>
</>
);
};
并创建 'HomeScreen.js' 文件:
import React, {useContext} from 'react';
import {StyleSheet, SafeAreaView, Text, Button} from 'react-native';
import LocalizationContext from './context/LocalizationContext';
function HomeScreen(props) {
const {localizationChange} = props;
const {t, locale, setLocale} = useContext(LocalizationContext);
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>React-Native example translation</Text>
<Text style={styles.subtitle}>{t('Home screen')}</Text>
<Text style={styles.paragraph}>Locale: {locale}</Text>
{locale === 'en' ? (
<Button title="FR" onPress={() => localizationChange('fr')} />
) : (
<Button title="EN" onPress={() => localizationChange('en')} />
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
title: {
textAlign: 'center',
fontSize: 22,
marginBottom: 40,
},
subtitle: {
textAlign: 'center',
fontSize: 18,
marginBottom: 10,
},
paragraph: {
fontSize: 14,
marginBottom: 10,
},
langButton: {
flex: 1,
},
});
export default HomeScreen;
这里我们可以翻译js中的字符串
处理本地化系统变更
现在我们必须安装本地化模块:
$ npm install --save react-native-localize
然后把app.js修改成这样
import React, {useEffect, useCallback} from 'react';
import {StyleSheet} from 'react-native';
import * as RNLocalize from 'react-native-localize';
import * as i18n from './i18n';
import LocalizationContext from './context/LocalizationContext';
import HomeScreen from './HomeScreen';
const App: () => React$Node = () => {
const [locale, setLocale] = React.useState(i18n.DEFAULT_LANGUAGE);
const localizationContext = React.useMemo(
() => ({
t: (scope, options) => i18n.t(scope, {locale, ...options}),
locale,
setLocale,
}),
[locale],
);
const handleLocalizationChange = useCallback(
(newLocale) => {
const newSetLocale = i18n.setI18nConfig(newLocale);
setLocale(newSetLocale);
},
[locale],
);
useEffect(() => {
handleLocalizationChange();
RNLocalize.addEventListener('change', handleLocalizationChange);
return () => {
RNLocalize.removeEventListener('change', handleLocalizationChange);
};
}, []);
return (
<>
<LocalizationContext.Provider value={localizationContext}>
<HomeScreen localizationChange={handleLocalizationChange} />
</LocalizationContext.Provider>
</>
);
};
然后 'i18n.js' 像这样的文件:
import {I18nManager} from 'react-native';
import * as RNLocalize from 'react-native-localize';
import i18n from 'i18n-js';
import memoize from 'lodash.memoize';
export const DEFAULT_LANGUAGE = 'en';
export const translationGetters = {
// lazy requires (metro bundler does not support symlinks)
en: () => require('./assets/locales/en/translations.json'),
fr: () => require('./assets/locales/fr/translations.json'),
};
export const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key),
);
export const t = translate;
export const setI18nConfig = (codeLang = null) => {
// fallback if no available language fits
const fallback = {languageTag: DEFAULT_LANGUAGE, isRTL: false};
const lang = codeLang ? {languageTag: codeLang, isRTL: false} : null;
# Use RNLocalize to detect the user system language
const {languageTag, isRTL} = lang
? lang
: RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback;
// clear translation cache
translate.cache.clear();
// update layout direction
I18nManager.forceRTL(isRTL);
// set i18n-js config
i18n.translations = {[languageTag]: translationGetters[languageTag]()};
i18n.locale = languageTag;
return languageTag;
};
生成翻译
为了生成语言文件,您可以使用 i18next-scanner。
我们需要全局安装它
npm install -g i18next-scanner
在您的项目根目录创建一个 'i18next-scanner.config.js' 文件:
const fs = require('fs');
const chalk = require('chalk');
module.exports = {
input: [
'src/**/*.{js,jsx}',
// Use ! to filter out files or directories
'!app/**/*.spec.{js,jsx}',
'!app/i18n/**',
'!**/node_modules/**',
],
output: './',
options: {
debug: false,
removeUnusedKeys: true,
func: {
list: ['i18next.t', 'i18n.t', 't'],
extensions: ['.js', '.jsx'],
},
trans: {
component: 'Trans',
i18nKey: 'i18nKey',
defaultsKey: 'defaults',
extensions: [],
fallbackKey: function (ns, value) {
return value;
},
acorn: {
ecmaVersion: 10, // defaults to 10
sourceType: 'module', // defaults to 'module'
// Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options
},
},
lngs: ['en', 'fr'],
ns: ['translations'],
defaultLng: 'en',
defaultNs: 'translations',
defaultValue: '__STRING_NOT_TRANSLATED__',
resource: {
loadPath: 'src/assets/locales/{{lng}}/{{ns}}.json',
savePath: 'src/assets/locales/{{lng}}/{{ns}}.json',
jsonIndent: 2,
lineEnding: '\n',
},
nsSeparator: false, // namespace separator
keySeparator: false, // key separator
interpolation: {
prefix: '{{',
suffix: '}}',
},
},
transform: function customTransform(file, enc, done) {
'use strict';
const parser = this.parser;
const options = {
presets: ['@babel/preset-flow'],
plugins: [
'@babel/plugin-syntax-jsx',
'@babel/plugin-proposal-class-properties',
],
configFile: false,
};
const content = fs.readFileSync(file.path, enc);
let count = 0;
const code = require('@babel/core').transform(content, options);
parser.parseFuncFromString(
code.code,
{list: ['i18next._', 'i18next.__']},
(key, options) => {
parser.set(
key,
Object.assign({}, options, {
nsSeparator: false,
keySeparator: false,
}),
);
++count;
},
);
if (count > 0) {
console.log(
`i18next-scanner: count=${chalk.cyan(count)}, file=${chalk.yellow(
JSON.stringify(file.relative),
)}`,
);
}
done();
},
};
这里我们可以使用命令:
$ i18next-scanner
现在它将生成预填充翻译文件
'./src/assets/locales/en/translations.json' and './src/assets/locales/fr/translations.json'.
我们只需要通过正确的翻译来更改这些文件
现在 运行 应用程序;
npx react-native run-android
它将运行成功。
如果你想切换语言那么这是最重要的解决方案。试试这个。
i18n.locale = "ar";
i18n.reset();
制作 React App International 时推荐使用哪种技术?
我正在考虑编写以下代码:
- 创建 React 上下文“语言”
- 创建不同的模块,导出包含每种语言“pt”、“en”、“fr”、“sp”、“it”的所有字符串的映射...
- 尝试使用语言上下文提供程序提供的方法从 AsyncStorage 加载默认语言到我的启动画面。
- 如果没有找到,根据他的位置获取用户语言。
对我来说这是有道理的,但可能还有其他更简单的方法可以达到同样的目的,感觉更专业。
// This is my Languages Context
import React, { createContext, useState } from "react";
import * as Localization from "expo-localization";
import { VALID_LANGUAGES } from "../../utils/languages/constants"; // "English", "Spanish", "Portuguese"...
import languages from "../../languages"; // en, sp, pt, ... modules with all texts (key, value)
import AsyncStorage from "../../lib/async-storage/AsyncStorage";
const LanguagesContext = createContext(null);
export default LanguagesContext;
export function LanguagesProvider({ children }) {
const [language, setLanguage] = useState(undefined);
const loadLanguage = async () => {
// Get the default language from async storage
const language = await AsyncStorage.getData("language");
// TODO - if undefined -> get user location and use the corresponding lang
// TODO - if not supported, use english
if (!language) {
setLanguage(VALID_LANGUAGES[0]);
}
};
const changeLanguage = async (language) => {
// Update state
setLanguage(language);
// Save language in async storage
await AsyncStorage.storeData("language", language);
};
return (
<LanguagesContext.Provider
value={{ language, loadLanguage, changeLanguage }}
>
{children}
</LanguagesContext.Provider>
);
}
export { LanguagesProvider };
我正在做的是在我的闪屏组件中使用方法“loadLanguage”,它可以确保应用程序在呈现任何内容之前就可以使用。你觉得这个技术怎么样?
如何在应用程序中使用文本? 我想在 Context 提供程序中创建另一个方法 getAppTexts(),只是为了 return 来自我的语言模块(“en”、“it”、“pt”、...)
有库和示例吗?
谢谢。
首先你只需初始化一个新的 react-native 项目
$ npx react-native init rn_example_translation
我想创建一个 src 文件夹来放置我们所有的 JS 代码,所以我们在下面的项目根目录中修改 index.js,如下所示:
import {AppRegistry} from 'react-native';
import App from './src/App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
管理翻译
我们将使用 'i18n-js' 模块翻译应用程序,因此我们安装它:
$ npm install --save i18n-js
然后创建一个文件 'i18n.js' 包含:
import {I18nManager} from 'react-native';
import i18n from 'i18n-js';
import memoize from 'lodash.memoize';
export const DEFAULT_LANGUAGE = 'en';
export const translationGetters = {
// lazy requires (metro bundler does not support symlinks)
en: () => require('./assets/locales/en/translations.json'),
fr: () => require('./assets/locales/fr/translations.json'),
};
export const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key),
);
export const t = translate;
export const setI18nConfig = (codeLang = null) => {
// fallback if no available language fits
const fallback = {languageTag: DEFAULT_LANGUAGE, isRTL: false};
const lang = codeLang ? {languageTag: codeLang, isRTL: false} : null;
const {languageTag, isRTL} = lang ? lang : fallback;
// clear translation cache
translate.cache.clear();
// update layout direction
I18nManager.forceRTL(isRTL);
// set i18n-js config
i18n.translations = {[languageTag]: translationGetters[languageTag]()};
i18n.locale = languageTag;
return languageTag;
};
然后像下面这样创建空的翻译文件:
'./src/assets/locales/en/translations.json' and './src/assets/locales/fr/translations.json'
现在我们可以翻译法语和英语的应用程序 JS 字符串,如下所示:
i18n.t('Hello world!')
在应用程序中切换语言环境
现在我们将设置一个 React 上下文来保持当前的用户语言,并设置一个开关来为用户提供更改语言的选项。翻译字符串很酷,但翻译后的字符串必须与用户语言匹配。
为了在应用程序中保持当前用户语言与反应上下文,我们需要在上下文文件夹中创建一个文件 'LocalisationContext.js':
import React from 'react';
const LocalizationContext = React.createContext();
export default LocalizationContext;
现在在你的 App.js
import React, {useEffect, useCallback} from 'react';
import {StyleSheet} from 'react-native';
import * as i18n from './i18n';
import LocalizationContext from './context/LocalizationContext';
import HomeScreen from './HomeScreen';
const App: () => React$Node = () => {
const [locale, setLocale] = React.useState(i18n.DEFAULT_LANGUAGE);
const localizationContext = React.useMemo(
() => ({
t: (scope, options) => i18n.t(scope, {locale, ...options}),
locale,
setLocale,
}),
[locale],
);
return (
<>
<LocalizationContext.Provider value={localizationContext}>
<HomeScreen localizationChange={handleLocalizationChange} />
</LocalizationContext.Provider>
</>
);
};
并创建 'HomeScreen.js' 文件:
import React, {useContext} from 'react';
import {StyleSheet, SafeAreaView, Text, Button} from 'react-native';
import LocalizationContext from './context/LocalizationContext';
function HomeScreen(props) {
const {localizationChange} = props;
const {t, locale, setLocale} = useContext(LocalizationContext);
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>React-Native example translation</Text>
<Text style={styles.subtitle}>{t('Home screen')}</Text>
<Text style={styles.paragraph}>Locale: {locale}</Text>
{locale === 'en' ? (
<Button title="FR" onPress={() => localizationChange('fr')} />
) : (
<Button title="EN" onPress={() => localizationChange('en')} />
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
title: {
textAlign: 'center',
fontSize: 22,
marginBottom: 40,
},
subtitle: {
textAlign: 'center',
fontSize: 18,
marginBottom: 10,
},
paragraph: {
fontSize: 14,
marginBottom: 10,
},
langButton: {
flex: 1,
},
});
export default HomeScreen;
这里我们可以翻译js中的字符串
处理本地化系统变更
现在我们必须安装本地化模块:
$ npm install --save react-native-localize
然后把app.js修改成这样
import React, {useEffect, useCallback} from 'react';
import {StyleSheet} from 'react-native';
import * as RNLocalize from 'react-native-localize';
import * as i18n from './i18n';
import LocalizationContext from './context/LocalizationContext';
import HomeScreen from './HomeScreen';
const App: () => React$Node = () => {
const [locale, setLocale] = React.useState(i18n.DEFAULT_LANGUAGE);
const localizationContext = React.useMemo(
() => ({
t: (scope, options) => i18n.t(scope, {locale, ...options}),
locale,
setLocale,
}),
[locale],
);
const handleLocalizationChange = useCallback(
(newLocale) => {
const newSetLocale = i18n.setI18nConfig(newLocale);
setLocale(newSetLocale);
},
[locale],
);
useEffect(() => {
handleLocalizationChange();
RNLocalize.addEventListener('change', handleLocalizationChange);
return () => {
RNLocalize.removeEventListener('change', handleLocalizationChange);
};
}, []);
return (
<>
<LocalizationContext.Provider value={localizationContext}>
<HomeScreen localizationChange={handleLocalizationChange} />
</LocalizationContext.Provider>
</>
);
};
然后 'i18n.js' 像这样的文件:
import {I18nManager} from 'react-native';
import * as RNLocalize from 'react-native-localize';
import i18n from 'i18n-js';
import memoize from 'lodash.memoize';
export const DEFAULT_LANGUAGE = 'en';
export const translationGetters = {
// lazy requires (metro bundler does not support symlinks)
en: () => require('./assets/locales/en/translations.json'),
fr: () => require('./assets/locales/fr/translations.json'),
};
export const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key),
);
export const t = translate;
export const setI18nConfig = (codeLang = null) => {
// fallback if no available language fits
const fallback = {languageTag: DEFAULT_LANGUAGE, isRTL: false};
const lang = codeLang ? {languageTag: codeLang, isRTL: false} : null;
# Use RNLocalize to detect the user system language
const {languageTag, isRTL} = lang
? lang
: RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback;
// clear translation cache
translate.cache.clear();
// update layout direction
I18nManager.forceRTL(isRTL);
// set i18n-js config
i18n.translations = {[languageTag]: translationGetters[languageTag]()};
i18n.locale = languageTag;
return languageTag;
};
生成翻译
为了生成语言文件,您可以使用 i18next-scanner。 我们需要全局安装它
npm install -g i18next-scanner
在您的项目根目录创建一个 'i18next-scanner.config.js' 文件:
const fs = require('fs');
const chalk = require('chalk');
module.exports = {
input: [
'src/**/*.{js,jsx}',
// Use ! to filter out files or directories
'!app/**/*.spec.{js,jsx}',
'!app/i18n/**',
'!**/node_modules/**',
],
output: './',
options: {
debug: false,
removeUnusedKeys: true,
func: {
list: ['i18next.t', 'i18n.t', 't'],
extensions: ['.js', '.jsx'],
},
trans: {
component: 'Trans',
i18nKey: 'i18nKey',
defaultsKey: 'defaults',
extensions: [],
fallbackKey: function (ns, value) {
return value;
},
acorn: {
ecmaVersion: 10, // defaults to 10
sourceType: 'module', // defaults to 'module'
// Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options
},
},
lngs: ['en', 'fr'],
ns: ['translations'],
defaultLng: 'en',
defaultNs: 'translations',
defaultValue: '__STRING_NOT_TRANSLATED__',
resource: {
loadPath: 'src/assets/locales/{{lng}}/{{ns}}.json',
savePath: 'src/assets/locales/{{lng}}/{{ns}}.json',
jsonIndent: 2,
lineEnding: '\n',
},
nsSeparator: false, // namespace separator
keySeparator: false, // key separator
interpolation: {
prefix: '{{',
suffix: '}}',
},
},
transform: function customTransform(file, enc, done) {
'use strict';
const parser = this.parser;
const options = {
presets: ['@babel/preset-flow'],
plugins: [
'@babel/plugin-syntax-jsx',
'@babel/plugin-proposal-class-properties',
],
configFile: false,
};
const content = fs.readFileSync(file.path, enc);
let count = 0;
const code = require('@babel/core').transform(content, options);
parser.parseFuncFromString(
code.code,
{list: ['i18next._', 'i18next.__']},
(key, options) => {
parser.set(
key,
Object.assign({}, options, {
nsSeparator: false,
keySeparator: false,
}),
);
++count;
},
);
if (count > 0) {
console.log(
`i18next-scanner: count=${chalk.cyan(count)}, file=${chalk.yellow(
JSON.stringify(file.relative),
)}`,
);
}
done();
},
};
这里我们可以使用命令:
$ i18next-scanner
现在它将生成预填充翻译文件
'./src/assets/locales/en/translations.json' and './src/assets/locales/fr/translations.json'.
我们只需要通过正确的翻译来更改这些文件
现在 运行 应用程序;
npx react-native run-android
它将运行成功。
如果你想切换语言那么这是最重要的解决方案。试试这个。
i18n.locale = "ar";
i18n.reset();