likes
comments
collection
share

React-Intl + Redux 实现React项目国际化

作者站长头像
站长
· 阅读数 12

前言

现在的前端项目中,很多时候我们需要对我们项目的文案进行国际化,以适用各国用户使用,今天我们就用React-Intl库配合Redux实现语言的实时切换如果对在React中使用Redux不熟悉的,可以我的参照另外两篇文章:这篇Redux,Redux-Toolkit教程真的很详细React项目中使用Redux,Redux-Toolkit

安装依赖

我们需要安装@reduxjs/toolkit,react-redux,react-intl三个依赖

npm i @reduxjs/toolkit react-redux react-intl

创建多语言文件夹

React-Intl + Redux 实现React项目国际化在项目中创建一个多语言文件夹,如上图所示,其中包含一个语言文件夹和入口文件,语言文件夹中是每种语言对应文案的翻译,index.tsx入口文件实现语言文件的整合

创建语言文件

我们创建三个语言文件,分别是中文,英语,法语。文件对象中的key是我们需要在项目中使用的id,value是对应的具体文案,当我们切换成某种语言时,就会根据key的值显示对应value具体文案。其中的{name},{text}是文案占位符,后续我们可以动态传入文案替换{}中的内容,这也就是在国际化文案中包含变量zh_CN.json

{
    "pageTitle": "谨防电信网络诈骗",
    "pageContent1": "近期,电信网络诈骗案件屡见不鲜,为了保障您的资金安全,{name}提醒您:{text}"
}

en_US.json

{
    "pageTitle": "Beware of Telecommunications and Network Fraud",
    "pageContent1": "Recently, cases of telecommunications and network fraud are common. In order to ensure the safety of your funds, {name} reminds you: {text}"
}

fr_FR.json

{
    "pageTitle": "Méfiez-vous des télécommunications et de la fraude sur les réseaux",
    "pageContent1": "Récemment, les cas de télécommunications et de fraude sur les réseaux sont courants. Afin d'assurer la sécurité de vos fonds, {name} vous rappelle : {text}"
}

整合文件并导出

index.tsx整合各个语言文件并导出

import zh_CN from './languageJson/zh_CN.json'
import en_US from './languageJson/en_US.json'
import fr_FR from './languageJson/fr_FR.json'

type LanguageType = 'zh' | 'en' | 'fr'

type LanguageJsonType = {
    [key: string]: string
}

type LanguageMessageType = {
    [key in LanguageType]: LanguageJsonType
}

type LanguageListType = {
    label: string,
    value: LanguageType
}[]

const languageList: LanguageListType = [
    {
        label: '简体中文',
        value: 'zh'
    },
    {
        label: 'English',
        value: 'en'
    },
    {
        label: 'Français',
        value: 'fr'
    }
]

const languageMessage: LanguageMessageType = {
    zh: zh_CN as LanguageJsonType,
    en: en_US as LanguageJsonType,
    fr: fr_FR as LanguageJsonType
}

export {
    LanguageType,
    LanguageJsonType,
    languageList,
    languageMessage
}

文件解析:

  1. 定义类型,定义各个变量类型
  2. languageList,定义后续切换文件所需的语言列表
  3. languageMessage,整合所有语言成一个对象,对象的key为语言的名称,value就是对应的语言文件中的所有字段的对象集合

Redux中创建language状态

import { createSlice } from '@reduxjs/toolkit'
import { LanguageType } from '@src/language'

// 定义action类型
type LanguageActionType = {
    type: string,
    payload: LanguageType
}

// 定义初始state
const initialState = 'zh'

// 定义counterSlice
const counterSlice = createSlice({
    name: 'language',
    initialState,
    reducers: {
        changeLanguageAction(state: LanguageType, action: LanguageActionType) {
            return action.payload
        }
    }
})

// 取出action creator
const { changeLanguageAction } = counterSlice.actions
// 取出reducer
const languageReducer = counterSlice.reducer

export {
    languageReducer,
    changeLanguageAction
}

创建React根,并注入Redux store

import React from 'react'
import ReactDOM from 'react-dom/client'
import IntlApp from '@app/intlApp'
import { Provider } from 'react-redux'
import store from '@src/reduxToolkit/store'

