国际化是现代 App 的必备功能。用户来自世界各地,他们使用不同的语言。通过语言设置,用户可以选择自己熟悉的语言来使用 App。这篇文章来实现一个完整的语言设置系统,包括多语言支持、动态切换和持久化存储。

语言配置的设计
首先需要定义 App 支持哪些语言。在 AppContext 中已经有了语言状态,现在需要为每种语言定义对应的文本内容。
先创建一个语言配置对象,包含所有支持的语言和对应的翻译文本:
tsx
const LANGUAGE_CONFIG = {
'zh-CN': {
name: '简体中文',
flag: '🇨🇳',
translations: {
home: '首页',
favorites: '收藏',
history: '历史',
settings: '设置',
language: '语言',
theme: '主题',
notifications: '通知',
about: '关于',
help: '帮助',
feedback: '反馈',
}
},
'en-US': {
name: 'English',
flag: '🇺🇸',
translations: {
home: 'Home',
favorites: 'Favorites',
history: 'History',
settings: 'Settings',
language: 'Language',
theme: 'Theme',
notifications: 'Notifications',
about: 'About',
help: 'Help',
feedback: 'Feedback',
}
},
'ja-JP': {
name: '日本語',
flag: '🇯🇵',
translations: {
home: 'ホーム',
favorites: 'お気に入り',
history: '履歴',
settings: '設定',
language: '言語',
theme: 'テーマ',
notifications: '通知',
about: 'について',
help: 'ヘルプ',
feedback: 'フィードバック',
}
}
};
这个配置对象的结构很清晰:
name- 语言的显示名称,比如"简体中文"flag- 语言对应的国旗 emoji,用于视觉识别translations- 该语言的所有翻译文本,键是文本的 ID,值是翻译后的文本
扩展性 - 这种结构很容易扩展。如果要添加新的语言,只需要在
LANGUAGE_CONFIG中添加一个新的语言对象就行。如果要添加新的翻译文本,只需要在translations中添加新的键值对。
创建翻译 Hook
为了方便在组件中使用翻译文本,创建一个自定义 Hook 来获取当前语言的翻译:
tsx
export const useTranslation = () => {
const {language} = useApp();
const t = (key: string, defaultValue?: string) => {
const config = LANGUAGE_CONFIG[language as keyof typeof LANGUAGE_CONFIG];
if (!config) {
return defaultValue || key;
}
return config.translations[key as keyof typeof config.translations] || defaultValue || key;
};
return {t, language};
};
这个 Hook 的逻辑很简单:
- 获取当前语言 - 从全局状态中获取当前选中的语言
- 翻译函数 -
t函数接收一个文本 ID,返回对应语言的翻译文本 - 兜底处理 - 如果找不到翻译,返回默认值或原始的 key
使用这个 Hook 非常简单:
tsx
const {t} = useTranslation();
<Text>{t('home')}</Text> // 显示"首页"或"Home"或"ホーム"
Hook 的好处 - 通过自定义 Hook,可以把翻译逻辑封装起来。组件只需要调用
t()函数就能获得翻译文本,不需要关心翻译的具体实现。
语言选择界面
语言设置页面需要显示所有可用的语言,让用户选择。先看语言列表的 UI:
tsx
export const LanguageSettingsScreen = () => {
const {language, setLanguage} = useApp();
const {t} = useTranslation();
const languages = Object.entries(LANGUAGE_CONFIG).map(([code, config]) => ({
code,
...config,
}));
return (
<View style={styles.container}>
<Header title={t('language')} showBack />
<ScrollView style={styles.content}>
<View style={styles.languageList}>
{languages.map(lang => (
<TouchableOpacity
key={lang.code}
style={[styles.languageItem, language === lang.code && styles.languageItemActive]}
onPress={() => setLanguage(lang.code)}
>
<Text style={styles.languageFlag}>{lang.flag}</Text>
<View style={styles.languageInfo}>
<Text style={styles.languageName}>{lang.name}</Text>
<Text style={styles.languageCode}>{lang.code}</Text>
</View>
{language === lang.code && <Text style={styles.checkmark}>✓</Text>}
</TouchableOpacity>
))}
</View>
</ScrollView>
<TabBar />
</View>
);
};
这个界面的关键点:
- 遍历语言 - 用
Object.entries()遍历LANGUAGE_CONFIG对象,获取所有可用的语言 - 选中状态 - 当前选中的语言会显示不同的样式和一个对勾符号
- 点击处理 - 点击语言项时调用
setLanguage()更新全局状态
用户体验 - 通过国旗 emoji 和语言名称,用户可以快速识别每种语言。选中状态的视觉反馈让用户知道当前选择了哪种语言。
然后看样式设计:
tsx
languageList: {paddingVertical: 16},
languageItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: '#2a475e',
},
languageItemActive: {backgroundColor: '#1b2838'},
languageFlag: {fontSize: 32, marginRight: 16},
languageInfo: {flex: 1},
languageName: {fontSize: 16, fontWeight: '600', color: '#fff', marginBottom: 2},
languageCode: {fontSize: 12, color: '#8f98a0'},
checkmark: {fontSize: 20, color: '#66c0f4', fontWeight: 'bold'},
样式设计的要点:
- 列表项 - 用
flexDirection: 'row'让国旗、语言信息和对勾水平排列 - 活跃状态 - 选中的语言项背景色会变深,对勾显示为 Steam 蓝
- 间距 - 各个元素之间有适当的间距,看起来不拥挤
- 文字层级 - 语言名称用较大的字体和白色,语言代码用较小的字体和灰色
语言切换的实时生效
当用户切换语言时,整个 App 的文字应该立即改变。这需要在 AppContext 中实现响应式的语言切换。
在 AppContext 中,setLanguage 函数很简单:
tsx
const setLanguage = (lang: string) =>
setState(prev => ({...prev, language: lang}));
但关键是,所有使用 useTranslation Hook 的组件都会自动重新渲染,因为 Hook 内部依赖了全局状态中的 language。
响应式更新 - 当全局状态中的
language改变时,所有使用useApp()或useTranslation()的组件都会重新渲染。这样用户切换语言后,整个 App 的文字会立即改变。
语言设置的持久化
用户的语言选择应该被保存,这样下次打开 App 时还能使用之前选择的语言。可以使用 AsyncStorage 来实现:
tsx
import AsyncStorage from '@react-native-async-storage/async-storage';
const saveLanguage = async (language: string) => {
try {
await AsyncStorage.setItem('app_language', language);
} catch (error) {
console.error('Error saving language:', error);
}
};
const loadLanguage = async () => {
try {
const saved = await AsyncStorage.getItem('app_language');
return saved || 'zh-CN';
} catch (error) {
console.error('Error loading language:', error);
return 'zh-CN';
}
};
然后在 AppProvider 中集成这些函数:
tsx
export const AppProvider = ({children}: {children: ReactNode}) => {
const [state, setState] = useState<AppState>({
// ... 初始状态
language: 'zh-CN',
});
useEffect(() => {
// 应用启动时加载保存的语言设置
loadLanguage().then(lang => {
setState(prev => ({...prev, language: lang}));
});
}, []);
useEffect(() => {
// 当语言改变时保存到本地
saveLanguage(state.language);
}, [state.language]);
// ... 其他代码
};
这样实现的好处:
- 自动保存 - 用户改变语言时,自动保存到本地存储
- 自动恢复 - App 启动时自动加载之前保存的语言设置
- 无缝体验 - 用户不需要每次打开 App 都重新选择语言
持久化策略 - 通过
useEffect监听语言的变化,当语言改变时自动保存。这样用户的设置能在应用关闭后保留。
系统语言检测
为了提供更好的用户体验,可以在 App 首次启动时自动检测系统语言,并设置为对应的语言。
tsx
import {Platform, NativeModules} from 'react-native';
const getSystemLanguage = () => {
let locale = 'zh-CN';
if (Platform.OS === 'android') {
locale = NativeModules.I18nManager.localeIdentifier;
} else if (Platform.OS === 'ios') {
locale = NativeModules.SettingsManager.settings.AppleLocale;
}
// 将系统语言代码转换为 App 支持的语言
if (locale.startsWith('en')) {
return 'en-US';
} else if (locale.startsWith('ja')) {
return 'ja-JP';
} else {
return 'zh-CN';
}
};
然后在 AppProvider 中使用这个函数:
tsx
useEffect(() => {
loadLanguage().then(saved => {
if (!saved) {
// 如果没有保存的语言设置,使用系统语言
const systemLang = getSystemLanguage();
setState(prev => ({...prev, language: systemLang}));
}
});
}, []);
用户友好 - 通过检测系统语言,新用户打开 App 时会自动使用他们熟悉的语言。这提供了更好的首次使用体验。
在其他页面中使用翻译
现在在任何页面中都可以使用 useTranslation Hook 来获取翻译文本。比如在首页中:
tsx
export const HomeScreen = () => {
const {t} = useTranslation();
return (
<View style={styles.container}>
<Header title={t('home')} />
<ScrollView>
<Text style={styles.sectionTitle}>{t('favorites')}</Text>
{/* ... 其他内容 */}
</ScrollView>
</View>
);
};
这样做的好处是,所有的文本都通过 t() 函数来获取,当用户切换语言时,这些文本会自动更新。
代码组织 - 通过统一使用
t()函数来获取文本,代码变得更清晰。如果要修改某个文本,只需要在LANGUAGE_CONFIG中修改就行,不需要在代码中搜索和替换。
样式汇总
语言设置页面的样式采用 Steam 的深色主题:
tsx
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#171a21'},
content: {flex: 1},
languageList: {paddingVertical: 16},
languageItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: '#2a475e',
},
languageItemActive: {backgroundColor: '#1b2838'},
languageFlag: {fontSize: 32, marginRight: 16},
languageInfo: {flex: 1},
languageName: {fontSize: 16, fontWeight: '600', color: '#fff', marginBottom: 2},
languageCode: {fontSize: 12, color: '#8f98a0'},
checkmark: {fontSize: 20, color: '#66c0f4', fontWeight: 'bold'},
});
配色说明:
#171a21- 最深的背景色,用于页面底色#1b2838- 选中项的背景色#2a475e- 分割线颜色#66c0f4- Steam 标志蓝,用于对勾符号#8f98a0- 灰色,用于语言代码#fff- 白色,用于语言名称
小结
语言设置系统展示了如何实现一个完整的国际化方案:
- 配置管理 - 通过配置对象集中管理所有语言和翻译文本
- 自定义 Hook - 创建
useTranslationHook 简化组件中的翻译逻辑 - 全局状态 - 语言设置存储在全局状态中,确保整个 App 都能访问
- 实时切换 - 用户切换语言时,整个 App 的文字立即改变
- 持久化存储 - 用户的语言选择被保存,下次打开 App 时自动恢复
- 系统检测 - 首次启动时自动检测系统语言,提供更好的用户体验
- 易于扩展 - 添加新的语言或翻译文本只需要修改配置对象
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net