日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

作者:qiangwu,騰訊CSIG Web開發(fā)工程師

i18n-helper-cli 是什么

i18n-helper-cli 是一個(gè) Web 國際化整體解決方案,包含自動(dòng)包裹詞條,提取詞條, 翻譯詞條,詞條翻譯統(tǒng)計(jì),節(jié)省人力預(yù)估統(tǒng)計(jì),網(wǎng)頁多語言顯示異常檢測(cè)(Coming soon)等功能。可以大大減低開發(fā),測(cè)試,翻譯各個(gè)角色的人力成本,減少重復(fù)勞動(dòng),低級(jí)錯(cuò)誤。

超實(shí)用!一個(gè)命令搞定 Web 國際化

 

為什么需要 i18n-helper-cli

Web 國際化流程

簡(jiǎn)單來說可以分為以下 5 個(gè)步驟

  1. 【選型】多語言框架選型(這里不深究,不在此篇范圍),我們選定 i18next,18n-helper-cli 對(duì)多語言框架并不限制
  2. 【開發(fā) - 包裹詞條】從上面這步驟,我們知道需要把詞條包裹起來 e.g 你好 => t('你好')
  3. 【開發(fā) - 提取詞條】把上一步中包裹的詞條 copy 到翻譯文件中
  4. 【翻譯 - 翻譯】翻譯把詞條翻譯好,填入翻譯文件
  5. 【測(cè)試 - 測(cè)試頁面】開發(fā)提交測(cè)試后,對(duì)多語言頁面進(jìn)行測(cè)試

問題

通過上面 5 步,可以完成站點(diǎn)國際化。大多數(shù)場(chǎng)景大家就是這么做的,但這里充斥著大量人工勞動(dòng),大量人工勞動(dòng)意味著重復(fù)低效,出錯(cuò)幾率提高。讓我們從以下三個(gè)階段分析下這些問題

  • 【開發(fā)階段】 人工操作包裹和提取詞條耗時(shí)長(zhǎng),但對(duì)個(gè)人無任何成長(zhǎng)。如果是【全新開發(fā)】的站點(diǎn),大家還可以耐著性子包裹詞條,提取詞條,但如果是【存量修改】及對(duì)已有的站點(diǎn)做國際化,而且這里的頁面幾十上百,甚至更多,這里的包裹詞條,提取詞條的工作量會(huì)讓人崩潰 遺漏包裹,提取詞條(代碼多,詞條隱藏在各個(gè)文件的各個(gè)角落里。。。) 提取詞條后,運(yùn)行多語言界面無法看到效果,需要等到翻譯返回
  • 【翻譯階段】 翻譯耗時(shí)長(zhǎng)遺漏翻譯
  • 【測(cè)試階段】 多語言頁面測(cè)試每個(gè)都要測(cè),耗費(fèi)大量時(shí)間遺漏測(cè)試某個(gè)多語言頁面

所以這里最大的問題是上面這些工作都需人工操作,問題清楚了,那接下來我們要做的就是把這些人工操作能夠交給機(jī)器,實(shí)現(xiàn)自動(dòng)化,提高效率,降低出錯(cuò)幾率。

解決方案

先上結(jié)論,i18n-helper-cli 可以很好的解決上述問題。

原理

整體方案

  • 【詞條包裹】通過對(duì)代碼進(jìn)行編譯,得到AST,找到符合條件(中文,或者其他語言,可配置)的 Node,根據(jù)配置創(chuàng)建新 Node,替換老的 Node
  • 【詞條提取】同上,也是AST, 找到的符合條件的詞條以及原代碼已經(jīng)包裹的詞條會(huì)被一起提取,根據(jù)配置寫入文件
  • 【詞條翻譯】 從源文件翻譯:如果有一份翻譯詞庫(這里有常見的翻譯),我們提取出來的未翻譯詞條在這里有,我們就可以直接從這里翻譯機(jī)器翻譯:未翻譯詞條調(diào)用云服務(wù)實(shí)現(xiàn)翻譯(這里我們用的是騰訊云的翻譯服務(wù))
  • 【網(wǎng)頁多語言顯示異常檢測(cè)】提供一份頁面 url 列表,用 Cypress 進(jìn)行截圖,調(diào)用騰訊云 OCR 服務(wù)提取圖片文字,進(jìn)行對(duì)比,假設(shè)我們有個(gè)叫你好的詞條翻譯成 en 為Hello,如果我們通過 OCR 得到的是Hel,那么我們可以認(rèn)為這個(gè)頁面有問題(Coming soon)
  • 【統(tǒng)計(jì)】 翻譯詞條統(tǒng)計(jì):根據(jù)當(dāng)前語言下未翻譯詞條數(shù) / 詞條總數(shù)減低人工耗時(shí)預(yù)估:根據(jù)包裹,提取,翻譯詞條數(shù)預(yù)估

