hzinfo-i18n-router-loader v1.0.10
hzinfo-i18n-router-loader
一键式实现项目的国际化 语言包 处理的过程,vue2的loader
HTML
<div>你好,世界</div>
资源文件
//zh_md5
module.exports = {
"a018615b35588a01": "你好,世界" //key是根据中文生成16的MD5
}
// zh_md5_key_route_map
// 如果 路由下有该文件的key 会自动匹配生成
module.exports = {
"/demo/list": [
"a018615b35588a01"
],
"/demo/about": [
]
};
页面在中文下展示为
你好世界
在英文下展示为
Hello, world
介绍
该loader的主要目的是将国际化资源与项目代码分离维护,这样我们去查找对应文案的时候更加简单方便,如上述demo所示,我们原文件中不需要去使用 <div>{{ $t('a018615b35588a01') }}</div>
的方式去显示声明国际化,通过loader自动进行替换完成我们的国际化工作,这样对开发查找页面文案之类带来了极大的方便。本组件还支持在vue的props中以及单独的js文件中进行国际化处理。
安装
npm i hzinfo-i18n-router-loader --save-dev
yarn add hzinfo-i18n-router-loader --dev
使用
本组件分为两部分,一部分是cli,目的是为了生成资源文件,一部分是loader,目的是为了替换项目中的中文为国际化的代码,我们最好在打包测试/上线前执行以下cli命令,生成资源文件,然后拷贝一份资源文件给翻译组进行各国语言的翻译,之后将资源文件配置到vue-18n@5.x上,再进行打包即可。
cli的使用
cli可使用的所有命令
i18n generate
自动抓取中文
项目根目录执行
npx i18n generate
对src目录下的vue/js/ts/tsx/jsx文件进行中文提取并生成国际化资源文件
### 跳过中文抓取
js文件和vue文件内被<i18n-ignore (跳过的内容) i18n-ignore> 包括的内容将不会进行抓取
使用实例
js语法
```bash
// <i18n-ignore
const str = "我的世界"
// i18n-ignore>
template内使用
<!-- <i18n-ignore -->
<div>我的世界</div>
<!-- i18n-ignore> -->
loader的使用
loader中需要对vue.config.js 的chainWebpack对象进行配置
chainWebpack: (config) => {
config.module
.rule('vue')
.use('hzinfo-i18n-router-loader')
.loader('hzinfo-i18n-router-loader')
.options({
localeFile: path.join(process.cwd(), './src/lang/zh_md5.js')
})
.end()
}
config.module
.rule('js')
.exclude
.add(/node_modules/)
// .add(/main.js/)
.end()
.use('hzinfo-i18n-router-loader')
.loader('hzinfo-i18n-router-loader')
.options({
localeFile: path.join(process.cwd(), 'src/lang/zh_md5.js')
})
.end()
main.js 修改子应用 这个方法
function storeTest(props) {
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value, prev) => {
console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev);
if (value.themeName) store.commit('SET_THEME_NAME', value.themeName);
if (value.fontSize) store.commit('SET_FONT_SIZE', value.fontSize);
if (value.colorName) store.commit('SET_COLOR_NAME', value.colorName);
if (value.languageData){
function setLang(obj){
const language = getStore({name:'language'})
console.log(language)
const zh = i18n.getLocaleMessage(language||'zh')
i18n.setLocaleMessage(language||'zh',{
...zh,
...obj,
})
}
setLang(value.languageData)
Vue.prototype.$forceUpdate()
}
},
true,
);
props.setGlobalState &&
props.setGlobalState({
ignore: props.name,
user: {
name: props.name,
},
});
}
jsonLang.js 与路由文件jsonAes.js 同级 修改package.json 的 script
"build": "vue-cli-service build && node jsonAes.js && node jsonLang.js",
jsonLang.js的代码
let fs = require('fs');
const path = require('path');
const { name } = require('./package');
const file = require('./node_modules/hzinfo-i18n-router-loader/lib/const')
let CryptoJS = require("crypto-js");
const i18nPath = path.join(process.cwd(), file.config.dir+file.config.file);
const zhMd5KeyRouteMap = require(i18nPath+'_key_route_map.js')
const zhMd5 = require(i18nPath+'.js')
// 初始化一个空对象,用于存储合并后的结果
let mergedResult = {};
console.log(zhMd5KeyRouteMap)
console.log(zhMd5)
// 遍历每个文件
const hasKeys = []
Object.keys(zhMd5KeyRouteMap).forEach(filePath => {
zhMd5KeyRouteMap[filePath].forEach(item=>{
mergedResult[`${name}${filePath}.${item}`] = zhMd5[item]
hasKeys.push(item)
})
});
// 路由未使用的剩余key直接存到子应用上面
Object.keys(zhMd5).filter(key=>!hasKeys.includes(key)).forEach(item=>{
mergedResult[`${name}.${item}`] = zhMd5[item]
})
console.log(mergedResult);
let key = '1234567890HZINFO1234567890ABCDEF';
let sRoutes = JSON.stringify(mergedResult);
key = CryptoJS.enc.Utf8.parse(key)
let encryptedData = CryptoJS.AES.encrypt(sRoutes, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
encryptedData = encryptedData.ciphertext.toString()
fs.writeFile('./dist/cdn/common/i18n.json', encryptedData, 'utf8', function (err) {
if (err){
console.log(err)
};
});
axios改动
- axios.interceptors.request.use中加入
// i18n 的 请求头标识传入
const language = getStore({name:'language'})
const langs = {
'zh':'zh_CN',
'en':'en_US'
}
config.headers["Hz-Language"] = langs[language]||language||'zh_CN' ;
原有lang文件的index文件移到i18n文件夹,并且进行修改解决js文件循环引用问题
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
import enLocale from '../lang/en'
import zhLocale from '../lang/zh'
function validatenull(val) {
if (typeof val == 'boolean') {
return false;
}
if (typeof val == 'number') {
return false;
}
if (val instanceof Array) {
if (val.length == 0) return true;
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true;
} else {
if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true;
return false;
}
return false;
}
const getStore = (params = {}) => {
const keyName = 'saber-'
let {
name,
debug
} = params;
name = keyName + name
let obj = {},
content;
obj = window.sessionStorage.getItem(name);
if (validatenull(obj)) obj = window.localStorage.getItem(name);
if (validatenull(obj)) return;
try {
obj = JSON.parse(obj);
} catch{
return obj;
}
if (debug) {
return obj;
}
if (obj.dataType == 'string') {
content = obj.content;
} else if (obj.dataType == 'number') {
content = Number(obj.content);
} else if (obj.dataType == 'boolean') {
content = eval(obj.content);
} else if (obj.dataType == 'object') {
content = obj.content;
}
return content;
}
Vue.use(VueI18n)
const Avue = window.AVUE;
const messages = {
en: {
...enLocale,
...elementEnLocale,
...Avue.locale.en,
},
zh: {
...zhLocale,
...elementZhLocale,
...Avue.locale.zh,
}
}
const i18n = new VueI18n({
locale: getStore({ name: 'language' }) || 'zh',
messages
})
export default i18n
index.html 去掉中文 loading样式稍微调整一下 去掉
.avue-home__loading {
height: 50px;
width: 50px;
}
.avue-home__main {
height: 100px;
user-select: none;
width: 100%;
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
国际化资源配置
此处一定要注意!!!否则是个坑。
国际化的配置一定一定要在所有逻辑之前,建议采用 i18n.js 文件单独配置,然后在入口文件最先引入这个文件即可! 要确保('@/i18n')这个路径可以取到i18n对象
我是将i18n文件单独放到i18n文件夹下的
注意
- vue的template中不要出現双引号与单引号重复包裹的字符串 例如:
<template slot-scope="{disabled,size}" slot="templateParamForm">
<el-input type="textarea" v-model="form.templateParam" :showWordLimit='true' maxlength="1000" :rows="5"
placeholder='请输入JSON格式字符串,例如 {"a":1,"b":2}' allow-auth="true"></el-input>
</template>
这样使用 将会造成打包以及启动项目 报Syntax Error: SyntaxError: Unexpected token 错误
- 建议字符串的连接用模板字符串方式,这样其中涉及到的一些动态参数也会自动生成 {0} {1} 这样的参数注入
- import 与import语句之间不要加入中文
例如:
import {getDeviceInstanceList} from '@/api/analysis/new'
const dayDic = [
{
dictKey: 0,
dictValue: '月底'
},
...new Array(31).fill(1).map((item, index) => {
return {
dictKey: index + 1,
dictValue: index + 1 + '日'
}
})
]
const monDic = new Array(12).fill(1).map((item, index) => {
return {
dictKey: index + 1,
dictValue: index + 1 + '月'
}
})
const hoursDic = new Array(24).fill(1).map((item, index) => {
return {
dictKey: index,
dictValue: index + '时'
}
})
import MyProject from '@/views/components/myProject'
- npx i18n generate时请手动删除之前生成的2个多语言文件
- 避免js文件与i18n文件夹下的index.js循环引用