本地化

编辑页面

了解如何在 Expo 项目中使用 expo-localization 开始并配置本地化。


For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

如果你希望你的应用对使用不同语言或来自不同文化背景的用户来说更易用,就应该对其进行本地化。对应用进行本地化会使其适配用户设备的地区设置。应用会显示用户熟悉并能够理解的翻译和货币。数字、列表等内容也会按照用户习惯的方式进行格式化。

本指南使用 expo-localization 库来获取用户语言设置并添加对多种语言的支持。它以 i18n-js 作为添加多语言支持的示例。

获取用户的语言

使用 expo-localization 库获取用户当前语言。通过运行以下命令安装该包:

Terminal
npx expo install expo-localization

然后,你就可以在应用中访问本地化方法和数据:

import { getLocales } from 'expo-localization'; const deviceLanguage = getLocales()[0].languageCode;

getLocales 方法会根据设备的系统设置返回当前地区设置。在较新的 Android 和 iOS 版本中,应用语言可以按应用单独设置,因此你通常不需要构建自定义 UI 来允许用户在应用内更改当前地区设置。

有时,为了让用户能够按应用单独设置其他本地化偏好,构建一个 UI 是有意义的。通常来说,你应该允许用户更改以下内容:

  • 如果你的应用对本地化单位有中等以上程度的使用,应允许更改本地化单位(例如公制/英制、货币、温度等)
  • 如果你支持的平台没有获取默认值的 API,则应允许更改其他偏好设置(有关详情,请查看 expo-localization API 文档)

通过系统设置启用按应用语言选择

Android 和 iOS 都允许用户通过系统设置为单个应用选择首选语言。要支持此功能,你的应用必须向系统声明其支持的地区设置。

为此,请使用 expo-localization 配置插件,并将 supportedLocales 属性传递给 expo-localization 配置插件。 你可以直接提供一个支持地区设置的数组,也可以使用 supportedLocales.iossupportedLocales.android 字段来指定平台相关的值:

app.json
{ "expo": { "plugins": [ [ "expo-localization", { "supportedLocales": { "ios": ["en", "ja"], "android": ["en", "ja"] } } ] ] } }

在 Android 上,请参考 地区名称命名指南最常用地区设置列表

在 iOS 上,请使用语言名称或 ISO 语言标识符。

翻译应用

创建和管理翻译很快就会变成一项大工程。你可以手动处理翻译,但最好使用库来帮你完成这项工作。

让我们让应用支持英语和日语。为此,本指南使用 i18n-js 包:

Terminal
npx expo install i18n-js

查看其他翻译库,找到最适合你需求的库。

然后,为你的应用配置语言:

import { getLocales } from 'expo-localization'; import { I18n } from 'i18n-js'; // 为你想支持的不同语言设置键值对。 const i18n = new I18n({ en: { welcome: 'Hello' }, ja: { welcome: 'こんにちは' }, }); // 在应用开始时只设置一次地区。 i18n.locale = getLocales().at(0)?.languageCode ?? 'en'; // 你也可以写成 getLocales()[0].languageCode ?? 'en' console.log(i18n.t('welcome'));

现在,你可以在整个应用中使用 i18n.t 函数来翻译字符串。

对于某些内容,例如姓名,你可以不对其进行本地化。在这种情况下,你可以在默认语言中将它们 只定义一次,并通过 i18n.enableFallback = true; 复用它们。

在 Android 上,当用户更改设备语言时,应用不会重启。你可以使用 AppState API 监听应用状态变化,并在应用状态每次变化时调用 getLocales() 函数。

在 iOS 上,当用户更改设备语言时,应用会重启。这意味着你可以只设置一次语言,而无需更新任何 React 组件来响应语言变化。

完整示例

本地化
import { View, StyleSheet, Text } from 'react-native'; import { getLocales } from 'expo-localization'; import { I18n } from 'i18n-js'; // 为你想支持的不同语言设置键值对。 const translations = { en: { welcome: 'Hello', name: 'Charlie' }, ja: { welcome: 'こんにちは' }, }; const i18n = new I18n(translations); // 在应用开始时只设置一次地区。 i18n.locale = getLocales()[0].languageCode ?? 'en'; // 当某种语言缺少某个值时,会回退到另一个包含该键的语言。 i18n.enableFallback = true; // 如需查看回退机制,请取消下面一行的注释,强制应用使用日语。 // i18n.locale = 'ja'; export default function App() { return ( <View style={styles.container}> <Text style={styles.text}> {i18n.t('welcome')} {i18n.t('name')} </Text> <Text>当前地区:{i18n.locale}</Text> <Text>设备地区:{getLocales()[0].languageCode}</Text> </View> ); } const styles = StyleSheet.create({ container: { backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', flex: 1, }, text: { fontSize: 20, marginBottom: 16, }, });