const rootElement = document.getElementById('root') as HTMLElement
const root = ReactDOM.createRoot(rootElement)

root.render(
    <Provider store={store}>
        <IntlApp />
    </Provider>
)

根组件中注入多语言

import React from 'react'
import { IntlProvider } from 'react-intl'
import { languageMessage, LanguageJsonType } from '@src/language'
import { useSelector, shallowEqual } from 'react-redux'
import { StateType } from '@src/reduxToolkit/stateType'
import Demo01 from '@pages/intlDemo/demo01'
import Demo02 from '@pages/intlDemo/demo02'

type StoreSelector = {
    language: string
}

function IntlApp() {

    const storeSelector = (state: StateType) => ({
        language: state.language
    }) as StoreSelector

    const { language } = useSelector(storeSelector, shallowEqual) as StoreSelector

    return (
        <IntlProvider locale={language} messages={languageMessage[language] as LanguageJsonType}>
            <Demo01 />
            <hr />
            <Demo02 />
        </IntlProvider>
    )
}

export default IntlApp

文件解析:

  1. react-intl导入组件IntlProvider这是react-intl的核心组件,使用该组件包裹根组件的所有内容,该组件接收两个props,loacle是当前的语言,messages是当前语言对应的文案对象
  2. 我们从Redux中导入当前的language值并通过locale传入IntlProvider,并从language文件夹获取所有语言的文件对象集合,并根据Redux中的当前语言值获取当前语言的文案对象,通过messages传入IntlProvider
  3. 下面我们引用Demo01,Demo02两个文件分别用来切换语言和显示文案

实现语言切换

Demo01.tsx

import React, { memo, ChangeEvent } from 'react'
import { languageList, LanguageType } from '@src/language'
import { useSelector, shallowEqual, useDispatch } from 'react-redux'
import { StateType } from '@src/reduxToolkit/stateType'
import { changeLanguageAction } from '@src/reduxToolkit/states/language'

type StoreSelector = {
    language: string
}

function Demo02() {

    const dispatch = useDispatch()

    const storeSelector = (state: StateType) => ({
        language: state.language
    }) as StoreSelector

    const { language } = useSelector(storeSelector, shallowEqual) as StoreSelector

    const handleChangeLanguage = (e: ChangeEvent<HTMLSelectElement>) => {
        if (e.target.value !== language) {
            dispatch(changeLanguageAction(e.target.value as LanguageType))
        }
    }

    return (
        <div>
            <select onChange={handleChangeLanguage} value={language}>
                {
                    languageList.map((item) => (
                        <option key={item.value} value={item.value}>{item.label}</option>
                    ))
                }
            </select>
        </div>
    )
}

export default memo(Demo02)

文件解析:

  1. 导入languageList和redux中的language,显示选择框并根据当前的Redux值给select赋初始值
  2. 选择语言后,使用dispatch修改redux中的language值

组件中使用语言

import React, { memo } from 'react'
import { useIntl } from 'react-intl'

function Demo02() {

    const { formatMessage } = useIntl()

    return (
        <div>
            <h1>{formatMessage({ id: 'pageTitle' })}</h1>
            <h2>
                {formatMessage({ id: 'pageContent1' }, {
                    name: '杭州公安',
                    text: formatMessage({ id: 'pageTitle' })
                })}
            </h2>
        </div>
    )
}

export default memo(Demo02)

文件解析:

  1. 我们使用文案时,需要一个核心方法formatMessage,该方法通过react-intl中的useIntl返回
  2. formatMessage方法接收两个参数,第一个参数是一个对象,对象中设置属性id的值,formatMessage就会根据id的值,返回当前语言文件中对应的具体文案
  3. formatMessage的第二个参数也是一个对象,对象中传入的是id对应文案中变量的值,对象的key对应文案中使用{}包裹的变量,value是具体的替代文案,替代文案也可以传入其他的文案,如同上面text所示

测试代码演示

至此我们就实现了使用React-Intl库配合Redux实现语言的实时切换,下面是代码演示React-Intl + Redux 实现React项目国际化React-Intl + Redux 实现React项目国际化React-Intl + Redux 实现React项目国际化