React Native for OpenHarmony 实战:Steam 资讯 App 语言设置实现

案例开源地址:https://atomgit.com/nutpi/rn_openharmony_steam

国际化是现代 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 - 创建 useTranslation Hook 简化组件中的翻译逻辑
  • 全局状态 - 语言设置存储在全局状态中,确保整个 App 都能访问
  • 实时切换 - 用户切换语言时,整个 App 的文字立即改变
  • 持久化存储 - 用户的语言选择被保存,下次打开 App 时自动恢复
  • 系统检测 - 首次启动时自动检测系统语言,提供更好的用户体验
  • 易于扩展 - 添加新的语言或翻译文本只需要修改配置对象

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
小夏卷编程2 小时前
ant-design-vue 2.0 a-table 中实现特殊行样式,选中样式,鼠标悬浮样式不一样
前端·javascript·vue.js
AI_56782 小时前
Webpack从“配置到提速”,4步解决“打包慢、体积大”问题
前端·javascript·vue.js
pinkQQx2 小时前
手把手搭建前端跨平台服务(IPlatform + iOS / Android / Web)
前端·javascript
2501_948194982 小时前
RN for OpenHarmony AnimeHub项目实战:即将上映页面开发
react native
2501_948122632 小时前
React Native for OpenHarmony 实战:Steam 资讯 App 关于我们页面
javascript·react native·react.js·游戏·harmonyos
Aotman_2 小时前
Vue el-table 字段自定义排序
前端·javascript·vue.js·es6
我有一棵树2 小时前
Vite 7 中 dev 没样式、build 却正常:一次由 CSS import 位置引发的工程化问题
前端·javascript·vue.js
怕浪猫2 小时前
React从入门到出门第七章 管理你的CSS 模块化样式控制方案
javascript·css·react.js
lili-felicity3 小时前
React Native for Harmony:地址管理页面(新增+编辑)完整实现
react native·react.js·harmonyos