其他翻译库

本指南以 i18n-js 作为示例,但其他库也可以帮助你完成这项任务。创建翻译是一项重大工作。在选择库时,可以考虑以下几点:

  • 与翻译管理工具集成,以便更轻松地管理字符串和进行自动化处理。
  • 能够为字符串提供上下文,这样 AI 翻译工具和/或人工审校者就能理解字符串的上下文并提供更好的翻译。
  • 开发体验——在 React / JSX 环境中的使用方式。有些库附带 ESLint 插件和其他工具来辅助开发。
  • 不必担心日期或数字的本地化——请使用标准化的 Intl API 来处理这些内容。

下面是不完全列举的其他可考虑库:

  • Lingui 是一个成熟的库,原生支持 React(包括 React Server Components (RSC)),并且与翻译管理工具集成良好。

  • fbtee 是一个面向 JavaScript 和 React 的国际化框架,设计目标是强大、灵活且直观。

  • React i18next 是一个稳定、维护良好的库,基于 i18next

  • Intlayer 是一个按组件划分的 i18n 库,带有提取器和 AI 工具,重点关注包体积和性能。

翻译应用元数据

如果你计划将应用发布到不同国家或地区,或者希望它支持多种语言,你可以为显示名称和系统对话框等内容提供本地化字符串。这可以很容易地在应用配置文件中设置。首先,将 ios.infoPlist.CFBundleAllowMixedLocalizations 设为 true,然后为 locales 提供文件路径列表。

app.json
{ "expo": { "ios": { "infoPlist": { "CFBundleAllowMixedLocalizations": true } }, "locales": { "ja": "./languages/japanese.json" } } }

传递给 locales 的键应该是语言标识符,由你想要的语言的2 字母语言代码组成,并可选带有地区代码(例如 en-USen-GB),其值应指向一个类似下面这样的 JSON 文件:

japanese.json
{ "ios": { "CFBundleDisplayName": "こんにちは", "NSContactsUsageDescription": "日本語のこれらの言葉", "Localizable.strings": { "HELLO_NOTIFICATION_KEY": "こんにちは世界" } }, "android": { "app_name": "こんにちは", "HELLO_NOTIFICATION_KEY": "こんにちは世界" } }

现在,当应用安装在语言设置为日语的设备上时,其显示名称会被设置为 こんにちは

在 SDK 55 及更高版本中,iOS 还提供一个选项,可以指定 Localizable.strings 对象,其条目会被用来创建原生本地化文件。这些条目可用于 iOS 本地化通知

启用 RTL 支持

世界上有些地区的文本是从右向左书写的。如果你希望对应用进行本地化,使其在 RTL 语言中外观符合预期,你需要确保应用相应地处理这些布局和文本方向变化。

要启用 RTL 支持,请使用 expo-localization 配置插件,并在应用配置中启用 extra.supportsRTL 属性:

app.json
{ "expo": { "extra": { "supportsRTL": true }, "plugins": ["expo-localization"] } }

这样会在应用加载于 Expo Go、Expo dev Client,以及使用 EAS Build 或 npx expo prebuild 构建的应用中启用 RTL。

当应用启动时,Expo 会检查当前设备地区设置是否应以 RTL 布局渲染,以便看起来正确。例如,在应用配置文件中标记支持 RTL 的应用,在希伯来语或阿拉伯语地区设置下会以 RTL 模式渲染。

强制使用 RTL 布局

你也可以通过在应用配置中启用 extra.forcesRTL 属性,来进行测试时的 RTL 布局强制设置,或者用于仅为 RTL 地区提供本地化的应用:

app.json
{ "expo": { "extra": { "supportsRTL": true, "forcesRTL": true }, "plugins": ["expo-localization"] } }
动态覆盖 RTL 设置

如果你想在应用代码中动态覆盖默认的 RTL 检测,就不能使用 app config 中的静态配置。相反,你需要从应用代码中动态应用这些更改。

这在 Expo Go 中不起作用,因为 Expo Go 在打开启动器或单个项目时会重置 RTL 偏好设置。