包裹詞條方案詳解

接下來我們?cè)敿?xì)分析下詞條包裹的方案。我們要實(shí)現(xiàn)的是類似你好 => t('你好'),所以:

  1. 找到你好
  2. 替換成t('你好')

哈哈,剛說的就像網(wǎng)上的經(jīng)典問題: 如何把大象放到冰箱?

回答:先打冰箱門,然后把大象放進(jìn)去,在關(guān)上冰箱門

聽起來沒問題,好像很有道理的樣子,但沒有任何實(shí)際價(jià)值。言歸正傳,我們來探討下實(shí)際解決方案:

方案 1 - 正則

針對(duì)匹配到中文,這里我們第一個(gè)想法應(yīng)該就是正則表達(dá)式了。/[u4e00-u9fa5]可以匹配中文。至此,我們完成了第一步找到需要包裹的文字。接下來就是把包裹上了'你好'.replace(/([u4e00-u9fa5]+)/gi,'t('$1')'),搞定。慢著,真的這么簡(jiǎn)單嗎,想想我們真實(shí)代碼的情況,注釋,各種復(fù)雜的模板字符串,換行等等。這意味著這個(gè)正則到后面會(huì)巨復(fù)雜,到后面會(huì)面對(duì)一個(gè)晦澀難懂,難于維護(hù)的正則表達(dá)式。

另外還有個(gè)很蛋疼的事情,比方說我們的調(diào)試,上報(bào)等等代碼,如console.log('不需要提取'),怎么辦?感覺腦子不夠用了。

方案 2 - AST

我們希望只匹配我們想要的詞條。比如下如下代碼,我們預(yù)期匹配你好(注釋和 console.log 的里的都不需要),如果有個(gè)方式只給到我們你好,然后我再判斷它是不是包含中文,再包裹就再好不過了。

// 這是一段注釋
const word = '你好';
console.log('世界');

有沒有這樣的好事?答案是還真有。是時(shí)候上這張神圖了

超實(shí)用!一個(gè)命令搞定 Web 國際化

 

Babel 的工作流程主要分為以下 3 個(gè)階段:

  1. Parse階段:詞法分析 & 語法分析
  2. Transform階段:生成AST,抽象語法樹
  3. Generate階段:生成代碼

這里我們著重說下Transform階段,AST 處理的核心要素:

  • babel-core 通過 transform 將代碼字符串轉(zhuǎn)換為 AST 樹;
  • babel-types 一個(gè)強(qiáng)大的用于處理 AST 節(jié)點(diǎn)的工具庫,它包含了構(gòu)造、驗(yàn)證以及變換 AST 節(jié)點(diǎn)的方法;
  • visitor 當(dāng) Babel 處理 Node 時(shí),以訪問者的形式獲取節(jié)點(diǎn)信息,并進(jìn)行相關(guān)操作。這種方式是通過一個(gè) visitor 對(duì)象來完成,在 visitor 對(duì)象中定義了對(duì)于各種節(jié)點(diǎn)類型函數(shù),我們可以通過不同類型節(jié)點(diǎn)做出相應(yīng)處理。

通過上述要素,我們既可以完成對(duì) AST 的修改。下面我們看下這里的核心代碼:

return {
      visitor: {
        StringLiteral(path: NodePath<tt.StringLiteral>) {
          let { value } = path.node;
          value = replaceLineBreak(value);

          if (needWrap(wrapCharacter, value)) {
            let newNode = t.CallExpression(t.Identifier(T_WRAppER), [
              combine(value),
            ]);

            path.replaceWith(newNode);
          }
        },
        CallExpression(path: NodePath<tt.CallExpression>) {
          switch (path.node.callee.type) {
            case 'MemberExpression': {
              const excludeFuncName = i18nConf.parsedExcludeWrapperFuncName;
              if (excludeFuncName.length > 0) {
                const names: string[] = [];
                const me = path.node.callee as tt.MemberExpression;
                getName(me, names);
                const MEName = names.reverse().join('.');
                if (excludeFuncName.includes(MEName)) {
                  path.skip();
                }
              }
              break;
            }
            default:
              break;
          }
        },
}

針對(duì)上面我們?cè)V求的例子,當(dāng)我們得到 AST 后

  • // 這是一段注釋 - 實(shí)際上會(huì)被解析成 CommentLine 類型,我們的代碼不處理,所以該什么樣還是什么樣
  • const word = '你好' - 你好被解析為StringLiteral,判斷是中文,這時(shí)候我們?cè)僦匦聵?gòu)造一個(gè)新的節(jié)點(diǎn),替換老的及完成了包裹
  • console.log('世界') - console.log被解析為CallExpression,我們可以通過在配置文件中配置需要忽略的包裹的方法,如果解析到的方法名在配置中,則忽略掉,這樣就不會(huì)出來這里的世界

至此,我們即可完成我們的訴求,完美的對(duì)符合我們需要的詞條就行包裹。

題外話 - 如何編寫自己的 babel 插件

通過上面 AST 的方案,我們可以看得出這里的功能很強(qiáng)大,業(yè)界eslint,prettier,webpack等等都是通過對(duì)源碼進(jìn)行分析,轉(zhuǎn)換,生成實(shí)現(xiàn)各種各樣的功能。

我們可以開發(fā)自己的插件,去做各種有意思的事情,比如說代碼埋點(diǎn),國際化方案等等。看到這里我想大家一定會(huì)有個(gè)問題:

  1. 上面說的代碼轉(zhuǎn) AST 時(shí)的各種類型,我們?cè)趺粗擂D(zhuǎn)成什么類型了呢?

答:https://astexplorer.net/

超實(shí)用!一個(gè)命令搞定 Web 國際化

 

2.另外這些類型如何構(gòu)造新的節(jié)點(diǎn)?

答:https://babeljs.io/docs/en/babel-types

超實(shí)用!一個(gè)命令搞定 Web 國際化

 

如何使用 i18n-helper-cli

實(shí)例

請(qǐng)參考 example

安裝

注意:請(qǐng)確保 Nodejs 版本大于 14!!!

# npm 安裝
npm install i18n-helper-cli -D
# yarn 安裝
yarn add i18n-helper-cli —dev

快捷使用

  1. 在項(xiàng)目根目錄下生成 i18n.config.json 文件
# 交互式命令行
i18n-helper init
# 生成默認(rèn)配置文件,具體參見【配置說明】( 推薦大家用這個(gè)哈,交互方式的的后面加了不少配置還沒來得及補(bǔ)齊)
i18n-helper init -y
  1. 包裹 & 提取 & 翻譯 & 統(tǒng)計(jì)
# 包裹 & 提取 & 翻譯 & 統(tǒng)計(jì) i18n.config.json 中 srcPath 文件中的中文詞條
i18n-helper scan -wetc
  1. 切換 Cli 語言
# cli 默認(rèn)為中文,支持語言切換,目前支持zh & en
i18n-helper switch en

命令詳情

# 包裹 & 提取 & 翻譯 & 統(tǒng)計(jì) i18n.config.json 中 srcPath 文件中的中文詞條
# w:wrap e:extract t:translate tm: translate machine c:count
# l:language
# 這 5 個(gè)操作可以隨意組合 e.g. i18n-helper scan -we 則只會(huì)翻譯 & 提取
i18n-helper scan -wetc
i18n-helper scan -we -tm -c
# 包裹 & 提取 & 翻譯 & 統(tǒng)計(jì) 指定路徑,指定語言內(nèi)符合規(guī)則的詞條
# e.g i18n-helper scan -wetc -l en ./src/test/index.js
i18n-helper scan -wetc -l [language] [filepath]
i18n-helper scan -we -tm -c -l [language] [filepath]

