为了让使用不同语言、来自不同文化背景的用户方便地使用应用,需要对应用进行本地化。应用本地化后,可以根据用户设备的区域设置调整内容,并显示用户熟悉的译文与货币。
可以使用 Expo 提供的
expo-localization↗ 来支持多语言。它让应用可以读取语言设置,再交给
i18n-js↗ 等翻译库处理。面向全球提供服务时,多语言支持很重要。
通常可以借助基于 i18n 的库实现多语言。再配合 Expo 的 SDK,读取设备设置也会更简单。
安装 expo-localization 和 react-i18next 🔗
先执行以下命令,安装 expo-localization、react-i18next、i18next 和 @react-native-async-storage/async-storage。
npx expo install expo-localization react-i18next i18next @react-native-async-storage/async-storage
安装后,可以导入 expo-localization,编写读取设备当前语言设置的代码。
/app/index.tsximport React from "react";
import { View, Text } from "react-native";
import { getLocales } from "expo-localization";
const App = () => {
const deviceLanguage = getLocales()[0].languageCode;
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={{ fontSize: 20 }}>현재 기기 언어: {deviceLanguage}</Text>
</View>
);
};
export default App;
上面的
getLocales() 会根据设备的
系统设置
返回当前区域信息,应用可以据此自动检测用户偏好的语言。
👨💻
较新的 Android 和 iOS 支持为单个应用设置语言。可以据此判断应用内是否需要提供语言切换按钮。
预览 /app/index.ts 完整代码
/app/index.tsimport React from "react";
import { View, Text, Button } from "react-native";
import { initReactI18next, useTranslation } from "react-i18next";
import { getLocales } from "expo-localization";
import i18n from "i18next";
const resources = {
en: {
translation: {
welcome: "Welcome to the app!",
},
},
ko: {
translation: {
welcome: "앱에 오신 것을 환영합니다!",
},
},
ar: {
translation: {
welcome: "مرحبًا بك في التطبيق!",
},
},
};
i18n.use(initReactI18next).init({
resources,
lng: getLocales()[0].languageCode || "ko",
fallbackLng: {
"en-*": ["en"],
"ko-*": ["ko"],
"ar-*": ["ar"],
default: ["en"],
},
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
});
const App = () => {
const { t, i18n } = useTranslation();
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
gap: 20,
}}
>
<Text>{t("welcome")}</Text>
<Button title="English" onPress={() => i18n.changeLanguage("en")} />
<Button title="Korean" onPress={() => i18n.changeLanguage("ko")} />
<Button title="Arabic" onPress={() => i18n.changeLanguage("ar")} />
</View>
);
};
export default App;
定义应用支持的语言与翻译数据。
/app/index.ts// 컴포넌트 밖에 번역 데이터 정의
const resources = {
en: {
translation: {
welcome: "Welcome to the app!",
},
},
ko: {
translation: {
welcome: "앱에 오신 것을 환영합니다!",
},
},
ar: {
translation: {
welcome: "مرحبًا بك في التطبيق!",
},
},
};
// ...
接着编写 i18n 初始化代码,通过
getLocales()[0].languageCode 将设备语言设为默认语言。
还可以通过 fallbackLng 指定未设置语言时使用的备用语言。
/app/index.tsimport i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n.use(initReactI18next).init({
resources,
lng: getLocales()[0].languageCode || "ko",
fallbackLng: {
"en-*": ["en"],
"ko-*": ["ko"],
"ar-*": ["ar"],
default: ["en"],
},
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
});
最后,在界面中使用译文,并添加可以切换语言的按钮。通过 useTranslation Hook 读取译文。
/app/index.tsimport React from "react";
import { View, Text, Button } from "react-native";
import { initReactI18next, useTranslation } from "react-i18next";
import { getLocales } from "expo-localization";
import i18n from "i18next";
//... i18n 초기화 코드
const App = () => {
const { t, i18n } = useTranslation();
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
gap: 20,
}}
>
<Text>{t("welcome")}</Text>
<Button title="English" onPress={() => i18n.changeLanguage("en")} />
<Button title="Korean" onPress={() => i18n.changeLanguage("ko")} />
<Button title="Arabic" onPress={() => i18n.changeLanguage("ar")} />
</View>
);
};
export default App;
现在点击不同按钮,界面就会切换到对应语言。
上面的代码将翻译数据作为变量定义在组件外部。随着内容增加,文件会变长,也会难以管理。下面把翻译数据拆到单独的文件中。
在终端执行以下命令,创建保存翻译数据的目录与文件。
mkdir -p src/i18n/locales/{en-US,ko-KR,ar-SA} && \
touch src/i18n/index.ts \
src/i18n/locales/en-US/translations.json \
src/i18n/locales/ko-KR/translations.json \
src/i18n/locales/ar-SA/translations.json
这会生成下面的目录结构。
📦 src
┣ 📂 i18n
┃ ┣ 📂 locales
┃ ┃ ┣ 📂 ar-SA
┃ ┃ ┃ ┗ 📜 translations.json
┃ ┃ ┣ 📂 en-US
┃ ┃ ┃ ┗ 📜 translations.json
┃ ┃ ┗ 📂 ko-KR
┃ ┃ ┃ ┗ 📜 translations.json
┗ ┗ 📜 index.ts
在各个 JSON 文件中编写翻译数据。
/src/i18n/locales/en-US/translations.json{
"welcome": "Welcome to the app!"
}
/src/i18n/locales/ko-KR/translations.json{
"welcome": "앱에 오신 것을 환영합니다!"
}
/src/i18n/locales/ar-SA/translations.json{
"welcome": "مرحبًا بك في التطبيق!"
}
接着在 src/i18n/index.ts 中编写读取翻译数据的代码。
/src/i18n/index.tsimport i18n from "i18next";
import { initReactI18next } from "react-i18next";
import { getLocales } from "expo-localization";
import translationEn from "./locales/en-US/translations.json";
import translationKo from "./locales/ko-KR/translations.json";
import translationAr from "./locales/ar-SA/translations.json";
const resources = {
en: { translation: translationEn },
ko: { translation: translationKo },
ar: { translation: translationAr },
};
const initI18n = async () => {
i18n.use(initReactI18next).init({
resources,
lng: getLocales()[0].languageCode || "ko",
fallbackLng: {
"en-*": ["en"],
"ko-*": ["ko"],
"ar-*": ["ar"],
default: ["en"],
},
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
});
};
initI18n();
export default i18n;
最后,导入 src/i18n/index.ts 并使用。这样管理翻译数据会更方便。也可以在 /app/_layout.tsx 中导入,让设置应用到整个项目。
/app/index.tsimport "@/src/i18n";
// ... App 컴포넌트 밖의 i18n 초기화 코드 제거
如果希望应用关闭并重新打开后,仍使用用户选定的语言,可以通过
AsyncStorage 保存语言设置。
AsyncStorage 可以异步存取应用数据。
在
/src/i18n/index.ts 中用
AsyncStorage 保存并读取语言设置,步骤如下。
- 检查 AsyncStorage 是否已有保存的语言设置。
- 如果没有,就读取设备的语言设置并使用该语言。
- 如果已经保存过,就使用保存的语言。
- 将语言设置写入 AsyncStorage。
语言标识通常采用 {语言代码}-{国家代码} 格式,例如韩语是 ko-KR,英语是 en-US。当前的翻译数据使用 ko、en、ar 这类简短代码,因此从设备读到设置后,只提取语言代码并保存。为防止出错,将 en 设为默认语言。
/src/i18n/index.ts 完整代码
/src/i18n/index.tsimport i18n from "i18next";
import { initReactI18next } from "react-i18next";
import * as Localization from "expo-localization";
import AsyncStorage from "@react-native-async-storage/async-storage";
import translationEn from "./locales/en-US/translations.json";
import translationKo from "./locales/ko-KR/translations.json";
import translationAr from "./locales/ar-SA/translations.json";
const resources = {
en: { translation: translationEn },
ko: { translation: translationKo },
ar: { translation: translationAr },
};
const LANGUAGE_KEY = "@app_language";
const initI18n = async () => {
try {
const savedLanguage = await AsyncStorage.getItem(LANGUAGE_KEY);
let selectedLanguage = savedLanguage;
if (!selectedLanguage) {
const deviceLocales = Localization.getLocales();
const deviceLocale = deviceLocales[0]?.languageTag || "en-US";
const languageCode = deviceLocale.split("-")[0];
if (languageCode in resources) {
selectedLanguage = languageCode;
} else {
selectedLanguage = "en";
}
}
await i18n.use(initReactI18next).init({
resources,
lng: selectedLanguage,
fallbackLng: {
"en-*": ["en"],
"ko-*": ["ko"],
"ar-*": ["ar"],
default: ["en"],
},
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
});
if (!savedLanguage) {
await AsyncStorage.setItem(LANGUAGE_KEY, selectedLanguage);
}
} catch (error) {
await i18n.use(initReactI18next).init({
resources,
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
});
}
};
initI18n();
export default i18n;
在 /app/index.tsx 中,每次点击语言切换按钮时,都将语言设置写入 AsyncStorage。
/app/index.ts 完整代码
/app/index.tsimport React from "react";
import "@/src/i18n";
import { View, Text, Button } from "react-native";
import { useTranslation } from "react-i18next";
import AsyncStorage from "@react-native-async-storage/async-storage";
const App = () => {
const { t, i18n } = useTranslation();
const changeLanguage = async (language: "en" | "ko" | "ar") => {
i18n.changeLanguage(language);
await AsyncStorage.setItem("@app_language", language);
};
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
gap: 20,
}}
>
<Text>{t("welcome")}</Text>
<Button title="English" onPress={() => changeLanguage("en")} />
<Button title="Korean" onPress={() => changeLanguage("ko")} />
<Button title="Arabic" onPress={() => changeLanguage("ar")} />
</View>
);
};
export default App;
现在运行应用,即使退出,选定的语言也会保留。可以切换语言、退出应用,再重新打开验证。
Cannot find native module 'ExpoLocalization' 🔗
这个问题会在开发模式中出现,原因是原生代码尚未同步。运行以下命令即可解决。
cd ios && pod install && cd ..
npx expo run:ios
本文介绍了如何在 Expo 中支持多语言包。通过 Expo 提供的
expo-localization 读取设备语言设置,再使用
i18next 实现多语言翻译。
我们也把翻译数据拆到单独的文件中,并用 AsyncStorage 保存语言设置。如果应用面向全球用户,多语言包可以让不同语言的用户更方便地使用应用。