覆盖 RTL 设置
import { Text, View, StyleSheet, I18nManager, Platform } from 'react-native'; import Constants from 'expo-constants'; import * as Updates from 'expo-updates'; export default function App() { const shouldBeRTL = true; if (shouldBeRTL !== I18nManager.isRTL && Platform.OS !== 'web') { I18nManager.allowRTL(shouldBeRTL); I18nManager.forceRTL(shouldBeRTL); Updates.reloadAsync(); } return ( <View style={styles.container}> <Text style={styles.paragraph}>{I18nManager.isRTL ? ' RTL' : ' LTR'}</Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', paddingTop: Constants.statusBarHeight, padding: 8, }, paragraph: { fontSize: 18, fontWeight: 'bold', textAlign: 'left', width: '50%', backgroundColor: 'pink', }, });

让应用在 RTL 区域设置下正常运行

布局和视图

你不需要根据区域设置手动调整 <View> 的样式属性。你可以使用 justifyContentalignItems 等属性。它们的属性值会按需改变行为。

  • 在 LTR 区域设置中,startend 等同于 leftright
  • 在 RTL 区域设置中,startend 等同于 rightleft
有关 React Native 中 RTL 工作方式的更多细节,请查看介绍 RTL 支持的 React Native 博客文章

Web 支持

RTL 布局的 Web 支持不需要更改应用配置。

Expo 在浏览器中运行 Expo 项目时使用 react-native-web。要让 react-native-web 自动适应区域方向,请在根 <View> 组件上添加 dir 属性。

App.tsx
import { View } from 'react-native'; import { getLocales } from 'expo-localization'; // ... return <View dir={getLocales()[0].textDirection || 'ltr'}>...</View>;
textDirection 在 Firefox 和较旧的浏览器版本中不可用。必要时请手动检测

文本对齐

React Native 的 textDirection 属性不接受你可以在 flex 属性中使用的 startend 值。相反,left 实际上可作为 start 使用(在 LTR 中对齐到左侧,在 RTL 中对齐到右侧),而 right 可作为 end 使用。

不过,textDirection 属性默认的未设置值表示真正的 left(在 LTR 和 RTL 中都对齐到左侧)。这意味着如果你希望每个 <Text> 标签都能正确对齐,就应该将 textDirection: lefttextDirection: right 样式设置到它上面。

最好在你自定义的可复用 <Text> 组件中定义这个样式,然后在需要渲染文本字符串的所有地方导入它。

mobile-text.tsx
import { Text as RNText, TextProps as RNTextProps } from 'react-native'; const MobileText = (props: RNTextProps) => { return <RNText style={{ textAlign: 'left', ...props.style }} {...props} />; }; export default MobileText;

Web 支持

对于每个文本标签,你需要添加带有当前区域设置标识符的 lang 属性。最好在自定义的可复用组件中定义这个样式。

web-text.tsx
import { getLocales } from 'expo-localization'; const deviceLanguage = getLocales()[0].languageCode; const WebText = (props: RNTextProps) => { return <RNText lang={deviceLanguage} {...props} />; }; export default WebText;

然后,你可以根据当前平台选择移动端或 Web 版 Text 组件。

Text.tsx
const Text = Platform.OS === 'web' ? WebText : MobileText; export default Text;

根据区域方向选择资源

如果你需要为 LTR/RTL 使用不同的图标,或者根据此设置更改样式,你可以使用 I18nManager.isRTL 来获取当前布局方向。

import { I18nManager } from 'react-native'; const isRTL = I18nManager.isRTL;

区域设置和单位

Expo 提供了 expo-localization 库,让你可以读取用户的区域设置和其他偏好。你可以使用同步的 getLocales()getCalendars() 方法来获取用户设备当前的区域设置:

  • getLocales() 会根据用户偏好的顺序返回一个区域设置列表。列表中始终至少会有一个区域设置。

  • getCalendars() 会根据用户偏好的顺序返回一个日历列表。列表中始终至少会有一个日历。

import { getLocales, getCalendars } from 'expo-localization'; const { languageTag, languageCode, textDirection, digitGroupingSeparator, decimalSeparator, measurementSystem, currencyCode, currencySymbol, regionCode, } = getLocales()[0]; const { calendar, timeZone, uses24hourClock, firstWeekday } = getCalendars()[0];
限制

在依赖 expo-localization 自动检测到的区域偏好时,需要注意一些限制。

  • 目前还没有办法从用户偏好中读取温度单位。在 Android 上,你可以使用基于区域设置的查找表。不过,在 iOS 上,用户可以在设备偏好设置中更改它。
  • 某些属性在当前平台上不可用时可能为 null。

Intl API

如果你在应用中使用 Hermes,你可以在所有平台上使用 Intl API。

它提供了一组实用工具,可用于格式化列表、日期、数字、货币金额、单位、复数形式等更多内容。

如果你将 default 作为区域设置字符串传入,Intl API 会使用设备的区域设置,因此你无需依赖 expo-localization 来获取当前区域设置(例如 "en-US")。

new Intl.NumberFormat('default', { style: 'currency', currency: 'EUR' }).format(5.0);

一旦你知道用户期望看到什么,就可以使用 Intl API 来格式化字符串和值。

Intl API 不提供设备或当前区域设置的信息,因此你不能使用 Intl API 来获取当前区域设置中的单位、货币或计量系统。

为此,你需要使用 expo-localization、Web 上的 JS 代码,或者 Android 和 iOS 上的第三方或自定义原生代码。