子贤的独立博客 子贤的独立博客
  • 首页
  • 编程
  • 视频
    • 哔哩哔哩
    • 斗鱼TV
    • VLOG
  • 社交媒体
    • 新浪微博
  • 专题
首页 › 编程 › React Native 多语言国际化

React Native 多语言国际化

James
5年前编程阅读 7,381

前言

多语言国际化在日常开发中算是一个比较常见的需求,一般应用中都至少需要中文以及英文的国际化处理,Vue中使用vue-i18n可以快速实现需求,这次来聊聊React Native中怎么实现此需求(React实现并无特别)。

一、需求分析

首先通过功能拆解,我们可以主要区分为两个功能点:

1、读取本地国际化实现对应多语言

主要先通过识别获取设备环境(浏览器,手机…)的系统语言,然后使应用在初始化时加载对应的语言文件实现初始化多语言。

2、通过语言修改选项切换应用当前语言

当用户主动进行修改语言操作后,我们将用户的语言修改结果作为应用初始化的优先级最高的预设,如无则次级再去获取设备环境语言并应用,所以这里的用户修改语言的结果我们需要做到本地存储化并读取。

二、解决方案

因为当前使用的项目是基于React Native开发的,所以先寻求最贴近的解决方案,大多数人使用的是 react-native-i18n,但是经过查看Github,发现该仓库已废弃并停止维护。

React Native 多语言国际化-子贤的独立博客

官方推荐的是使用 react-native-localize 配合 i18n.js实现这个需求,那么我们也就采用这个方案来实现吧。

Setup

$ npm install --save react-native-localize
$ npm install --save i18n-js

# --- or ---

$ yarn add react-native-localize
$ yarn add i18n-js

三、识别系统语言并自动初始化

我们通过使用 react-native-localize 获取手机的系统语言,然后赋值为i18n的locale变量即可,与vue-i18n有点不同的是,语言包需设置给translations字段,注意这里的对象键名即为语言包使用时的属性值,如果使用与定义的对不上就会语言翻译错误。

/**
 * 多语言配置文件
 */
import I18n from 'i18n-js';
import * as RNLocalize from 'react-native-localize';
import cn from './lang/zh-cn';
import en from './lang/en-us';

// 获取手机本地国际化信息
const locales = RNLocalize.getLocales();
const systemLanguage = locales[0]?.languageCode; // 用户系统偏好语言

// 如果获取到了即使用,否则启用默认语言
if (systemLanguage) {
  I18n.locale = systemLanguage;
} else {
  I18n.locale = 'en'; // 用户既没有设置,也没有获取到系统语言,默认加载英语语言资源
}

I18n.fallbacks = true;

// 加载语言包
I18n.translations = {
  cn,
  en,
};

export default I18n;

至于多语言的语言包文件可按项目需求进行添加,推荐单独目录管理,便于维护,语言包的格式要求较为宽松,通过文档便可了解到,一般我们使用js或者json格式即可。

e.g.:

// lang/zh-cn.js
export default {
  signIn: {
    title: '欢迎登陆',
  },
};

// lang/en-us.js
export default {
  signIn: {
    title: 'Welcome',
  },
};

使用时也非常简单,对应的更多使用示例可参考i18n的文档,最简单的视图文本使用如下:

I18n.t('signIn.title')

至此,完成了简单的需求功能一的开发,但是一般也允许用户自主修改App语言,于是我们继续修改实现功能二的需求。

四、支持用户手动修改App语言并持久化

中心思路很简单,持久化存储用户修改的语言,并修改现有逻辑首选读取它进行预设加载App,这里主要使用 redux 和 持久化插件 redux-persist。

1、新增操作的 action type

// 具体名称根据自己喜好
  USER_SET_LANGUAGE: 'USER_SET_LANGUAGE',

2、新增派发的 actionCreator函数

export const userSetLanguage = languageCode => {
  return {
    type: Types.USER_SET_LANGUAGE,
    payload: {
      languageCode,
    },
  };
};

3、在 reducers 中进行状态更新处理

import Types from '../../action/types';

const defaultState = {
  ...,
  userLanguageSetting: null, //用户手动设置的语言
};

const onAction = (state = defaultState, action) => {
  const {type, payload} = action;
  switch (type) {
    case Types.USER_SET_LANGUAGE:
      const {languageCode} = payload;
      return {
        ...state,
        userLanguageSetting: languageCode,
      };
    default:
      return state;
  }
};

export default onAction;

4、修改核心逻辑读取变量并应用

刚已经通过 redux 维护了一个名为 userLanguageSetting 的变量来管理用户设置的语言设置,如未设置将为 null,接下来修改核心逻辑,首选读取应用该字段,无则读取使用手机的系统语言,兜底默认加载英文。

使用 redux 保存应用状态会在应用重新启动时全部丢失,所以这里要对 redux 状态进行持久化,推荐使用 redux-persist 进行处理,具体查阅文档即可。

/**
 * 多语言配置文件
 */
