目录
关于如何实现国际化,有很多方法,比如 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 (
- <div className="acss-llcihc" onClick={() => changeLanguage()}>
- <span className={selClassZH}>中span>
- <span className={selClassEN}>Enspan>
- div>
- )
- }
-
- 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 'lodash'
-
- 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(
- <Provider store={store}>
- <BrowserRouter basename={process.env.PUBLIC_URL}>
- <App />
- BrowserRouter>
- Provider>,
- 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 (
- <div>
- <ConfigProvider
- locale={locale}
- >
- <App>
- <Route exact path="/" component={Welcome} />
- App>
- ConfigProvider>
- div>
- )
- }
-
- 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'
- import SelectLang from '../components/SelectLang'
- const Welcome = () => {
- const commonStore = useSelector(state => state.commonStore)
- return (
- <div className="welcome">
- <SelectLang />
- <h2 className="welcome-text">{formatMessage('languageObj.welcomeLocale.welcome', commonStore)}h2>
- div>
- )
- }
-
- export default Welcome
如果遇到不能动态刷新,尝试可以一下 store.subscribe
- import store from './redux'
- store.subscribe(() => {
- const state = store.getState()
- const language = state.commonStore.language
- // ...
- })