# 包裹 i18n.config.json 中 srcPath 文件中的中文詞條
i18n-helper wrap
i18n-helper scan -w
# 包裹指定文件中的中文詞條
i18n-helper wrap [filepath]
i18n-helper scan -w [filepath]

# 提取 i18n.config.json 中 srcPath 文件中的中文詞條到所有配置語言文件
i18n-helper extract
i18n-helper scan -e
# 提取指定文件中文詞條到指定語言文件
# e.g i18n-helper extract -l en ./src/test/index.js
i18n-helper extract -l [language] [filepath]
i18n-helper scan -e -l [language] [filepath]

# 翻譯 i18n.config.json 中配置翻譯文件詞條, -m 騰訊翻譯君機(jī)器翻譯
# 從翻譯源文件文件中翻譯
i18n-helper translate
i18n-helper scan -t
# 騰訊翻譯君自動(dòng)翻譯
i18n-helper translate -m
i18n-helper scan -tm
# 翻譯指定語言
# 從翻譯源文件文件中翻譯
i18n-helper translate [language]
i18n-helper scan -t -l [language]
# 騰訊翻譯君自動(dòng)翻譯指定語言文件
i18n-helper translate -m [language]
i18n-helper scan -tm -l [language]

# 統(tǒng)計(jì) i18n.config.json 中翻譯文件的翻譯情況
i18n-helper count
i18n-helper scan -c
# 統(tǒng)計(jì)指定語言翻譯文件的翻譯情況,多個(gè)語言用,分隔
i18n-helper count [language]
i18n-helper scan -c -l [language]

配置詳情

超實(shí)用!一個(gè)命令搞定 Web 國際化

 

module.exports = {
  // cli 語言
  cliLang: 'zh',
  // 項(xiàng)目類型:react | vue | js
  projectType: '[react]',
  // 默認(rèn)包裹和提取詞條的目錄
  srcPath: './',
  // 掃描文件格式
  fileExt: 'js,ts,tsx',
  // 包裹的字符集,下面是中文
  wrapCharacter: '[u4e00-u9fa5]',
  // 包裹詞條的名字
  wrapperFuncName: 't',
  // 忽略掉包裹的方法,多個(gè)用,分隔
  excludeWrapperFuncName: 'console.log,console.error',
  // jsx中的文字包裹方式,true用<trans></trans>, false用【wrapperFuncName】的value包裹
  jsx2Trans: false,
  // 當(dāng)文件需要翻譯時(shí)引入的文件
  importStr: `import {t} from './i18n;';n`,
  // 排除目錄,此目錄下的不會(huì)不會(huì)執(zhí)行包裹和提取詞條操作
  exclude: 'node_modules,dist,git',
  // 翻譯詞條目錄
  localeDir: './locales',
  // 翻譯語種
  languages: 'zh,en',
  // 源語言
  sourceLanguage: 'zh',
  // 翻譯詞條文件名
  transFileName: 'translation',
  // 翻譯詞條文件格式: json, po
  transFileExt: 'json',
  // 翻譯詞庫目錄(自動(dòng)翻譯目錄)
  targetTransDir: './translations',
  // 翻譯詞庫文件名
  targetTransFile: 'sourceTranslation.json',
  // 騰訊云 secretId
  secretId: '',
  // 騰訊云 secretKey
  secretKey: '',
};

未來規(guī)劃

  • [ ] 網(wǎng)頁多語言顯示異常檢測(cè)
  • [ ] 豐富提取文件(po, csv, Excel 等等)
  • [ ] 增加 git 模式,針對(duì)當(dāng)前改動(dòng)的文件才轉(zhuǎn) AST 包裹,提取
  • [ ] 詞條提取 cleanMode,目前如果代碼中沒有這個(gè)詞條了,提取后的文件依然會(huì)有

其他

源碼

https://github.com/wuqiang1985/i18n-helper

NPM 包

https://www.npmjs.com/package/i18n-helper-cli

目前還在完善中,歡迎大家試用,大家有問題可以提 issue。

分享到:
標(biāo)簽:國際化 Web
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定