目录
一、Redux准备工作
commonTypes.js
commonActions.js
commonReducer.js
rootReducer.js
二、然后定义SelectLang组件
index.js
index.less
三、创建语言包
welcomeLocale.js
index.js
四、使用
react的入口文件
App.js
welcome.js
附
关于如何实现国际化,有很多方法,比如 vue-i18n 、 react-i18next 、 umi 中的 useIntl 等等,网上有很多的资料可以参看,今天不想使用这些库,于是乎打算自己写一个,期初设计是写两个语言文件,每次改变时把语言标识存 localStorage 中,然后刷新页面获取对应的语言文件,但是,本着提供良好的用户体验原则,否则了这一想法。于是想到了使用全局状态容器 Redux ,这样就可以在不刷新页面的情况下更新页面。尝试一下,效果还可以。以React为例,Vue实现也类似,具体代码如下:
为例防止文件过大,对Redux进行了拆分目录如下:
// commonTypes.js
export const SET_LANGUAGE = 'set_language'
export const SET_LANGUAGE_OBJ = 'set_language_obj'
// commonActions.js
import {
SET_LANGUAGE,
SET_LANGUAGE_OBJ
} from '../actionTypes/commonTypes'
export const setLanguage = payload => {
return {
type: SET_LANGUAGE,
payload
}
}
export const setLanguageObj = payload => {
return {
type: SET_LANGUAGE_OBJ,
payload
}
}
// commonReducer.js
import {
SET_LANGUAGE,
SET_LANGUAGE_OBJ
} from '../actionTypes/commonTypes'
let lang = 'zh_CN'
if (localStorage.getItem('language') === 'zh_CN' ||
localStorage.getItem('language') === 'en_US'
) {
// 防止莫名出现其他值
lang = localStorage.getItem('language')
}
const initState = {
language: lang,
languageObj: {}
}
const commonReducer = (state = initState, action) => {
const { type, payload } = action
switch (type) {
case SET_LANGUAGE:
return {
...state,
language: payload
}
case SET_LANGUAGE_OBJ:
return {
...state,
languageObj: payload
}
default:
return {
...state
}
}
}
export default commonReducer
// rootReducer.js
import commonReducer from './commonReducer'
const rootReducer = {
commonStore: commonReducer
}
export default rootReducer
// index.js
import { createStore, combineReducers } from 'redux'
import rootReducer from './reducers/rootReducer'
const store = createStore(combineReducers(rootReducer))
export default store
样式参考的antd,目录如下:
// index.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { setLanguage } from '../../redux/actions/commonActions'
import './index.less'
const SelectLang = props => {
const language = useSelector(state => state.commonStore.language)
const dispatch = useDispatch()
const changeLanguage = () => {
let lang = language === 'zh_CN' ? 'en_US' : 'zh_CN'
localStorage.setItem('language', lang)
dispatch(setLanguage(lang))
}
let selClassZH = language === 'zh_CN' ? 'acss-1nbrequ acss-1n10ay4' : 'acss-1nbrequ acss-3ew1dt'
let selClassEN = language === 'en_US' ? 'acss-1nbrequ acss-1n10ay4' : 'acss-1nbrequ acss-3ew1dt'
return (
changeLanguage()}>
中
En
)
}
export default SelectLang
// index.less
.acss-llcihc {
position: relative;
cursor: pointer;
width: 1.3rem;
height: 1.3rem;
display: inline-block;
.acss-1nbrequ {
position: absolute;
font-size: 1.3rem;
line-height: 1;
color: #ffffff;
}
.acss-1n10ay4 {
left: -5%;
top: 0;
z-index: 1;
color: #ffffff;
-webkit-transform: scale(0.7);
-moz-transform: scale(0.7);
-ms-transform: scale(0.7);
transform: scale(0.7);
transform-origin: 0 0;
}
.acss-3ew1dt {
right: -5%;
bottom: 0;
z-index: 0;
-webkit-transform: scale(0.5);
-moz-transform: scale(0.5);
-ms-transform: scale(0.5);
transform: scale(0.5);
transform-origin: 100% 100%;
}
}
防止文件过大,可以按类别穿件文件,目录如下:
// welcomeLocale.js
module.exports = {
welcome: 'Welcome To System'
}
// index.js
import _ from 'loadsh'
const modulesFilesen = require.context('./en', true, /\.js$/)
const modulesen = modulesFilesen.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^.\/(.*)\.js/, '$1')
const value = modulesFilesen(modulePath)
modules[moduleName] = value
return modules
}, {})
const modulesFileszh = require.context('./zh', true, /\.js$/)
const moduleszh = modulesFileszh.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^.\/(.*)\.js/, '$1')
const value = modulesFileszh(modulePath)
modules[moduleName] = value
return modules
}, {})
// 动态读取文件并组合到一个对象中
export const languageObj = {
zh_CN: moduleszh,
en_US: modulesen
}
// 判断语言包中是否存在该字段,没有返回空
export const formatMessage = (titles, storeState) => {
let titleList = titles.split('.')
let resObj = _.cloneDeep(storeState)
for (let index = 0; index < titleList.length; index++) {
const element = titleList[index]
if (resObj[element]) {
resObj = resObj[element]
} else {
resObj = ''
}
}
return resObj.toString()
}
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import './index.less'
import App from './App'
import { languageObj } from './locale'
import store from './redux'
import { setLanguageObj } from './redux/actions/commonActions'
import { Provider } from 'react-redux'
const state = store.getState()
const language = state.commonStore.language
if (language === 'zh_CN') {
store.dispatch(setLanguageObj(languageObj['zh_CN']))
}
if (language === 'en_US') {
store.dispatch(setLanguageObj(languageObj['en_US']))
}
ReactDOM.render(
,
document.getElementById('root')
)
// App.js
import React, { useEffect, useState } from 'react'
import { Route, withRouter, Redirect } from 'react-router-dom'
import { ConfigProvider, App } from 'antd'
import { useSelector, useDispatch } from 'react-redux'
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
import zh_CN from 'antd/locale/zh_CN'
import en_US from 'antd/locale/en_US'
import { setLanguageObj } from './redux/actions/commonActions'
import { languageObj } from './locale'
import Welcome from './welcome'
import './App.less'
dayjs.locale('zh-cn')
const AppPage = () => {
const dispatch = useDispatch()
const [locale, setLocal] = useState({})
const languageState = useSelector(state => state.commonStore.language)
useEffect(() => {
if (languageState === 'zh_CN') {
dayjs.locale('zh-cn')
setLocal(zh_CN)
}
if (languageState === 'en_US') {
dayjs.locale('en')
setLocal(en_US)
}
}, [languageState])
useEffect(() => {
dispatch(setLanguageObj(languageObj[languageState]))
}, [locale])
return (
)
}
export default withRouter(AppPage)
formatMessage 方法参数:
languageObj.welcomeLocale.welcome
commonStore :具体store, 可在formatMessage方法优化一下,就可以不用传了,自己处理尝试吧。
// welcome.js
import React from 'react'
import { useSelector } from 'react-redux'
import { formatMessage } from '../locale'
const Welcome = () => {
const commonStore = useSelector(state => state.commonStore)
return (
{formatMessage('languageObj.welcomeLocale.welcome', commonStore)}
)
}
export default Welcome
如果遇到不能动态刷新,尝试可以一下 store.subscribe
import store from './redux'
store.subscribe(() => {
const state = store.getState()
const language = state.commonStore.language
// ...
})