import I18n from 'i18n-js';
import * as RNLocalize from 'react-native-localize';
import cn from './lang/zh-cn';
import en from './lang/en-us';
import store from '../store';

// 获取手机本地国际化信息
const locales = RNLocalize.getLocales();
const systemLanguage = locales[0]?.languageCode; // 用户系统偏好语言
const {
  app: {userLanguageSetting},
} = store.getState();

// 首选用户设置的语言 其次手机系统语言 最后兜底默认语言
if (userLanguageSetting) {
  I18n.locale = userLanguageSetting;
} else if (systemLanguage) {
  I18n.locale = systemLanguage;
} else {
  I18n.locale = 'en'; // 用户既没有设置,也没有获取到系统语言,默认加载英语语言资源
}

// 订阅store变化 如果用户设置语言变动相应更新i18n的locale字段
store.subscribe(() => {

// 使用解构获取store.app.userLanguageSetting
  const {
    app: {userLanguageSetting: newUserLanguageSetting},
  } = store.getState();

  if (
    newUserLanguageSetting &&
    newUserLanguageSetting !== userLanguageSetting
  ) {
    I18n.locale = newUserLanguageSetting;
  }
});

I18n.fallbacks = true;

// 加载语言包
I18n.translations = {
  cn,
  en,
};

export default I18n;
export {systemLanguage};

使用 store.getState() 获取 store 中的 userLanguageSetting 字段值,然后通过 store.subscribe 来订阅 redux 状态改变,并更新 i18n 的 locale 字段。

五、切换语言后实现已渲染页面的响应重绘

现在我们已经能实现用户切换语言的需求,但是仔细会发现,如果页面已完成渲染再修改语言,已渲染的页面并不会随着语言更新进行重绘。

查阅网上解决方案,stackoverflow的开发者们有推荐使用 react-native-restart 重新 reload 应用来使 js 重新加载,可是 reload 应用来的效果在用户体验上的确很差,所以暂不考虑。

分析下核心思路是在语言修改后,让已渲染的页面监听到并触发重绘即可解决问题,可以通过编写一个自定义 hook 解决。

// @/hooks/useLanguageUpdate.js

import React, {useState, useEffect} from 'react';
import store from '../store';
import I18n from '../i18n';

export const useLanguageUpdate = (funcWhenUpdate, listenParamArr = []) => {
  const [currentLanguageCode, setCurrentLanguageCode] = useState(I18n.locale);

  useEffect(() => {
    return store.subscribe(() => {
      const {
        app: {userLanguageSetting: newLanguageCode},
      } = store.getState();
      console.log('change');

      if (newLanguageCode && newLanguageCode != currentLanguageCode) {
        setCurrentLanguageCode(newLanguageCode);
        console.log('123');
        if (funcWhenUpdate) {
          funcWhenUpdate();
        }
      }
    });
  }, [currentLanguageCode, ...listenParamArr]);

  return currentLanguageCode;
};

然后在涉及的页面使用该 hook,e.g.:

function SignInPage(props) {
  ...
  useLanguageUpdate();

需要使用更新后的语言进行业务逻辑可以直接使用该 hook 的返回值进行处理,e.g.:

let currentLanguage = useLanguageUpdate();

switch(currentLanguage) {
    case 'cn':
        ...
        break;
    case 'en':
        ...
        break;
    ...
}

或者需要在语言更新时,执行业务逻辑,可以直接传入回调函数 funcWhenUpdate 实现,第二个参数可以传入监听的变量合集,默认为空数组。

使用回调函数
useLanguageUpdate(() => {
  doSomeThing();
});

总结

到此已经实现所有功能需求,仔细梳理回顾下会发现其实逻辑并不困难,实现起来也并不困难,好的思路解刨会使开发效率提升很多,这次记录就先到这啦,溜溜球!

React Native
赞赏 赞(10)
React Native 设置文件路径别名
上一篇
再想想
所有评论(1)
  • SuperMan

    Mark!感谢分享!

    5年前 回复

Recent Posts

  • React Native 多语言国际化
  • React Native 设置文件路径别名
  • 不要笑大挑战 | 两个憨憨 | 达达倾情出演
  • 黄埔古港 | “哥哥影你啊” | 随剪短片
  • 参观省博物馆 | 展品片段随剪

Recent Comments

  1. SuperMan发表在React Native 多语言国际化
React Native 设置文件路径别名
5年前
3,245 0 1
解决charles模拟localhost请求无效问题
6年前
3,587 0 0
Cordova构建IOS应用适配iPhone X
6年前
2,598 0 0
记IOS构建运行时遇到A valid provisioning profile for this executable was not found
6年前
3,115 0 1
1 10
  • 10
  • 1
Copyright © 2019-2025 子贤的独立博客. Designed by nicetheme.
粤ICP备19162060号
  • 首页
  • 编程
  • 视频
    • 哔哩哔哩
    • 斗鱼TV
    • VLOG
  • 社交媒体
    • 新浪微博
  • 专题
# Vlog # # Cordova # # ETH # # Css # # Android #
James
32
文章
0
评论
35
喜欢