Handwritten automated Prompter and Backfill Tool to achieve front-end internationalization (language switch)
The function requirements of front-end internationalization are as follows: select a language from the drop-down list, and then switch the language accordingly. Chinese and English are supported at first. (Currently, there are 10+ projects, which should be handled gradually)
Technical tools :(the project is vue project) vue-i18n, all Chinese needs to be put out and backfill
Difficulties: The project is old and large, manual lifting is too stupid and inefficient (and there are 10+ projects), so you need a tool that can automatically complete lifting and backfilling
Implementation outline:
- The usage of VUE-I18N
- Develop automated prompter and backfill tool analysis
- Difficulties encountered in promptings
- Backfill encountered difficulties
- Add a tool platform to make it easy for team members to use
1. Introduction to vuE-I18N
// import main.js
import i18n from './i18n'.new Vue({
i18n,
...
})
Copy the code
// i18n.js
import Vue from 'vue'
import iView from 'view-design'
import VueI18n from 'vue-i18n'
import zhView from 'view-design/dist/locale/zh-CN'
import enView from 'view-design/dist/locale/en-US'
import zh from './langs/zh.json'
import en from './langs/en.json'
Vue.use(VueI18n)
Vue.locale = () = > {}
const messages = {
en: Object.assign(enView, en), // Combine your Own English package with iView
zh: Object.assign(zhView, zh) // Combine your Own Chinese package with iView
}
let locale = localStorage.getItem('lan')
if(! locale) {if (navigator.language === 'zh-CN') { // Get the language of the browser
locale = 'zh'
} else {
locale = 'en'}}const i18n = new VueI18n({
locale: locale, // Set the language, if local storage is used, use local, not default 'en'
messages
})
Vue.use(iView, {
i18n: (key, value) = > i18n.t(key, value)
})
export default i18n
Copy the code
// The logic of the corresponding language switch button
<Select @on-change="languageChange" placeholder="Language" size="small">
<Option value="zh">Simplified Chinese</Option>
<Option value="en">English</Option>
</Select>
languageChange(lang) {
localStorage.setItem('lan', lang)
location.reload()
}
Copy the code
Vue-i18n is now configured, and also requires resources zh.json, en.json, and backfill
- After vue-i18n is registered, there is a global $T that handles the language switch
// json {" confirm ", "confirm "," cancel ", "cancel ",... } / / en. Json {" sure ", "confirm", "cancel", "cancel",... } // xx.vue backfill, vue-i18n after registration, there will be a global $t, handle the language switch<template>Originally written:<button>determine</button>After the backfill:<button>{{$t (' sure ')}}</button>
</template>
<script>
export default {
data() {
return {
msg: 'cancel'.// The original way of writing
msg: this.$t('cancel') / / after backfill}}}</script>
Copy the code
2. Developed automated prompter and backfill tool analysis
We also need resources zh.json, en.json, and backfill
The goal is to write a script, execute a command, such as LAN/SRC /views, to get zh.json, en.json in/SRC /views, and backfill it
Decoupling development consists of three steps:
-
There is a front-end tool platform (internal front-end tool platform setup) that can execute commands such as terminal input: eslint or webpack to execute the corresponding tool library
- The purpose is that the team members can use it easily, for example
npm i -g feTools
And thenfe lan /src/views
You can execute the script. Instead of using copy source files (copy is also bad for version management)
- The purpose is that the team members can use it easily, for example
-
The flash
Use the re match, get all the Chinese, and generate zh.json and en.json as follows
// zh.json { "Sure"."Sure"."Cancel"."Cancel". }// en.json is then sent to the product for translation, or translated by itself, and the result of translation is written to the corresponding value { "Sure".""."Cancel"."". }Copy the code
-
backfill
$(“); $(“); $(“)
- Such as:
<span>{{$t(' query ')}}</span>
/* Handles the template part of vue */ letVueStr = (source code).replace(re,word= > { if (zhJson[word.trim()]) { return "$t('" + word.trim() + "')" } return word }) Copy the code
- Such as:
3. Difficulties encountered in promptings
The regular expressions that match Chinese: /[\u4e00-\u9fa5]{1,}/g, but in fact many of them are not simple Chinese
Things are:
<span> HTTP content-type is in JSON format. The recommended format is </span> 2. <span> </span> 3. '< SPAN > Enterprise {{obj.name}} atlas </span>' <span> Enterprise {{obj.name}} atlas </span> 4. Filter comments (HTML comments, JS comments) // comments <! - comments -- >Copy the code
Analysis problem:
<span> HTTP content-type is in JSON format. The recommended format is </span> 2. <span> </span> 3. <span> enterprise {{obj. Name}} atlas </span> (consider the subsequent backfill) < span > HTTP {{$t (' ')}} the content-type {{$t (' adopt ')}} json {{$t (' format ')}}, {{$t (' suggestion using the format of the ')}} < / span > points more paragraphs are too ugly right example: <span>{{$t(' HTTP content-type: json ')}}</span> 4. Filter comment (HTML comment, JS comment) // comment /* comment * comment comment comment */ <! -- Comment --> <! - comments -- >Copy the code
The regular expression results: [\ u4e00 – \ u9fa5 | | | \ \ w s,,,. / / / * : : = () ()!!?? \ \ -] {1}
Regular expression analysis: Chinese + English digits + Spaces or newlines + some punctuation marks (<> and {} are filtered out, so you can’t write \S directly)
- The key point is: don’t assume that a single re will solve all the problems, because it matches English, so all English matches will be matched, you need to write a re to filter the results
Regular character string: < SPAN > Enterprise {{obj.name}} atlas </ SPAN > < SPAN > HTTP content-type is in JSON format. The recommended format is </ SPAN > // Comment <! - comments -- > regular expression: [\ u4e00 - \ u9fa5 | | | \ \ w s,,,. / / / * : : = () ()!!?? \ \ -] {1} matching results: a total of find 10 matches: Span Enterprise obj.name Atlas/SPAN SPAN The HTTP content-type is in json format. The recommended format is/SPAN // Comments! - comments - the source code: the function getChineseList (STR) {/ / get all the Chinese English mix Spaces and punctuation const re = / [\ u4e00 - \ u9fa5 | | | \ \ w s,,,. / / / * : : = () ()!! \? -] {1} / g return STR. The match (re). The map (e = > {e = e. rim () / / and English annotation to filter out the if (/ [\ u4E00 - \ u9FA5] /. The test (e) &&! /\/\/|\/\*--/.test(e)) return e }).filter(e => e) }Copy the code
Final summary:
- When you’re thinking about a problem, it’s easy to end up in a dead end thinking that one regular rule will solve all the problems. You can do more regex or filter the result of the regex
- The scene will be complex, and there will still be some overextraction (e.g. <> Angle brackets in comments), but not underextraction. It’s hard to be 100% accurate, at least not less
4. Difficulties encountered in backfilling
There are too many cases to handle in one session
Things are:
-
Elements within the template > tag a replacement: < / span > < span > query replace = > < span > {{$t (‘ query ‘)}} < / span >
Solution:
// There are some complications:< < span > query/span> / query XXX </< a > information/span> / query {{obj.name}} information </span> // Additional re processing is required here // Actual processing const getZh = / [\ u4e00 - \ u9fa5 | | | \ \ w s,,,. / / / * : : = () ()!!? \? -] {1} / g // Match Both Chinese and English with some punctuation letVueStr = (source code).replace(getZh,word= > { if (zhJson[word.trim()]) { // zhJson is the map taken by the prompter function return "$t('" + word.trim() + "')" } return word }) const textRe = / >. *? <|\n\$.*? \n/g //. Will not match "\n", add a question mark, non-greedy mode vueStr = vueStr.replace(textRe, e= > { if (e.slice(0.2) = = ='> $') { if (/ / {{.test(e)) { $t(' query '){{obj. Name}}$t(' info ')< e = e.replace(/\$t\(.*? \)/g.res= > { return '{{' + res + '}} '})}else { return ` > {{${e.slice(1, e.length - 1)}}} < `}}else if (e[0= = ='\n') { return `\n{{${e.slice(1, e.length - 1)}}}\n` } return e }) Copy the code
-
tag props + colon :placeholder=” task ID”; placeholder= > :placeholder=”$t(‘ task ID’)”
Now, placeholder=” task ID” has become placeholder=”$t(‘ task ID’)”
Solution:
const vueRe = /[a-zA-Z|="'\>\$t\(]{1,}/g / / processing within the props, coupled with a colon Such as: placeholder = "$t (' task ID)" = > : placeholder = "$t (' task ID ')" let propsVue = vueStr.match(vueRe).map(e= > { if (e.includes('="$t(')) return e }).filter(e= > e) const map = new Map(a)/ / to heavy propsVue = propsVue.map(e= > { // Remove the weight, avoid extra colons const val = e.split('=') [0] if(! map.get(val)) { map.set(val,1) return val } }).filter(e= > e) propsVue.forEach(e= > { vueStr = vueStr.replace(new RegExp(e, 'g'), word= > { if (word.includes(':')) { return word } else { return ':' + word } }) }) Copy the code
Abnormal conditions:
- It is difficult to clean up some exceptions in the regular process. It is much easier to clean up exceptions in the regular process
1.:label-width='70' 2.In some ternary expressions, it appears: xx?'$t('Open the') ' : '$t('Shut down') 'You need to get rid of the quotes'$t('Open the') ' => $t('open') / /!!!!!!!!!! Wash some of the dirt off vueStr = vueStr.replace(new RegExp(/ : : /.'g'), word= > ':') $t(' open ') => $t(' open ') vueStr = vueStr.replace(new RegExp(/'\$t\('/.'g'), word= > "$t('") vueStr = vueStr.replace(new RegExp(/ '\]'.'g'), word= > "')") Copy the code
-
Substitution in <script> :(and possibly template string concatenation)
/ * js: * the columns: [{(old) title: 'task name, (new) title: this. $t (" task name "), the key: 'error ${res.message}' '${this.$t(' error ')}${res.message}' */Copy the code
Solution:
function escapeRegExp (str) { // The function of escapeRegExp is: for example, if STR is' before change (', then new RegExp(STR) will report an error, so it needs to escape to: 'before change \\(' return str.replace(/[\(\)]/g.'\ \ $&')}// Process non-template strings let jsStr = data[1] const zhJsonNew = {} Object.keys(zhJson).forEach(e= > { zhJsonNew[` '${e}'`] = e // Here take Chinese + double quotes string for example: "message" zhJsonNew[`"${e}"`] = e // Here take Chinese + single quote string for example: 'message' }) Object.keys(zhJsonNew).forEach(e= > { // The function of escapeRegExp is as follows: for example, e is' before change (', in this case, new RegExp(e) will report an error, which needs to be escaped to: 'before change \\(' jsStr = jsStr.replace(new RegExp(escapeRegExp(e), 'g'), word= > { return 'this.$t(' + word + ') '})})// Process template strings const zhJsonTpl = {} Object.keys(zhJson).forEach(e= > { zhJsonTpl[e] = e // Only Chinese strings are taken here (to match template strings) e.g. message }) jsStr = jsStr.replace(/`.*`/g.word= > { Object.keys(zhJsonTpl).forEach(e= > { // The function of escapeRegExp is as follows: for example, e is' before change (', in this case, new RegExp(e) will report an error, which needs to be escaped to: 'before change \\(' word = word.replace(new RegExp(escapeRegExp(e), 'g'), zh= > { return "${this.$t('" + zh + "')}"})})return word }) Copy the code
5. Join a tool platform that team members can easily use
Add tool platform: build internal front-end tool platform
After the tool platform is installed :(actual usage 🙂
-
Terminal input: FE LAN parameter 1 [parameter 2:replace or R, parameter 3: noOutput or n]]
Parameter 1 (mandatory) : path to the cue
- Directories are supported as well as files
- Recommend absolute path, can use editor copy out. Relative paths work, but make sure you input them correctly
Parameter 2 (optional) : whether to backfill, can be abbreviated as r
- If you do, you can only fill in replace or R.
- Parameter 3 (optional. Parameter 2 is required to take effect): Function: Backfill only. Json file is not output
Code word is not easy, little praise ~