From ad42d3fe2037ace775c081c5dcb0c9dd122e23c3 Mon Sep 17 00:00:00 2001 From: zhaoyingbo Date: Tue, 11 Oct 2022 15:25:18 +0800 Subject: [PATCH] inital --- .gitignore | 1 + CHANGELOG.md | 23 ++ README.md | 122 ++++++++ dist/const.js | 46 +++ dist/const.js.map | 1 + dist/export.js | 40 +++ dist/export.js.map | 1 + dist/extract/extract.js | 266 ++++++++++++++++++ dist/extract/extract.js.map | 1 + dist/extract/file.js | 74 +++++ dist/extract/file.js.map | 1 + dist/extract/findChineseText.js | 399 ++++++++++++++++++++++++++ dist/extract/findChineseText.js.map | 1 + dist/extract/getLangData.js | 72 +++++ dist/extract/getLangData.js.map | 1 + dist/extract/replace.js | 233 +++++++++++++++ dist/extract/replace.js.map | 1 + dist/import.js | 75 +++++ dist/import.js.map | 1 + dist/index.js | 146 ++++++++++ dist/index.js.map | 1 + dist/init.js | 65 +++++ dist/init.js.map | 1 + dist/mock.js | 128 +++++++++ dist/mock.js.map | 1 + dist/sync.js | 113 ++++++++ dist/sync.js.map | 1 + dist/translate.js | 195 +++++++++++++ dist/translate.js.map | 1 + dist/unused.js | 101 +++++++ dist/unused.js.map | 1 + dist/utils.js | 295 +++++++++++++++++++ dist/utils.js.map | 1 + package.json | 52 ++++ public/extract.gif | Bin 0 -> 4990292 bytes src/const.ts | 44 +++ src/export.ts | 45 +++ src/extract/extract.ts | 293 +++++++++++++++++++ src/extract/file.ts | 78 ++++++ src/extract/findChineseText.ts | 421 ++++++++++++++++++++++++++++ src/extract/getLangData.ts | 78 ++++++ src/extract/replace.ts | 239 ++++++++++++++++ src/import.ts | 75 +++++ src/index.ts | 141 ++++++++++ src/init.ts | 66 +++++ src/mock.ts | 115 ++++++++ src/sync.ts | 123 ++++++++ src/translate.ts | 184 ++++++++++++ src/unused.ts | 104 +++++++ src/utils.ts | 315 +++++++++++++++++++++ tsconfig.json | 18 ++ 51 files changed, 4800 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 dist/const.js create mode 100644 dist/const.js.map create mode 100644 dist/export.js create mode 100644 dist/export.js.map create mode 100644 dist/extract/extract.js create mode 100644 dist/extract/extract.js.map create mode 100644 dist/extract/file.js create mode 100644 dist/extract/file.js.map create mode 100644 dist/extract/findChineseText.js create mode 100644 dist/extract/findChineseText.js.map create mode 100644 dist/extract/getLangData.js create mode 100644 dist/extract/getLangData.js.map create mode 100644 dist/extract/replace.js create mode 100644 dist/extract/replace.js.map create mode 100644 dist/import.js create mode 100644 dist/import.js.map create mode 100644 dist/index.js create mode 100644 dist/index.js.map create mode 100644 dist/init.js create mode 100644 dist/init.js.map create mode 100644 dist/mock.js create mode 100644 dist/mock.js.map create mode 100644 dist/sync.js create mode 100644 dist/sync.js.map create mode 100644 dist/translate.js create mode 100644 dist/translate.js.map create mode 100644 dist/unused.js create mode 100644 dist/unused.js.map create mode 100644 dist/utils.js create mode 100644 dist/utils.js.map create mode 100644 package.json create mode 100644 public/extract.gif create mode 100644 src/const.ts create mode 100644 src/export.ts create mode 100644 src/extract/extract.ts create mode 100644 src/extract/file.ts create mode 100644 src/extract/findChineseText.ts create mode 100644 src/extract/getLangData.ts create mode 100644 src/extract/replace.ts create mode 100644 src/import.ts create mode 100644 src/index.ts create mode 100644 src/init.ts create mode 100644 src/mock.ts create mode 100644 src/sync.ts create mode 100644 src/translate.ts create mode 100644 src/unused.ts create mode 100644 src/utils.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..668395f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# 🐤 Change Log + +Kiwi Cli + +## 1.0.22 (2022-03-04) +- kiwi --extract 修复多文件提取时的并发问题 +- kiwi --extract 修复文案key出现undefined的情况 +## 1.0.21 (2022-03-01) +- kiwi --extract 添加 --prefix 参数,自定义配置 118N 提取文案路径 +## 1.0.20(2022-02-28) +- kiwi 优化在vue环境下中文检测与linter保持同步 +## 1.0.19(2022-01-26) + +### Breaking changes + +- kiwi --extract 添加百度和拼音翻译源,且支持批量文件以,分隔符输入(原本仅支持指定文件夹) +- 配置文件 kiwi-config.json 添加 defaultTranslateKeyApi + +## 1.0.18(2021-12-07) + +### Breaking changes + +- 配置文件 kiwi-config.json 移动至根目录下 diff --git a/README.md b/README.md new file mode 100644 index 0000000..9510cd7 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +# 🐤 kiwi cli + +Kiwi 的 CLI 工具 + +## 如何使用 + +> yarn global add kiwi-clis + +> 推荐与[🐤 Kiwi-国际化全流程解决方案](https://github.com/alibaba/kiwi)结合使用 + +## CLI 参数 + +### kiwi `--init` + +初始化项目,生成 kiwi 的配置文件 `kiwi-config.json` + +```js +{ + // kiwi文件根目录,用于放置提取的langs文件 + "kiwiDir": "./.kiwi", + + // 配置文件目录,若调整配置文件,此处可手动修改 + "configFile": "./.kiwi/kiwi-config.json", + + // 语言目录名,注意连线和下划线 + "srcLang": "zh-CN", + "distLangs": ["en-US", "zh-TW"], + + // googleApiKey + "googleApiKey": "", + + // baiduApiKey + "baiduApiKey": + "appId": '', + "appKey": '' + }, + + // 百度翻译的语种代码映射 详情见官方文档 https://fanyi-api.baidu.com/doc/21 + "baiduLangMap": { + "en-US": 'en', + "zh-TW": 'cht' + }, + + // 批量提取文案时生成key值时的默认翻译源, Google/Baidu/Pinyin + "defaultTranslateKeyApi": 'Pinyin', + + // import 语句,不同项目请自己配置 + "importI18N": "", + + // 可跳过的文件夹名或者文加名,比如docs、mock等 + "ignoreDir": "", + "ignoreFile": "" +} +``` + +### kiwi `--extract` + +1. 一键批量替换指定文件夹下的所有文案 + +```shell script +# --extract [dirPath] 指定文件夹路径 +# --prefix [prefix] 指定文案前缀 I18N.xxxx +kiwi --extract [dirPath] --prefix [prefix] +``` + +2. commit 提交时自动增量提取,在 precommit 脚本内添加如下指令 + +```shell script +# 检测提交中是否存在ts或tsx文件 +TS_CHANGED=$(git diff --cached --numstat --diff-filter=ACM | grep -F '.ts' | wc -l) + +# 对提交的代码中存在未提取的中文文案统一处理 +if [ "$TS_CHANGED" -gt 0 ] +then + TS_FILES_LIST=($(git diff --cached --name-only --diff-filter=ACM | grep -F '.ts')) + TS_FILES='' + delim='' + for item in ${TS_FILES_LIST[@]};do + TS_FILES=$TS_FILES$delim$item; + delim=',' + done + echo "\033[33m 正在检测未提取的中文文案,请稍后 \033[0m" + kiwi --extract $TS_FILES || exit 1 +fi +``` + +![批量替换](https://raw.githubusercontent.com/alibaba/kiwi/master/kiwi-cli/public/extract.gif) + +### kiwi `--import` + +导入翻译文案,将翻译人员翻译的文案,导入到项目中 + +```shell script +# 导入送翻后的文案 +kiwi --import [filePath] en-US +``` + +### kiwi `--export` + +导出未翻译的文案 + +```shell script +# 导出指定语言的文案,lang取值为配置中distLangs值,如en-US导出还未翻译成英文的中文文案 +kiwi --export [filePath] en-US +``` + +### kiwi `--sync` + +同步各种语言的文案,同步未翻译文件 + +### kiwi `--mock` + +使用 Google 翻译,翻译未翻译的文案 +如果同时配置 baiduApiKey 与 baiduApiKey 则命令行可手动选择翻译源 + +### kiwi `--translate` + +全量翻译未翻译的中文文案,翻译结果自动导入 en-US zh-TW 等目录 + +```shell script +kiwi --translate +``` diff --git a/dist/const.js b/dist/const.js new file mode 100644 index 0000000..c5149b1 --- /dev/null +++ b/dist/const.js @@ -0,0 +1,46 @@ +"use strict"; +/** + * @author linhuiw + * @desc 项目配置文件配置信息 + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PROJECT_CONFIG = exports.CANARY_CONFIG_FILE = void 0; +exports.CANARY_CONFIG_FILE = 'canary-config.json'; +exports.PROJECT_CONFIG = { + dir: './.canary', + defaultConfig: { + canaryDir: './.canary', + srcLang: 'zh-CN', + distLangs: ['en-US', 'zh-CN'], + googleApiKey: '', + baiduApiKey: { + appId: '', + appKey: '' + }, + baiduLangMap: { + ['en-US']: 'en', + ['zh-TW']: 'cht' + }, + translateOptions: { + concurrentLimit: 10, + requestOptions: {} + }, + defaultTranslateKeyApi: 'Pinyin', + importI18N: `import I18N from 'src/utils/I18N';`, + ignoreDir: '', + ignoreFile: '' + }, + langMap: { + ['en-US']: 'en', + ['en_US']: 'en' + }, + zhIndexFile: `import common from './common'; + +export default Object.assign({}, { + common +});`, + zhTestFile: `export default { + test: '测试' +}`, +}; +//# sourceMappingURL=const.js.map \ No newline at end of file diff --git a/dist/const.js.map b/dist/const.js.map new file mode 100644 index 0000000..e1aea1a --- /dev/null +++ b/dist/const.js.map @@ -0,0 +1 @@ +{"version":3,"file":"const.js","sourceRoot":"","sources":["../src/const.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEU,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAE1C,QAAA,cAAc,GAAG;IAC5B,GAAG,EAAE,WAAW;IAChB,aAAa,EAAE;QACb,SAAS,EAAE,WAAW;QACtB,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;QAC7B,YAAY,EAAE,EAAE;QAChB,WAAW,EAAE;YACX,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;SACX;QACD,YAAY,EAAE;YACZ,CAAC,OAAO,CAAC,EAAE,IAAI;YACf,CAAC,OAAO,CAAC,EAAE,KAAK;SACjB;QACD,gBAAgB,EAAE;YAChB,eAAe,EAAE,EAAE;YACnB,cAAc,EAAE,EAAE;SACnB;QACD,sBAAsB,EAAE,QAAQ;QAChC,UAAU,EAAE,oCAAoC;QAChD,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,EAAE;KACf;IACD,OAAO,EAAE;QACP,CAAC,OAAO,CAAC,EAAE,IAAI;QACf,CAAC,OAAO,CAAC,EAAE,IAAI;KAChB;IACD,WAAW,EAAE;;;;IAIX;IACF,UAAU,EAAE;;EAEZ;CACD,CAAC"} \ No newline at end of file diff --git a/dist/export.js b/dist/export.js new file mode 100644 index 0000000..60ee4ad --- /dev/null +++ b/dist/export.js @@ -0,0 +1,40 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.exportMessages = void 0; +/** + * @author linhuiw + * @desc 导出未翻译文件 + */ +require('ts-node').register({ + compilerOptions: { + module: 'commonjs' + } +}); +const fs = require("fs"); +const d3_dsv_1 = require("d3-dsv"); +const utils_1 = require("./utils"); +function exportMessages(file, lang) { + const CONFIG = utils_1.getProjectConfig(); + const langs = lang ? [lang] : CONFIG.distLangs; + langs.map(lang => { + const allMessages = utils_1.getAllMessages(CONFIG.srcLang); + const existingTranslations = utils_1.getAllMessages(lang, (message, key) => !/[\u4E00-\u9FA5]/.test(allMessages[key]) || allMessages[key] !== message); + const messagesToTranslate = Object.keys(allMessages) + .filter(key => !existingTranslations.hasOwnProperty(key)) + .map(key => { + let message = allMessages[key]; + message = JSON.stringify(message).slice(1, -1); + return [key, message]; + }); + if (messagesToTranslate.length === 0) { + console.log('All the messages have been translated.'); + return; + } + const content = d3_dsv_1.tsvFormatRows(messagesToTranslate); + const sourceFile = file || `./export-${lang}`; + fs.writeFileSync(sourceFile, content); + console.log(`Exported ${messagesToTranslate.length} message(s).`); + }); +} +exports.exportMessages = exportMessages; +//# sourceMappingURL=export.js.map \ No newline at end of file diff --git a/dist/export.js.map b/dist/export.js.map new file mode 100644 index 0000000..ba20954 --- /dev/null +++ b/dist/export.js.map @@ -0,0 +1 @@ +{"version":3,"file":"export.js","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,OAAO,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC;IAC1B,eAAe,EAAE;QACf,MAAM,EAAE,UAAU;KACnB;CACF,CAAC,CAAC;AACH,yBAAyB;AACzB,mCAAuC;AACvC,mCAA2D;AAG3D,SAAS,cAAc,CAAC,IAAa,EAAE,IAAa;IAClD,MAAM,MAAM,GAAG,wBAAgB,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;IAE/C,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QACf,MAAM,WAAW,GAAG,sBAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,oBAAoB,GAAG,sBAAc,CACzC,IAAI,EACJ,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,OAAO,CAC5F,CAAC;QACF,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;aACjD,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;aACxD,GAAG,CAAC,GAAG,CAAC,EAAE;YACT,IAAI,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QAEL,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE;YACpC,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YACtD,OAAO;SACR;QAED,MAAM,OAAO,GAAG,sBAAa,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;QAC9C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,YAAY,mBAAmB,CAAC,MAAM,cAAc,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC;AAEQ,wCAAc"} \ No newline at end of file diff --git a/dist/extract/extract.js b/dist/extract/extract.js new file mode 100644 index 0000000..7a4db0d --- /dev/null +++ b/dist/extract/extract.js @@ -0,0 +1,266 @@ +"use strict"; +/** + * @author doubledream + * @desc 提取指定文件夹下的中文 + */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.extractAll = void 0; +const _ = require("lodash"); +const slash = require("slash2"); +const path = require("path"); +const colors = require("colors"); +const file_1 = require("./file"); +const findChineseText_1 = require("./findChineseText"); +const getLangData_1 = require("./getLangData"); +const utils_1 = require("../utils"); +const replace_1 = require("./replace"); +const utils_2 = require("../utils"); +const CONFIG = utils_2.getProjectConfig(); +/** + * 剔除 kiwiDir 下的文件 + */ +function removeLangsFiles(files) { + const langsDir = path.resolve(process.cwd(), CONFIG.canaryDir); + return files.filter(file => { + const completeFile = path.resolve(process.cwd(), file); + return !completeFile.includes(langsDir); + }); +} +/** + * 递归匹配项目中所有的代码的中文 + */ +function findAllChineseText(dir) { + const first = dir.split(',')[0]; + let files = []; + if (file_1.isDirectory(first)) { + const dirPath = path.resolve(process.cwd(), dir); + files = file_1.getSpecifiedFiles(dirPath, CONFIG.ignoreDir, CONFIG.ignoreFile); + } + else { + files = removeLangsFiles(dir.split(',')); + } + const filterFiles = files.filter(file => { + return (file_1.isFile(file) && file.endsWith('.ts')) || (file_1.isFile(file) && file.endsWith('.js')) || file.endsWith('.tsx') || file.endsWith('.vue'); + }); + const allTexts = filterFiles.reduce((pre, file) => { + const code = file_1.readFile(file); + const texts = findChineseText_1.findChineseText(code, file); + // 调整文案顺序,保证从后面的文案往前替换,避免位置更新导致替换出错 + const sortTexts = _.sortBy(texts, obj => -obj.range.start); + if (texts.length > 0) { + console.log(`${utils_1.highlightText(file)} 发现 ${utils_1.highlightText(texts.length)} 处中文文案`); + } + return texts.length > 0 ? pre.concat({ file, texts: sortTexts }) : pre; + }, []); + return allTexts; +} +/** + * 处理作为key值的翻译原文 + */ +function getTransOriginText(text) { + // 避免翻译的字符里包含数字或者特殊字符等情况,只过滤出汉字和字母 + const reg = /[a-zA-Z\u4e00-\u9fa5]+/g; + const findText = text.match(reg) || []; + const transOriginText = findText ? findText.join('').slice(0, 5) : '中文符号'; + return transOriginText; +} +/** + * @param currentFilename 文件路径 + * @returns string[] + */ +function getSuggestion(currentFilename) { + let suggestion = []; + const suggestPageRegex = /\/pages\/\w+\/([^\/]+)\/([^\/\.]+)/; + if (currentFilename.includes('/pages/')) { + suggestion = currentFilename.match(suggestPageRegex); + } + if (suggestion) { + suggestion.shift(); + } + /** 如果没有匹配到 Key */ + if (!(suggestion && suggestion.length)) { + const names = slash(currentFilename).split('/'); + const fileName = _.last(names); + const fileKey = fileName.split('.')[0].replace(new RegExp('-', 'g'), '_'); + const dir = names[names.length - 2].replace(new RegExp('-', 'g'), '_'); + if (dir === fileKey) { + suggestion = [dir]; + } + else { + suggestion = [dir, fileKey]; + } + } + return suggestion; +} +/** + * 统一处理key值,已提取过的文案直接替换,翻译后的key若相同,加上出现次数 + * @param currentFilename 文件路径 + * @param langsPrefix 替换后的前缀 + * @param translateTexts 翻译后的key值 + * @param targetStrs 当前文件提取后的文案 + * @returns any[] 最终可用于替换的key值和文案 + */ +function getReplaceableStrs(currentFilename, langsPrefix, translateTexts, targetStrs) { + const finalLangObj = getLangData_1.getSuggestLangObj(); + const virtualMemory = {}; + const suggestion = getSuggestion(currentFilename); + const replaceableStrs = targetStrs.reduce((prev, curr, i) => { + const _text = curr.text; + let key = utils_1.findMatchKey(finalLangObj, _text); + if (key) { + key = key.replace(/-/g, '_'); + } + if (!virtualMemory[_text]) { + if (key) { + virtualMemory[_text] = key; + return prev.concat({ + target: curr, + key, + needWrite: false + }); + } + const transText = translateTexts[i] && _.camelCase(translateTexts[i]); + let transKey = `${suggestion.length ? suggestion.join('.') + '.' : ''}${transText}`; + transKey = transKey.replace(/-/g, '_'); + if (langsPrefix) { + transKey = `${langsPrefix}.${transText}`; + } + let occurTime = 1; + // 防止出现前四位相同但是整体文案不同的情况 + while (utils_1.findMatchValue(finalLangObj, transKey) !== _text && + _.keys(finalLangObj).includes(`${transKey}${occurTime >= 2 ? occurTime : ''}`)) { + occurTime++; + } + if (occurTime >= 2) { + transKey = `${transKey}${occurTime}`; + } + virtualMemory[_text] = transKey; + finalLangObj[transKey] = _text; + return prev.concat({ + target: curr, + key: transKey, + needWrite: true + }); + } + else { + return prev.concat({ + target: curr, + key: virtualMemory[_text], + needWrite: true + }); + } + }, []); + return replaceableStrs; +} +/** + * 递归匹配项目中所有的代码的中文 + * @param {dirPath} 文件夹路径 + */ +function extractAll({ dirPath, prefix }) { + const dir = dirPath || './'; + // 去除I18N + const langsPrefix = prefix ? prefix.replace(/^I18N\./, '') : null; + // 翻译源配置错误,则终止 + const origin = CONFIG.defaultTranslateKeyApi || 'Pinyin'; + if (!['Pinyin', 'Google', 'Baidu'].includes(CONFIG.defaultTranslateKeyApi)) { + console.log(`Kiwi 仅支持 ${utils_1.highlightText('Pinyin、Google、Baidu')},请修改 ${utils_1.highlightText('defaultTranslateKeyApi')} 配置项`); + return; + } + const allTargetStrs = findAllChineseText(dir); + if (allTargetStrs.length === 0) { + console.log(utils_1.highlightText('没有发现可替换的文案!')); + return; + } + // 提示翻译源 + if (CONFIG.defaultTranslateKeyApi === 'Pinyin') { + console.log(`当前使用 ${utils_1.highlightText('Pinyin')} 作为key值的翻译源,若想得到更好的体验,可配置 ${utils_1.highlightText('googleApiKey')} 或 ${utils_1.highlightText('baiduApiKey')},并切换 ${utils_1.highlightText('defaultTranslateKeyApi')}`); + } + else { + console.log(`当前使用 ${utils_1.highlightText(CONFIG.defaultTranslateKeyApi)} 作为key值的翻译源`); + } + console.log('即将截取每个中文文案的前5位翻译生成key值,并替换中...'); + // 对当前文件进行文案key生成和替换 + const generateKeyAndReplace = (item) => __awaiter(this, void 0, void 0, function* () { + const currentFilename = item.file; + console.log(`${currentFilename} 替换中...`); + // 过滤掉模板字符串内的中文,避免替换时出现异常 + const targetStrs = item.texts.reduce((pre, strObj, i) => { + // 因为文案已经根据位置倒排,所以比较时只需要比较剩下的文案即可 + const afterStrs = item.texts.slice(i + 1); + if (afterStrs.some(obj => strObj.range.end <= obj.range.end)) { + return pre; + } + return pre.concat(strObj); + }, []); + const len = item.texts.length - targetStrs.length; + if (len > 0) { + console.log(colors.red(`存在 ${utils_1.highlightText(len)} 处文案无法替换,请避免在模板字符串的变量中嵌套中文`)); + } + let translateTexts; + if (origin !== 'Google') { + // 翻译中文文案,百度和pinyin将文案进行拼接统一翻译 + const delimiter = origin === 'Baidu' ? '\n' : '$'; + const translateOriginTexts = targetStrs.reduce((prev, curr, i) => { + const transOriginText = getTransOriginText(curr.text); + if (i === 0) { + return transOriginText; + } + return `${prev}${delimiter}${transOriginText}`; + }, []); + translateTexts = yield utils_1.translateKeyText(translateOriginTexts, origin); + } + else { + // google并发性较好,且未找到有效的分隔符,故仍然逐个文案进行翻译 + const translatePromises = targetStrs.reduce((prev, curr) => { + const transOriginText = getTransOriginText(curr.text); + return prev.concat(utils_1.translateText(transOriginText, 'en_US')); + }, []); + [...translateTexts] = yield Promise.all(translatePromises); + } + if (translateTexts.length === 0) { + utils_1.failInfo(`未得到翻译结果,${currentFilename}替换失败!`); + return; + } + const replaceableStrs = getReplaceableStrs(currentFilename, langsPrefix, translateTexts, targetStrs); + yield replaceableStrs + .reduce((prev, obj) => { + return prev.then(() => { + return replace_1.replaceAndUpdate(currentFilename, obj.target, `I18N.${obj.key}`, false, obj.needWrite); + }); + }, Promise.resolve()) + .then(() => { + // 添加 import I18N + if (!replace_1.hasImportI18N(currentFilename)) { + const code = replace_1.createImportI18N(currentFilename); + file_1.writeFile(currentFilename, code); + } + utils_1.successInfo(`${currentFilename} 替换完成,共替换 ${targetStrs.length} 处文案!`); + }) + .catch(e => { + utils_1.failInfo(e.message); + }); + }); + allTargetStrs + .reduce((prev, current) => { + return prev.then(() => { + return generateKeyAndReplace(current); + }); + }, Promise.resolve()) + .then(() => { + utils_1.successInfo('全部替换完成!'); + }) + .catch((e) => { + utils_1.failInfo(e.message); + }); +} +exports.extractAll = extractAll; +//# sourceMappingURL=extract.js.map \ No newline at end of file diff --git a/dist/extract/extract.js.map b/dist/extract/extract.js.map new file mode 100644 index 0000000..976e5b9 --- /dev/null +++ b/dist/extract/extract.js.map @@ -0,0 +1 @@ +{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/extract/extract.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/extract/file.js b/dist/extract/file.js new file mode 100644 index 0000000..7ec518b --- /dev/null +++ b/dist/extract/file.js @@ -0,0 +1,74 @@ +"use strict"; +/** + * @author doubledream + * @desc 文件处理方法 + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isDirectory = exports.isFile = exports.writeFile = exports.readFile = exports.getSpecifiedFiles = void 0; +const path = require("path"); +const fs = require("fs"); +/** + * 获取文件夹下符合要求的所有文件 + * @function getSpecifiedFiles + * @param {string} dir 路径 + * @param {ignoreDirectory} 忽略文件夹 {ignoreFile} 忽略的文件 + */ +function getSpecifiedFiles(dir, ignoreDirectory = '', ignoreFile = '') { + return fs.readdirSync(dir).reduce((files, file) => { + const name = path.join(dir, file); + const isDirectory = fs.statSync(name).isDirectory(); + const isFile = fs.statSync(name).isFile(); + if (isDirectory) { + return files.concat(getSpecifiedFiles(name, ignoreDirectory, ignoreFile)); + } + const isIgnoreDirectory = !ignoreDirectory || + (ignoreDirectory && + !path + .dirname(name) + .split('/') + .includes(ignoreDirectory)); + const isIgnoreFile = !ignoreFile || (ignoreFile && path.basename(name) !== ignoreFile); + if (isFile && isIgnoreDirectory && isIgnoreFile) { + return files.concat(name); + } + return files; + }, []); +} +exports.getSpecifiedFiles = getSpecifiedFiles; +/** + * 读取文件 + * @param fileName + */ +function readFile(fileName) { + if (fs.existsSync(fileName)) { + return fs.readFileSync(fileName, 'utf-8'); + } +} +exports.readFile = readFile; +/** + * 读取文件 + * @param fileName + */ +function writeFile(filePath, file) { + if (fs.existsSync(filePath)) { + fs.writeFileSync(filePath, file); + } +} +exports.writeFile = writeFile; +/** + * 判断是文件 + * @param path + */ +function isFile(path) { + return fs.statSync(path).isFile(); +} +exports.isFile = isFile; +/** + * 判断是文件夹 + * @param path + */ +function isDirectory(path) { + return fs.statSync(path).isDirectory(); +} +exports.isDirectory = isDirectory; +//# sourceMappingURL=file.js.map \ No newline at end of file diff --git a/dist/extract/file.js.map b/dist/extract/file.js.map new file mode 100644 index 0000000..affea43 --- /dev/null +++ b/dist/extract/file.js.map @@ -0,0 +1 @@ +{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/extract/file.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6BAA6B;AAE7B,yBAAyB;AAEzB;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE;IACnE,OAAO,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,WAAW,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QAE1C,IAAI,WAAW,EAAE;YACf,OAAO,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC,CAAC;SAC3E;QAED,MAAM,iBAAiB,GACrB,CAAC,eAAe;YAChB,CAAC,eAAe;gBACd,CAAC,IAAI;qBACF,OAAO,CAAC,IAAI,CAAC;qBACb,KAAK,CAAC,GAAG,CAAC;qBACV,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC;QAEvF,IAAI,MAAM,IAAI,iBAAiB,IAAI,YAAY,EAAE;YAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SAC3B;QACD,OAAO,KAAK,CAAC;IACf,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AAsCQ,8CAAiB;AApC1B;;;GAGG;AACH,SAAS,QAAQ,CAAC,QAAQ;IACxB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC3B,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;KAC3C;AACH,CAAC;AA4B2B,4BAAQ;AA1BpC;;;GAGG;AACH,SAAS,SAAS,CAAC,QAAQ,EAAE,IAAI;IAC/B,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC3B,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;KAClC;AACH,CAAC;AAkBqC,8BAAS;AAhB/C;;;GAGG;AACH,SAAS,MAAM,CAAC,IAAI;IAClB,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;AACpC,CAAC;AAUgD,wBAAM;AARvD;;;GAGG;AACH,SAAS,WAAW,CAAC,IAAI;IACvB,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AACzC,CAAC;AAEwD,kCAAW"} \ No newline at end of file diff --git a/dist/extract/findChineseText.js b/dist/extract/findChineseText.js new file mode 100644 index 0000000..131ebc9 --- /dev/null +++ b/dist/extract/findChineseText.js @@ -0,0 +1,399 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.findTextInVue = exports.findChineseText = void 0; +/** + * @author doubledream + * @desc 利用 Ast 查找对应文件中的中文文案 + */ +const ts = require("typescript"); +const compiler = require("@angular/compiler"); +const compilerVue = require("vue-template-compiler"); +const babel = require("@babel/core"); +/** unicode cjk 中日韩文 范围 */ +const DOUBLE_BYTE_REGEX = /[\u4E00-\u9FFF]/g; +function transerI18n(code, filename, lang) { + if (lang === 'ts') { + return typescriptI18n(code, filename); + } + else { + return javascriptI18n(code, filename); + } +} +function javascriptI18n(code, filename) { + let arr = []; + let visitor = { + StringLiteral(path) { + if (path.node.value.match(DOUBLE_BYTE_REGEX)) { + arr.push(path.node.value); + } + } + }; + let arrayPlugin = { visitor }; + babel.transformSync(code.toString(), { + filename, + plugins: [arrayPlugin] + }); + return arr; +} +function typescriptI18n(code, fileName) { + let arr = []; + const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS); + function visit(node) { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: { + /** 判断 Ts 中的字符串含有中文 */ + const { text } = node; + if (text.match(DOUBLE_BYTE_REGEX)) { + arr.push(text); + } + break; + } + } + ts.forEachChild(node, visit); + } + ts.forEachChild(ast, visit); + return arr; +} +/** + * 去掉文件中的注释 + * @param code + * @param fileName + */ +function removeFileComment(code, fileName) { + const printer = ts.createPrinter({ removeComments: true }); + const sourceFile = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, fileName.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS); + return printer.printFile(sourceFile); +} +/** + * 查找 Ts 文件中的中文 + * @param code + */ +function findTextInTs(code, fileName) { + const matches = []; + const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TSX); + function visit(node) { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: { + /** 判断 Ts 中的字符串含有中文 */ + const { text } = node; + if (text.match(DOUBLE_BYTE_REGEX)) { + const start = node.getStart(); + const end = node.getEnd(); + const range = { start, end }; + matches.push({ + range, + text, + isString: true + }); + } + break; + } + case ts.SyntaxKind.JsxElement: { + const { children } = node; + children.forEach(child => { + if (child.kind === ts.SyntaxKind.JsxText) { + const text = child.getText(); + /** 修复注释含有中文的情况,Angular 文件错误的 Ast 情况 */ + const noCommentText = removeFileComment(text, fileName); + if (noCommentText.match(DOUBLE_BYTE_REGEX)) { + const start = child.getStart(); + const end = child.getEnd(); + const range = { start, end }; + matches.push({ + range, + text: text.trim(), + isString: false + }); + } + } + }); + break; + } + case ts.SyntaxKind.TemplateExpression: { + const { pos, end } = node; + const templateContent = code.slice(pos, end); + if (templateContent.match(DOUBLE_BYTE_REGEX)) { + const start = node.getStart(); + const end = node.getEnd(); + const range = { start, end }; + matches.push({ + range, + text: code.slice(start + 1, end - 1), + isString: true + }); + } + break; + } + case ts.SyntaxKind.NoSubstitutionTemplateLiteral: { + const { pos, end } = node; + const templateContent = code.slice(pos, end); + if (templateContent.match(DOUBLE_BYTE_REGEX)) { + const start = node.getStart(); + const end = node.getEnd(); + const range = { start, end }; + matches.push({ + range, + text: code.slice(start + 1, end - 1), + isString: true + }); + } + } + } + ts.forEachChild(node, visit); + } + ts.forEachChild(ast, visit); + return matches; +} +/** + * 查找 HTML 文件中的中文 + * @param code + */ +function findTextInHtml(code) { + const matches = []; + const ast = compiler.parseTemplate(code, 'ast.html', { + preserveWhitespaces: false + }); + function visit(node) { + const value = node.value; + if (value && typeof value === 'string' && value.match(DOUBLE_BYTE_REGEX)) { + const valueSpan = node.valueSpan || node.sourceSpan; + let { start: { offset: startOffset }, end: { offset: endOffset } } = valueSpan; + const nodeValue = code.slice(startOffset, endOffset); + let isString = false; + /** 处理带引号的情况 */ + if (nodeValue.charAt(0) === '"' || nodeValue.charAt(0) === "'") { + isString = true; + } + const range = { start: startOffset, end: endOffset }; + matches.push({ + range, + text: value, + isString + }); + } + else if (value && typeof value === 'object' && value.source && value.source.match(DOUBLE_BYTE_REGEX)) { + /** + * {{expression}}中文 这种情况的兼容 + */ + const chineseMatches = value.source.match(DOUBLE_BYTE_REGEX); + chineseMatches.map(match => { + const valueSpan = node.valueSpan || node.sourceSpan; + let { start: { offset: startOffset }, end: { offset: endOffset } } = valueSpan; + const nodeValue = code.slice(startOffset, endOffset); + const start = nodeValue.indexOf(match); + const end = start + match.length; + const range = { start, end }; + matches.push({ + range, + text: match[0], + isString: false + }); + }); + } + if (node.children && node.children.length) { + node.children.forEach(visit); + } + if (node.attributes && node.attributes.length) { + node.attributes.forEach(visit); + } + } + if (ast.nodes && ast.nodes.length) { + ast.nodes.forEach(visit); + } + return matches; +} +/** + * 递归匹配vue代码的中文 + * @param code + */ +function findTextInVue(code) { + let rexspace1 = new RegExp(/ /, 'g'); + let rexspace2 = new RegExp(/ /, 'g'); + let rexspace3 = new RegExp(/ /, 'g'); + code = code + .replace(rexspace1, 'ccsp&;') + .replace(rexspace2, 'ecsp&;') + .replace(rexspace3, 'ncsp&;'); + let coverRex1 = new RegExp(/ccsp&;/, 'g'); + let coverRex2 = new RegExp(/ecsp&;/, 'g'); + let coverRex3 = new RegExp(/ncsp&;/, 'g'); + let matches = []; + var result; + const vueObejct = compilerVue.compile(code.toString(), { outputSourceRange: true }); + let vueAst = vueObejct.ast; + let expressTemp = findVueText(vueAst); + expressTemp.forEach(item => { + item.arrf = [item.start, item.end]; + }); + matches = expressTemp; + let outcode = vueObejct.render.toString().replace('with(this)', 'function a()'); + let vueTemp = transerI18n(outcode, 'as.vue', null); + /**删除所有的html中的头部空格 */ + vueTemp = vueTemp.map(item => { + return item.trim(); + }); + vueTemp = Array.from(new Set(vueTemp)); + let codeStaticArr = []; + vueObejct.staticRenderFns.forEach(item => { + let childcode = item.toString().replace('with(this)', 'function a()'); + let vueTempChild = transerI18n(childcode, 'as.vue', null); + codeStaticArr = codeStaticArr.concat(Array.from(new Set(vueTempChild))); + }); + vueTemp = Array.from(new Set(codeStaticArr.concat(vueTemp))); + vueTemp.forEach(item => { + let items = item + .replace(/\{/g, '\\{') + .replace(/\}/g, '\\}') + .replace(/\$/g, '\\$') + .replace(/\(/g, '\\(') + .replace(/\)/g, '\\)') + .replace(/\+/g, '\\+') + .replace(/\*/g, '\\*') + .replace(/\^/g, '\\^'); + let rex = new RegExp(items, 'g'); + let codeTemplate = code.substring(vueObejct.ast.start, vueObejct.ast.end); + while ((result = rex.exec(codeTemplate))) { + let res = result; + let last = rex.lastIndex; + last = last - (res[0].length - res[0].trimRight().length); + const range = { start: res.index, end: last }; + matches.push({ + arrf: [res.index, last], + range, + text: res[0] + .trimRight() + .replace(coverRex1, ' ') + .replace(coverRex2, ' ') + .replace(coverRex3, ' '), + isString: (codeTemplate.substr(res.index - 1, 1) === '"' && codeTemplate.substr(last, 1) === '"') || + (codeTemplate.substr(res.index - 1, 1) === "'" && codeTemplate.substr(last, 1) === "'") + ? true + : false + }); + } + }); + let matchesTemp = matches; + let matchesTempResult = matchesTemp.filter((item, index) => { + let canBe = true; + matchesTemp.forEach(items => { + if ((item.arrf[0] > items.arrf[0] && item.arrf[1] <= items.arrf[1]) || + (item.arrf[0] >= items.arrf[0] && item.arrf[1] < items.arrf[1]) || + (item.arrf[0] > items.arrf[0] && item.arrf[1] < items.arrf[1])) { + canBe = false; + } + }); + if (canBe) + return item; + }); + const sfc = compilerVue.parseComponent(code.toString()); + return matchesTempResult.concat(findTextInVueTs(sfc.script.content, 'AS', sfc.script.start)); +} +exports.findTextInVue = findTextInVue; +function findTextInVueTs(code, fileName, startNum) { + const matches = []; + const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS); + function visit(node) { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: { + /** 判断 Ts 中的字符串含有中文 */ + const { text } = node; + if (text.match(DOUBLE_BYTE_REGEX)) { + const start = node.getStart(); + const end = node.getEnd(); + /** 加一,减一的原因是,去除引号 */ + const range = { start: start + startNum, end: end + startNum }; + matches.push({ + range, + text, + isString: true + }); + } + break; + } + case ts.SyntaxKind.TemplateExpression: { + const { pos, end } = node; + let templateContent = code.slice(pos, end); + templateContent = templateContent.toString().replace(/\$\{[^\}]+\}/, ''); + if (templateContent.match(DOUBLE_BYTE_REGEX)) { + const start = node.getStart(); + const end = node.getEnd(); + /** 加一,减一的原因是,去除`号 */ + const range = { start: start + startNum, end: end + startNum }; + matches.push({ + range, + text: code.slice(start + 1, end - 1), + isString: true + }); + } + break; + } + } + ts.forEachChild(node, visit); + } + ts.forEachChild(ast, visit); + return matches; +} +function findVueText(ast) { + let arr = []; + const regex1 = /\`(.+?)\`/g; + function emun(ast) { + if (ast.expression) { + let text = ast.expression.match(regex1); + if (text && text[0].match(DOUBLE_BYTE_REGEX)) { + text.forEach(itemText => { + const varInStr = itemText.match(/(\$\{[^\}]+?\})/g); + if (varInStr) + itemText.match(DOUBLE_BYTE_REGEX) && + arr.push({ text: ' ' + itemText, range: { start: ast.start + 2, end: ast.end - 2 }, isString: true }); + else + itemText.match(DOUBLE_BYTE_REGEX) && + arr.push({ text: itemText, range: { start: ast.start, end: ast.end }, isString: false }); + }); + } + else { + ast.tokens && + ast.tokens.forEach(element => { + if (typeof element === 'string' && element.match(DOUBLE_BYTE_REGEX)) { + arr.push({ + text: element, + range: { + start: ast.start + ast.text.indexOf(element), + end: ast.start + ast.text.indexOf(element) + element.length + }, + isString: false + }); + } + }); + } + } + else if (!ast.expression && ast.text) { + ast.text.match(DOUBLE_BYTE_REGEX) && + arr.push({ text: ast.text, range: { start: ast.start, end: ast.end }, isString: false }); + } + else { + ast.children && + ast.children.forEach(item => { + emun(item); + }); + } + } + emun(ast); + return arr; +} +/** + * 递归匹配代码的中文 + * @param code + */ +function findChineseText(code, fileName) { + if (fileName.endsWith('.html')) { + return findTextInHtml(code); + } + else if (fileName.endsWith('.vue')) { + return findTextInVue(code); + } + else { + return findTextInTs(code, fileName); + } +} +exports.findChineseText = findChineseText; +//# sourceMappingURL=findChineseText.js.map \ No newline at end of file diff --git a/dist/extract/findChineseText.js.map b/dist/extract/findChineseText.js.map new file mode 100644 index 0000000..3ed3d77 --- /dev/null +++ b/dist/extract/findChineseText.js.map @@ -0,0 +1 @@ +{"version":3,"file":"findChineseText.js","sourceRoot":"","sources":["../../src/extract/findChineseText.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/extract/getLangData.js b/dist/extract/getLangData.js new file mode 100644 index 0000000..e17a73d --- /dev/null +++ b/dist/extract/getLangData.js @@ -0,0 +1,72 @@ +"use strict"; +/** + * @author doubledream + * @desc 获取语言文件 + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getLangData = exports.getSuggestLangObj = void 0; +const globby = require("globby"); +const fs = require("fs"); +const path = require("path"); +const utils_1 = require("../utils"); +const CONFIG = utils_1.getProjectConfig(); +const LANG_DIR = path.resolve(CONFIG.canaryDir, CONFIG.srcLang); +const I18N_GLOB = `${LANG_DIR}/**/*.ts`; +/** + * 获取对应文件的语言 + */ +function getLangData(fileName) { + if (fs.existsSync(fileName)) { + return getLangJson(fileName); + } + else { + return {}; + } +} +exports.getLangData = getLangData; +/** + * 获取文件 Json + */ +function getLangJson(fileName) { + const fileContent = fs.readFileSync(fileName, { encoding: 'utf8' }); + let obj = fileContent.match(/export\s*default\s*({[\s\S]+);?$/)[1]; + obj = obj.replace(/\s*;\s*$/, ''); + let jsObj = {}; + try { + jsObj = eval('(' + obj + ')'); + } + catch (err) { + console.log(obj); + console.error(err); + } + return jsObj; +} +function getI18N() { + const paths = globby.sync(I18N_GLOB); + const langObj = paths.reduce((prev, curr) => { + const filename = curr + .split('/') + .pop() + .replace(/\.tsx?$/, ''); + if (filename.replace(/\.tsx?/, '') === 'index') { + return prev; + } + const fileContent = getLangData(curr); + let jsObj = fileContent; + if (Object.keys(jsObj).length === 0) { + console.log(`\`${curr}\` 解析失败,该文件包含的文案无法自动补全`); + } + return Object.assign(Object.assign({}, prev), { [filename]: jsObj }); + }, {}); + return langObj; +} +/** + * 获取全部语言, 展平 + */ +function getSuggestLangObj() { + const langObj = getI18N(); + const finalLangObj = utils_1.flatten(langObj); + return finalLangObj; +} +exports.getSuggestLangObj = getSuggestLangObj; +//# sourceMappingURL=getLangData.js.map \ No newline at end of file diff --git a/dist/extract/getLangData.js.map b/dist/extract/getLangData.js.map new file mode 100644 index 0000000..f23cbd7 --- /dev/null +++ b/dist/extract/getLangData.js.map @@ -0,0 +1 @@ +{"version":3,"file":"getLangData.js","sourceRoot":"","sources":["../../src/extract/getLangData.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,iCAAiC;AACjC,yBAAyB;AACzB,6BAA6B;AAC7B,oCAAqD;AAErD,MAAM,MAAM,GAAG,wBAAgB,EAAE,CAAC;AAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;AAChE,MAAM,SAAS,GAAG,GAAG,QAAQ,UAAU,CAAC;AAExC;;GAEG;AACH,SAAS,WAAW,CAAC,QAAQ;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC3B,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;KAC9B;SAAM;QACL,OAAO,EAAE,CAAC;KACX;AACH,CAAC;AAsD2B,kCAAW;AApDvC;;GAEG;AACH,SAAS,WAAW,CAAC,QAAQ;IAC3B,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,IAAI,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI;QACF,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;KAC/B;IAAC,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;KACpB;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO;IACd,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC1C,MAAM,QAAQ,GAAG,IAAI;aAClB,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,EAAE;aACL,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC1B,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,OAAO,EAAE;YAC9C,OAAO,IAAI,CAAC;SACb;QAED,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,KAAK,GAAG,WAAW,CAAC;QAExB,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;YACnC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,wBAAwB,CAAC,CAAC;SAChD;QAED,uCACK,IAAI,KACP,CAAC,QAAQ,CAAC,EAAE,KAAK,IACjB;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;IAC1B,MAAM,YAAY,GAAG,eAAO,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,YAAY,CAAC;AACtB,CAAC;AAEQ,8CAAiB"} \ No newline at end of file diff --git a/dist/extract/replace.js b/dist/extract/replace.js new file mode 100644 index 0000000..aedebac --- /dev/null +++ b/dist/extract/replace.js @@ -0,0 +1,233 @@ +"use strict"; +/** + * @author doubledream + * @desc 更新文件 + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createImportI18N = exports.hasImportI18N = exports.replaceAndUpdate = void 0; +const fs = require("fs-extra"); +const _ = require("lodash"); +const prettier = require("prettier"); +const ts = require("typescript"); +const file_1 = require("./file"); +const getLangData_1 = require("./getLangData"); +const utils_1 = require("../utils"); +const CONFIG = utils_1.getProjectConfig(); +const srcLangDir = utils_1.getLangDir(CONFIG.srcLang); +function updateLangFiles(keyValue, text, validateDuplicate) { + if (!_.startsWith(keyValue, 'I18N.')) { + return; + } + const [, filename, ...restPath] = keyValue.split('.'); + const fullKey = restPath.join('.'); + const targetFilename = `${srcLangDir}/${filename}.ts`; + if (!fs.existsSync(targetFilename)) { + fs.writeFileSync(targetFilename, generateNewLangFile(fullKey, text)); + addImportToMainLangFile(filename); + utils_1.successInfo(`成功新建语言文件 ${targetFilename}`); + } + else { + // 清除 require 缓存,解决手动更新语言文件后再自动抽取,导致之前更新失效的问题 + const mainContent = getLangData_1.getLangData(targetFilename); + const obj = mainContent; + if (Object.keys(obj).length === 0) { + utils_1.failInfo(`${filename} 解析失败,该文件包含的文案无法自动补全`); + } + if (validateDuplicate && _.get(obj, fullKey) !== undefined) { + utils_1.failInfo(`${targetFilename} 中已存在 key 为 \`${fullKey}\` 的翻译,请重新命名变量`); + throw new Error('duplicate'); + } + // \n 会被自动转义成 \\n,这里转回来 + text = text.replace(/\\n/gm, '\n'); + _.set(obj, fullKey, text); + fs.writeFileSync(targetFilename, prettierFile(`export default ${JSON.stringify(obj, null, 2)}`)); + } +} +/** + * 使用 Prettier 格式化文件 + * @param fileContent + */ +function prettierFile(fileContent) { + try { + return prettier.format(fileContent, { + parser: 'typescript', + trailingComma: 'all', + singleQuote: true + }); + } + catch (e) { + utils_1.failInfo(`代码格式化报错!${e.toString()}\n代码为:${fileContent}`); + return fileContent; + } +} +function generateNewLangFile(key, value) { + const obj = _.set({}, key, value); + return prettierFile(`export default ${JSON.stringify(obj, null, 2)}`); +} +function addImportToMainLangFile(newFilename) { + let mainContent = ''; + if (fs.existsSync(`${srcLangDir}/index.ts`)) { + mainContent = fs.readFileSync(`${srcLangDir}/index.ts`, 'utf8'); + mainContent = mainContent.replace(/^(\s*import.*?;)$/m, `$1\nimport ${newFilename} from './${newFilename}';`); + if (/(}\);)/.test(mainContent)) { + if (/\,\n(}\);)/.test(mainContent)) { + /** 最后一行包含,号 */ + mainContent = mainContent.replace(/(}\);)/, ` ${newFilename},\n$1`); + } + else { + /** 最后一行不包含,号 */ + mainContent = mainContent.replace(/\n(}\);)/, `,\n ${newFilename},\n$1`); + } + } + // 兼容 export default { common };的写法 + if (/(};)/.test(mainContent)) { + if (/\,\n(};)/.test(mainContent)) { + /** 最后一行包含,号 */ + mainContent = mainContent.replace(/(};)/, ` ${newFilename},\n$1`); + } + else { + /** 最后一行不包含,号 */ + mainContent = mainContent.replace(/\n(};)/, `,\n ${newFilename},\n$1`); + } + } + } + else { + mainContent = `import ${newFilename} from './${newFilename}';\n\nexport default Object.assign({}, {\n ${newFilename},\n});`; + } + fs.writeFileSync(`${srcLangDir}/index.ts`, mainContent); +} +/** + * 检查是否添加 import I18N 命令 + * @param filePath 文件路径 + */ +function hasImportI18N(filePath) { + const code = file_1.readFile(filePath); + const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TSX); + let hasImportI18N = false; + function visit(node) { + if (node.kind === ts.SyntaxKind.ImportDeclaration) { + const importClause = node.importClause; + // import I18N from 'src/utils/I18N'; + if (_.get(importClause, 'kind') === ts.SyntaxKind.ImportClause) { + if (importClause.name) { + if (importClause.name.escapedText === 'I18N') { + hasImportI18N = true; + } + } + else { + const namedBindings = importClause.namedBindings; + // import { I18N } from 'src/utils/I18N'; + if (namedBindings.kind === ts.SyntaxKind.NamedImports) { + namedBindings.elements.forEach(element => { + if (element.kind === ts.SyntaxKind.ImportSpecifier && _.get(element, 'name.escapedText') === 'I18N') { + hasImportI18N = true; + } + }); + } + // import * as I18N from 'src/utils/I18N'; + if (namedBindings.kind === ts.SyntaxKind.NamespaceImport) { + if (_.get(namedBindings, 'name.escapedText') === 'I18N') { + hasImportI18N = true; + } + } + } + } + } + } + ts.forEachChild(ast, visit); + return hasImportI18N; +} +exports.hasImportI18N = hasImportI18N; +/** + * 在合适的位置添加 import I18N 语句 + * @param filePath 文件路径 + */ +function createImportI18N(filePath) { + const code = file_1.readFile(filePath); + const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TSX); + const isTsFile = _.endsWith(filePath, '.ts'); + const isJsFile = _.endsWith(filePath, '.js'); + const isTsxFile = _.endsWith(filePath, '.tsx'); + const isVueFile = _.endsWith(filePath, '.vue'); + if (isTsFile || isTsxFile || isJsFile) { + const importStatement = `${CONFIG.importI18N}\n`; + const pos = ast.getStart(ast, false); + const updateCode = code.slice(0, pos) + importStatement + code.slice(pos); + return updateCode; + } + else if (isVueFile) { + const importStatement = `${CONFIG.importI18N}\n`; + const updateCode = code.replace(/