finish: extract
This commit is contained in:
parent
9ddf81ab54
commit
39f7755468
1
dist/const.js
vendored
1
dist/const.js
vendored
@ -9,6 +9,7 @@ exports.CANARY_CONFIG_FILE = 'canary-config.json';
|
||||
exports.PROJECT_CONFIG = {
|
||||
dir: './.canary',
|
||||
defaultConfig: {
|
||||
isJsProj: false,
|
||||
canaryDir: './.canary',
|
||||
srcLang: 'zh-CN',
|
||||
distLangs: ['en-US', 'zh-CN'],
|
||||
|
2
dist/const.js.map
vendored
2
dist/const.js.map
vendored
@ -1 +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"}
|
||||
{"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,QAAQ,EAAE,KAAK;QACf,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"}
|
244
dist/extract/extract.js
vendored
244
dist/extract/extract.js
vendored
@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
/**
|
||||
* @author doubledream
|
||||
* @author zhaoyingbo
|
||||
* @desc 提取指定文件夹下的中文
|
||||
*/
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
@ -26,7 +26,7 @@ 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);
|
||||
@ -39,19 +39,29 @@ function removeLangsFiles(files) {
|
||||
* 递归匹配项目中所有的代码的中文
|
||||
*/
|
||||
function findAllChineseText(dir) {
|
||||
const first = dir.split(',')[0];
|
||||
// 使用逗号分割用户输入,可以混合输入文件和文件夹
|
||||
const dirList = dir.split(' ');
|
||||
let files = [];
|
||||
if (file_1.isDirectory(first)) {
|
||||
// 获取用户输入的全部文件
|
||||
dirList.forEach(dir => {
|
||||
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');
|
||||
// 输入文件夹
|
||||
if (file_1.isDirectory(dir)) {
|
||||
files.push(...file_1.getSpecifiedFiles(dirPath, CONFIG.ignoreDir, CONFIG.ignoreFile));
|
||||
}
|
||||
// 输入文件
|
||||
else {
|
||||
files.push(dirPath);
|
||||
}
|
||||
});
|
||||
const allTexts = filterFiles.reduce((pre, file) => {
|
||||
// 过滤输入中包含的配置文件夹下的文件
|
||||
files = removeLangsFiles(files);
|
||||
// 过滤非代码文件
|
||||
files = files.filter(file => {
|
||||
return (file_1.isFile(file) && ['ts', 'js', 'tsx', 'jsx', 'vue'].includes(file_1.getSuffix(file)));
|
||||
});
|
||||
// 匹配代码文件中的中文
|
||||
const allTexts = files.reduce((pre, file) => {
|
||||
const code = file_1.readFile(file);
|
||||
const texts = findChineseText_1.findChineseText(code, file);
|
||||
// 调整文案顺序,保证从后面的文案往前替换,避免位置更新导致替换出错
|
||||
@ -74,149 +84,180 @@ function getTransOriginText(text) {
|
||||
return transOriginText;
|
||||
}
|
||||
/**
|
||||
* @param currentFilename 文件路径
|
||||
* @returns string[]
|
||||
* 根据文件名和上级目录生成对应的前缀
|
||||
* @param fileName 文件路径
|
||||
* @returns 例如:home.index. | ''
|
||||
*/
|
||||
function getSuggestion(currentFilename) {
|
||||
let suggestion = [];
|
||||
const suggestPageRegex = /\/pages\/\w+\/([^\/]+)\/([^\/\.]+)/;
|
||||
if (currentFilename.includes('/pages/')) {
|
||||
suggestion = currentFilename.match(suggestPageRegex);
|
||||
function getFilePrefix(fileName) {
|
||||
// 反斜杠路由替换成正斜杠
|
||||
const forwardFileName = slash(fileName);
|
||||
let filePrefix = [];
|
||||
// 如果是在page下的目录直接匹配前缀
|
||||
// oldRegex : \/pages\/\w+\/([^\/]+)\/([^\/\.]+)
|
||||
const suggestPageRegex = /\/pages\/([^\/]+)\/([^\/\.]+)/;
|
||||
if (forwardFileName.includes('/pages/')) {
|
||||
filePrefix = forwardFileName.match(suggestPageRegex);
|
||||
filePrefix && filePrefix.shift();
|
||||
}
|
||||
if (suggestion) {
|
||||
suggestion.shift();
|
||||
}
|
||||
/** 如果没有匹配到 Key */
|
||||
if (!(suggestion && suggestion.length)) {
|
||||
const names = slash(currentFilename).split('/');
|
||||
// 如果没有匹配到前缀
|
||||
if (!(filePrefix && filePrefix.length)) {
|
||||
const names = forwardFileName.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];
|
||||
filePrefix = [dir];
|
||||
}
|
||||
else {
|
||||
suggestion = [dir, fileKey];
|
||||
filePrefix = [dir, fileKey];
|
||||
}
|
||||
}
|
||||
return suggestion;
|
||||
return filePrefix.length ? `${filePrefix.join('.')}.` : '';
|
||||
}
|
||||
/**
|
||||
* 统一处理key值,已提取过的文案直接替换,翻译后的key若相同,加上出现次数
|
||||
* @param currentFilename 文件路径
|
||||
* @param filePath 文件路径
|
||||
* @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);
|
||||
function getLangKeys(filePath, langsPrefix, translateTexts, targetStrs) {
|
||||
// 获取项目原语言的配置并拍平
|
||||
const srcLangObj = getLangData_1.getSrcLangObj();
|
||||
// 是区分是否是本函数生成而非原始数据,从而确定是否需要needWrite
|
||||
const genKeys = {};
|
||||
// 为文案生成对应的键值
|
||||
return targetStrs.reduce((prev, curr, i) => {
|
||||
const { text } = curr;
|
||||
// 如果已生成过对应的键则直接返回
|
||||
if (genKeys[text]) {
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key: genKeys[text],
|
||||
needWrite: true
|
||||
});
|
||||
}
|
||||
// 在原配置中找对应的键
|
||||
let key = utils_1.findMatchKey(srcLangObj, 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
|
||||
key,
|
||||
// 这里由于原语言配置就有键,所以不需要重写进配置文件
|
||||
needWrite: false
|
||||
});
|
||||
}
|
||||
// 没有生成过,原配置也没有,重新生成一个
|
||||
// 获取翻译后的键并驼峰化
|
||||
const transKey = translateTexts[i] && _.camelCase(translateTexts[i]);
|
||||
// 如果用户定义了前缀就直接加上
|
||||
if (langsPrefix) {
|
||||
key = `${langsPrefix}.${transKey}`;
|
||||
}
|
||||
// 没前缀就加上文件名和上级目录作为前缀
|
||||
else {
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key: virtualMemory[_text],
|
||||
needWrite: true
|
||||
});
|
||||
key = `${getFilePrefix(filePath)}${transKey}`;
|
||||
}
|
||||
key = key.replace(/-/g, '_');
|
||||
// 防止出现前五位相同但是整体文案不同的情况
|
||||
if (srcLangObj[key] && srcLangObj[key] !== text) {
|
||||
// 已经存在的Key
|
||||
const existKeys = _.keys(srcLangObj);
|
||||
let occurTime = 2;
|
||||
// 获取已经重复的次数
|
||||
while (existKeys.includes(`${key}${occurTime}`)) {
|
||||
occurTime += 1;
|
||||
}
|
||||
// 拼接重复次数,例如:common.index.key1
|
||||
key = key + occurTime;
|
||||
}
|
||||
// 写进新生成的键列表
|
||||
genKeys[text] = key;
|
||||
// 写入原配置,方便后续比较重复
|
||||
srcLangObj[key] = text;
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key,
|
||||
needWrite: true
|
||||
});
|
||||
}, []);
|
||||
return replaceableStrs;
|
||||
}
|
||||
/**
|
||||
* 递归匹配项目中所有的代码的中文
|
||||
* @param {dirPath} 文件夹路径
|
||||
* @returns
|
||||
*/
|
||||
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')} 配置项`);
|
||||
// 翻译源配置校验
|
||||
let origin = CONFIG.defaultTranslateKeyApi;
|
||||
// 用户设置了空字符串
|
||||
if (!origin) {
|
||||
origin = 'Pinyin';
|
||||
console.log(`配置文件未配置 ${utils_1.highlightText('defaultTranslateKeyApi')} , 使用默认值 ${utils_1.highlightText('Pinyin')}\n`);
|
||||
}
|
||||
// 对用户填入值进行校验
|
||||
else if (!['Pinyin', 'Google', 'Baidu'].includes(origin)) {
|
||||
console.log(`Canary 仅支持 ${utils_1.highlightText('Pinyin、Google、Baidu')},请修改 ${utils_1.highlightText('defaultTranslateKeyApi')} 配置项`);
|
||||
return;
|
||||
}
|
||||
// 地址为用户输入,默认为src文件夹
|
||||
const dir = dirPath || './src';
|
||||
// 去除I18N前缀,后续全局加
|
||||
const langsPrefix = prefix ? prefix.replace(/^I18N\./, '') : null;
|
||||
// 获取目标文件下全部中文文案
|
||||
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')}`);
|
||||
if (origin === 'Pinyin') {
|
||||
console.log(`\n当前使用 ${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(`\n当前使用 ${utils_1.highlightText(origin)} 作为key值的翻译源`);
|
||||
}
|
||||
console.log('即将截取每个中文文案的前5位翻译生成key值,并替换中...');
|
||||
console.log('即将截取每个中文文案的前5位翻译生成key值,并替换中...\n');
|
||||
// 对当前文件进行文案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 { texts, file: filePath } = item;
|
||||
console.log(`${utils_1.highlightText(filePath)} 替换中...`);
|
||||
// 过滤掉模板字符串内的中文,避免替换时出现异常,示例:https://imoaix.cn/canary-clis/1.png
|
||||
const targetStrs = texts.reduce((pre, strObj, i) => {
|
||||
// 因为文案已经根据位置倒排,所以比较时只需要比较剩下的文案即可
|
||||
const afterStrs = item.texts.slice(i + 1);
|
||||
const afterStrs = 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;
|
||||
// 比对前后数量提示用户,不存在过滤后长度为0的情况
|
||||
const len = texts.length - targetStrs.length;
|
||||
if (len > 0) {
|
||||
console.log(colors.red(`存在 ${utils_1.highlightText(len)} 处文案无法替换,请避免在模板字符串的变量中嵌套中文`));
|
||||
}
|
||||
// 翻译后的文案
|
||||
let translateTexts;
|
||||
// 翻译中文文案,百度和pinyin将文案拼接成一条统一翻译
|
||||
if (origin !== 'Google') {
|
||||
// 翻译中文文案,百度和pinyin将文案进行拼接统一翻译
|
||||
// 使用不同的分割符
|
||||
const delimiter = origin === 'Baidu' ? '\n' : '$';
|
||||
// 拼接字符串
|
||||
const translateOriginTexts = targetStrs.reduce((prev, curr, i) => {
|
||||
// 截取文案前5位,仅包含中文和字母
|
||||
const transOriginText = getTransOriginText(curr.text);
|
||||
if (i === 0) {
|
||||
return transOriginText;
|
||||
}
|
||||
return `${prev}${delimiter}${transOriginText}`;
|
||||
}, []);
|
||||
translateTexts = yield utils_1.translateKeyText(translateOriginTexts, origin);
|
||||
// 翻译后的文案
|
||||
translateTexts = yield utils_1.translateWithBaiduPinyin(translateOriginTexts, origin);
|
||||
}
|
||||
else {
|
||||
// google并发性较好,且未找到有效的分隔符,故仍然逐个文案进行翻译
|
||||
@ -226,24 +267,27 @@ function extractAll({ dirPath, prefix }) {
|
||||
}, []);
|
||||
[...translateTexts] = yield Promise.all(translatePromises);
|
||||
}
|
||||
// 翻译结果示例:https://imoaix.cn/canary-clis/2.png
|
||||
if (translateTexts.length === 0) {
|
||||
utils_1.failInfo(`未得到翻译结果,${currentFilename}替换失败!`);
|
||||
utils_1.failInfo(`未得到翻译结果,${filePath}替换失败!`);
|
||||
return;
|
||||
}
|
||||
const replaceableStrs = getReplaceableStrs(currentFilename, langsPrefix, translateTexts, targetStrs);
|
||||
yield replaceableStrs
|
||||
.reduce((prev, obj) => {
|
||||
// 统一处理Key值,如翻译后结果相同加上出现次数,结果示例:https://imoaix.cn/canary-clis/3.png
|
||||
const langKeys = getLangKeys(filePath, langsPrefix, translateTexts, targetStrs);
|
||||
yield langKeys
|
||||
.reduce((prev, { target, key, needWrite }) => {
|
||||
return prev.then(() => {
|
||||
return replace_1.replaceAndUpdate(currentFilename, obj.target, `I18N.${obj.key}`, false, obj.needWrite);
|
||||
// 根据生成的键值对更新源码文件以及语言文件
|
||||
return replace_1.replaceAndUpdate(filePath, target, `I18N.${key}`, false, needWrite);
|
||||
});
|
||||
}, Promise.resolve())
|
||||
.then(() => {
|
||||
// 添加 import I18N
|
||||
if (!replace_1.hasImportI18N(currentFilename)) {
|
||||
const code = replace_1.createImportI18N(currentFilename);
|
||||
file_1.writeFile(currentFilename, code);
|
||||
if (!replace_1.hasImportI18N(filePath)) {
|
||||
const code = replace_1.createImportI18N(filePath);
|
||||
file_1.writeFile(filePath, code);
|
||||
}
|
||||
utils_1.successInfo(`${currentFilename} 替换完成,共替换 ${targetStrs.length} 处文案!`);
|
||||
utils_1.successInfo(`${filePath} 替换完成,共替换 ${targetStrs.length} 处文案!\n`);
|
||||
})
|
||||
.catch(e => {
|
||||
utils_1.failInfo(e.message);
|
||||
|
2
dist/extract/extract.js.map
vendored
2
dist/extract/extract.js.map
vendored
File diff suppressed because one or more lines are too long
12
dist/extract/file.js
vendored
12
dist/extract/file.js
vendored
@ -1,10 +1,10 @@
|
||||
"use strict";
|
||||
/**
|
||||
* @author doubledream
|
||||
* @author zhaoyingbo
|
||||
* @desc 文件处理方法
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isDirectory = exports.isFile = exports.writeFile = exports.readFile = exports.getSpecifiedFiles = void 0;
|
||||
exports.getSuffix = exports.isDirectory = exports.isFile = exports.writeFile = exports.readFile = exports.getSpecifiedFiles = void 0;
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
/**
|
||||
@ -71,4 +71,12 @@ function isDirectory(path) {
|
||||
return fs.statSync(path).isDirectory();
|
||||
}
|
||||
exports.isDirectory = isDirectory;
|
||||
/**
|
||||
* 获取文件尾缀
|
||||
* @param path
|
||||
*/
|
||||
function getSuffix(path) {
|
||||
return path.split('.').pop();
|
||||
}
|
||||
exports.getSuffix = getSuffix;
|
||||
//# sourceMappingURL=file.js.map
|
2
dist/extract/file.js.map
vendored
2
dist/extract/file.js.map
vendored
@ -1 +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"}
|
||||
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/extract/file.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6BAA4B;AAE5B,yBAAwB;AAExB;;;;;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,CAAA;QACjC,MAAM,WAAW,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;QACnD,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAA;QAEzC,IAAI,WAAW,EAAE;YACf,OAAO,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC,CAAA;SAC1E;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,CAAA;QACjC,MAAM,YAAY,GAAG,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,CAAA;QAEtF,IAAI,MAAM,IAAI,iBAAiB,IAAI,YAAY,EAAE;YAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;SAC1B;QACD,OAAO,KAAK,CAAA;IACd,CAAC,EAAE,EAAE,CAAC,CAAA;AACR,CAAC;AA8CQ,8CAAiB;AA5C1B;;;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,CAAA;KAC1C;AACH,CAAC;AAoC2B,4BAAQ;AAlCpC;;;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,CAAA;KACjC;AACH,CAAC;AA0BqC,8BAAS;AAxB/C;;;GAGG;AACH,SAAS,MAAM,CAAC,IAAI;IAClB,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAA;AACnC,CAAC;AAkBgD,wBAAM;AAhBvD;;;GAGG;AACH,SAAS,WAAW,CAAC,IAAI;IACvB,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;AACxC,CAAC;AAUwD,kCAAW;AARpE;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAI;IACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;AAC9B,CAAC;AAEqE,8BAAS"}
|
37
dist/extract/getLangData.js
vendored
37
dist/extract/getLangData.js
vendored
@ -4,16 +4,18 @@
|
||||
* @desc 获取语言文件
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getLangData = exports.getSuggestLangObj = void 0;
|
||||
exports.getLangData = exports.getSrcLangObj = 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`;
|
||||
// 匹配文件
|
||||
const I18N_GLOB = `${LANG_DIR}/**/*.${CONFIG.isJsProj ? 'js' : 'ts'}`;
|
||||
/**
|
||||
* 获取对应文件的语言
|
||||
* 获取制定文件JSON内容
|
||||
*/
|
||||
function getLangData(fileName) {
|
||||
if (fs.existsSync(fileName)) {
|
||||
@ -29,8 +31,9 @@ exports.getLangData = getLangData;
|
||||
*/
|
||||
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 obj = fileContent.match(/export\s*default\s*({[\s\S]+)?$/)[1];
|
||||
obj = obj.replace(/\s*\s*$/, '');
|
||||
obj = obj.replace(';', '');
|
||||
let jsObj = {};
|
||||
try {
|
||||
jsObj = eval('(' + obj + ')');
|
||||
@ -41,18 +44,27 @@ function getLangJson(fileName) {
|
||||
}
|
||||
return jsObj;
|
||||
}
|
||||
/**
|
||||
* 获取项目原语言的I18N内容
|
||||
* @return 示例:{ 'common.ts': { test: '测试', chinese: '中国' } }
|
||||
*/
|
||||
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') {
|
||||
.split('.')
|
||||
.shift();
|
||||
// 排除index文件
|
||||
if (filename === 'index') {
|
||||
return prev;
|
||||
}
|
||||
// 获取文件内容
|
||||
const fileContent = getLangData(curr);
|
||||
let jsObj = fileContent;
|
||||
// 空对象,说明获取失败
|
||||
if (Object.keys(jsObj).length === 0) {
|
||||
console.log(`\`${curr}\` 解析失败,该文件包含的文案无法自动补全`);
|
||||
}
|
||||
@ -61,12 +73,11 @@ function getI18N() {
|
||||
return langObj;
|
||||
}
|
||||
/**
|
||||
* 获取全部语言, 展平
|
||||
* 获取项目原语言的配置, 展平
|
||||
* @return 示例:{ 'common.test': '测试', 'common.chinese': '中国' }
|
||||
*/
|
||||
function getSuggestLangObj() {
|
||||
const langObj = getI18N();
|
||||
const finalLangObj = utils_1.flatten(langObj);
|
||||
return finalLangObj;
|
||||
function getSrcLangObj() {
|
||||
return utils_1.flatten(getI18N());
|
||||
}
|
||||
exports.getSuggestLangObj = getSuggestLangObj;
|
||||
exports.getSrcLangObj = getSrcLangObj;
|
||||
//# sourceMappingURL=getLangData.js.map
|
2
dist/extract/getLangData.js.map
vendored
2
dist/extract/getLangData.js.map
vendored
@ -1 +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"}
|
||||
{"version":3,"file":"getLangData.js","sourceRoot":"","sources":["../../src/extract/getLangData.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,iCAAgC;AAChC,yBAAwB;AACxB,6BAA4B;AAC5B,oCAAoD;AAEpD,MAAM,MAAM,GAAG,wBAAgB,EAAE,CAAA;AACjC,cAAc;AACd,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;AAC/D,OAAO;AACP,MAAM,SAAS,GAAG,GAAG,QAAQ,SAAS,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAErE;;GAEG;AACH,SAAS,WAAW,CAAC,QAAQ;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC3B,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAA;KAC7B;SAAM;QACL,OAAO,EAAE,CAAA;KACV;AACH,CAAC;AA6DuB,kCAAW;AA3DnC;;GAEG;AACH,SAAS,WAAW,CAAC,QAAQ;IAC3B,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;IACnE,IAAI,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAChC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC1B,IAAI,KAAK,GAAG,EAAE,CAAA;IACd,IAAI;QACF,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAA;KAC9B;IAAC,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAChB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;KACnB;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,OAAO;IACd,eAAe;IACf,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACpC,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,KAAK,CAAC,GAAG,CAAC;aACV,KAAK,EAAE,CAAA;QACV,YAAY;QACZ,IAAI,QAAQ,KAAK,OAAO,EAAE;YACxB,OAAO,IAAI,CAAA;SACZ;QACD,SAAS;QACT,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;QACrC,IAAI,KAAK,GAAG,WAAW,CAAA;QACvB,aAAa;QACb,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;YACnC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,wBAAwB,CAAC,CAAA;SAC/C;QAED,uCACK,IAAI,KACP,CAAC,QAAQ,CAAC,EAAE,KAAK,IAClB;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IACN,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa;IACpB,OAAO,eAAO,CAAC,OAAO,EAAE,CAAC,CAAA;AAC3B,CAAC;AAEQ,sCAAa"}
|
199
dist/extract/replace.js
vendored
199
dist/extract/replace.js
vendored
@ -14,35 +14,6 @@ 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
|
||||
@ -60,44 +31,101 @@ function prettierFile(fileContent) {
|
||||
return fileContent;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 新建一个新的语言文件
|
||||
* @param key 语言文件中的I80N值
|
||||
* @param value 语言文件中的I18N值
|
||||
*/
|
||||
function generateNewLangFile(key, value) {
|
||||
const obj = _.set({}, key, value);
|
||||
return prettierFile(`export default ${JSON.stringify(obj, null, 2)}`);
|
||||
}
|
||||
/**
|
||||
* 在语言index文件中新增语言文件引入
|
||||
* @param newFilename 新的语言文件名
|
||||
*/
|
||||
function addImportToMainLangFile(newFilename) {
|
||||
const indexFileSrc = `${srcLangDir}/index.${CONFIG.isJsProj ? 'js' : 'ts'}`;
|
||||
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`);
|
||||
// 如果已经存在index文件则更新内容
|
||||
if (fs.existsSync(indexFileSrc)) {
|
||||
mainContent = fs.readFileSync(indexFileSrc, '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`);
|
||||
// 最后一行不包含,号
|
||||
mainContent = mainContent.replace(/\n(}\))/, `,\n ${newFilename},\n$1`);
|
||||
}
|
||||
}
|
||||
// 兼容 export default { common };的写法
|
||||
if (/(};)/.test(mainContent)) {
|
||||
if (/\,\n(};)/.test(mainContent)) {
|
||||
/** 最后一行包含,号 */
|
||||
mainContent = mainContent.replace(/(};)/, ` ${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`);
|
||||
// 最后一行不包含,号
|
||||
mainContent = mainContent.replace(/\n(})/, `,\n ${newFilename},\n$1`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 还未存在index文件,生成初始值
|
||||
else {
|
||||
mainContent = `import ${newFilename} from './${newFilename}';\n\nexport default Object.assign({}, {\n ${newFilename},\n});`;
|
||||
mainContent = `import ${newFilename} from './${newFilename}'\n\nexport default Object.assign({}, {\n ${newFilename},\n})`;
|
||||
}
|
||||
fs.writeFileSync(`${srcLangDir}/index.ts`, mainContent);
|
||||
// 写入文件
|
||||
fs.writeFileSync(indexFileSrc, mainContent);
|
||||
}
|
||||
/**
|
||||
* 检查是否添加 import I18N 命令
|
||||
* 更新语言文件
|
||||
* @param key 代码文件中的I18N键
|
||||
* @param val 语言文件中的I80N值
|
||||
* @param validateDuplicate 是否校验重复
|
||||
*/
|
||||
function updateLangFiles(key, val, validateDuplicate) {
|
||||
// 过滤掉不是I18N开头的Key
|
||||
if (!_.startsWith(key, 'I18N.')) {
|
||||
return;
|
||||
}
|
||||
// 拆分代码文件中的键
|
||||
const [, filename, ...restPath] = key.split('.');
|
||||
// 语言文件中的I18N键
|
||||
const fullKey = restPath.join('.');
|
||||
// 语言文件路径
|
||||
const langFilePath = `${srcLangDir}/${filename}.${CONFIG.isJsProj ? 'js' : 'ts'}`;
|
||||
// 还未生成对应的语言文件
|
||||
if (!fs.existsSync(langFilePath)) {
|
||||
// 生成新的语言文件内容并写入
|
||||
fs.writeFileSync(langFilePath, generateNewLangFile(fullKey, val));
|
||||
// 在index文件中增加语言文件引入
|
||||
addImportToMainLangFile(filename);
|
||||
utils_1.successInfo(`成功新建语言文件 ${langFilePath}`);
|
||||
}
|
||||
else {
|
||||
// 清除 require 缓存,解决手动更新语言文件后再自动抽取,导致之前更新失效的问题
|
||||
const mainContent = getLangData_1.getLangData(langFilePath);
|
||||
// 解析失败会返回{},需要过滤下
|
||||
if (Object.keys(mainContent).length === 0) {
|
||||
utils_1.failInfo(`${filename} 解析失败,该文件包含的文案无法自动补全`);
|
||||
}
|
||||
// 校验是否有重复的Key
|
||||
if (validateDuplicate && _.get(mainContent, fullKey) !== undefined) {
|
||||
utils_1.failInfo(`${langFilePath} 中已存在 key 为 \`${fullKey}\` 的翻译,请重新命名变量`);
|
||||
throw new Error('duplicate');
|
||||
}
|
||||
// \n 会被自动转义成 \\n,这里转回来
|
||||
val = val.replace(/\\n/gm, '\n');
|
||||
// 写入语言文件
|
||||
_.set(mainContent, fullKey, val);
|
||||
fs.writeFileSync(langFilePath, prettierFile(`export default ${JSON.stringify(mainContent, null, 2)}`));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查代码文件是否添加过 import I18N 命令
|
||||
* @param filePath 文件路径
|
||||
*/
|
||||
function hasImportI18N(filePath) {
|
||||
@ -107,7 +135,7 @@ function hasImportI18N(filePath) {
|
||||
function visit(node) {
|
||||
if (node.kind === ts.SyntaxKind.ImportDeclaration) {
|
||||
const importClause = node.importClause;
|
||||
// import I18N from 'src/utils/I18N';
|
||||
// import I18N from 'src/utils/I18N'
|
||||
if (_.get(importClause, 'kind') === ts.SyntaxKind.ImportClause) {
|
||||
if (importClause.name) {
|
||||
if (importClause.name.escapedText === 'I18N') {
|
||||
@ -116,7 +144,7 @@ function hasImportI18N(filePath) {
|
||||
}
|
||||
else {
|
||||
const namedBindings = importClause.namedBindings;
|
||||
// import { I18N } from 'src/utils/I18N';
|
||||
// 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') {
|
||||
@ -124,7 +152,7 @@ function hasImportI18N(filePath) {
|
||||
}
|
||||
});
|
||||
}
|
||||
// import * as I18N from 'src/utils/I18N';
|
||||
// import * as I18N from 'src/utils/I18N'
|
||||
if (namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
|
||||
if (_.get(namedBindings, 'name.escapedText') === 'I18N') {
|
||||
hasImportI18N = true;
|
||||
@ -145,16 +173,16 @@ exports.hasImportI18N = hasImportI18N;
|
||||
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 isNomalFile = ['ts', 'js', 'tsx', 'jsx'].includes(file_1.getSuffix(filePath));
|
||||
const isVueFile = _.endsWith(filePath, '.vue');
|
||||
if (isTsFile || isTsxFile || isJsFile) {
|
||||
// 正常文件开头添加import
|
||||
if (isNomalFile) {
|
||||
const importStatement = `${CONFIG.importI18N}\n`;
|
||||
const pos = ast.getStart(ast, false);
|
||||
const updateCode = code.slice(0, pos) + importStatement + code.slice(pos);
|
||||
return updateCode;
|
||||
}
|
||||
// Vue文件开头添加import
|
||||
else if (isVueFile) {
|
||||
const importStatement = `${CONFIG.importI18N}\n`;
|
||||
const updateCode = code.replace(/<script>/g, `<script>\n${importStatement}`);
|
||||
@ -163,66 +191,87 @@ function createImportI18N(filePath) {
|
||||
}
|
||||
exports.createImportI18N = createImportI18N;
|
||||
/**
|
||||
* 更新文件
|
||||
* @param filePath 当前文件路径
|
||||
* @param arg 目标字符串对象
|
||||
* @param val 目标 key
|
||||
* 更新语言文件以及源代码
|
||||
* @param filePath 当前代码文件路径
|
||||
* @param target 目标字符串对象
|
||||
* @param key 目标 key
|
||||
* @param validateDuplicate 是否校验文件中已经存在要写入的 key
|
||||
* @param needWrite 是否只需要替换不需要更新 langs 文件
|
||||
*/
|
||||
function replaceAndUpdate(filePath, arg, val, validateDuplicate, needWrite = true) {
|
||||
function replaceAndUpdate(filePath, target, key, validateDuplicate, needWrite = true) {
|
||||
// 获取目标字段内容
|
||||
const { text, range: { start, end }, isString } = target;
|
||||
// 获取当前代码内容
|
||||
const code = file_1.readFile(filePath);
|
||||
const isHtmlFile = _.endsWith(filePath, '.html');
|
||||
const isVueFile = _.endsWith(filePath, '.vue');
|
||||
// 写回代码文件的内容
|
||||
let newCode = code;
|
||||
let finalReplaceText = arg.text;
|
||||
const { start, end } = arg.range;
|
||||
// 中文语言结果
|
||||
let cnVal = text;
|
||||
// 若是字符串,删掉两侧的引号
|
||||
if (arg.isString) {
|
||||
if (isString) {
|
||||
// 写回代码文件的I18N键
|
||||
let codeKey = key;
|
||||
// 如果引号左侧是 等号,则可能是 jsx 的 props,此时要替换成 {
|
||||
const preTextStart = start - 1;
|
||||
const [last2Char, last1Char] = code.slice(preTextStart, start + 1).split('');
|
||||
let finalReplaceVal = val;
|
||||
if (last2Char === '=') {
|
||||
if (isHtmlFile) {
|
||||
finalReplaceVal = '{{' + val + '}}';
|
||||
codeKey = '{{' + key + '}}';
|
||||
}
|
||||
else if (isVueFile) {
|
||||
finalReplaceVal = '{{' + val + '}}';
|
||||
codeKey = '{{' + key + '}}';
|
||||
}
|
||||
else {
|
||||
finalReplaceVal = '{' + val + '}';
|
||||
codeKey = '{' + key + '}';
|
||||
}
|
||||
}
|
||||
// 若是模板字符串,看看其中是否包含变量
|
||||
if (last1Char === '`') {
|
||||
const varInStr = arg.text.match(/(\$\{[^\}]+?\})/g);
|
||||
// 正则可视化 https://c.runoob.com/front-end/7625/#!flags=&re=(%5C%24%5C%7B%5B%5E%5C%7D%5D%2B%3F%5C%7D)
|
||||
// 匹配模板字符串内的全部变量内容
|
||||
const varInStr = text.match(/(\$\{[^\}]+?\})/g);
|
||||
// 如果存在变量
|
||||
if (varInStr) {
|
||||
/**
|
||||
* 取出变量内容并用val{x}指代
|
||||
* 示例:[ 'val1: a', "val2: '全球每个人'" ]
|
||||
*/
|
||||
const kvPair = varInStr.map((str, index) => {
|
||||
return `val${index + 1}: ${str.replace(/^\${([^\}]+)\}$/, '$1')}`;
|
||||
});
|
||||
finalReplaceVal = `I18N.template(${val}, { ${kvPair.join(',\n')} })`;
|
||||
/**
|
||||
* 包装模板字符串的I18N替换代码,用于写回代码文件
|
||||
* 示例:I18N.template(I18N.home.index.shiZhongJianChiZuo, { val1: a, val2: '全球每个人' })
|
||||
*/
|
||||
codeKey = `I18N.template(${key}, { ${kvPair.join(',\n')} })`;
|
||||
/**
|
||||
* 使用val{x}替换掉原模板字符串的变量,用于生成语言文件
|
||||
* 示例:始终坚持做“{val1}”的好产品,让{val2}都能享受科技带来的美好生活
|
||||
*/
|
||||
varInStr.forEach((str, index) => {
|
||||
finalReplaceText = finalReplaceText.replace(str, `{val${index + 1}}`);
|
||||
cnVal = cnVal.replace(str, `{val${index + 1}}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
newCode = `${code.slice(0, start)}${finalReplaceVal}${code.slice(end)}`;
|
||||
// 将I18N替换代码写回代码文件
|
||||
newCode = `${code.slice(0, start)}${codeKey}${code.slice(end)}`;
|
||||
}
|
||||
else {
|
||||
if (isHtmlFile || isVueFile) {
|
||||
newCode = `${code.slice(0, start)}{{${val}}}${code.slice(end)}`;
|
||||
newCode = `${code.slice(0, start)}{{${key}}}${code.slice(end)}`;
|
||||
}
|
||||
else {
|
||||
newCode = `${code.slice(0, start)}{${val}}${code.slice(end)}`;
|
||||
newCode = `${code.slice(0, start)}{${key}}${code.slice(end)}`;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (needWrite) {
|
||||
// 更新语言文件
|
||||
updateLangFiles(val, finalReplaceText, validateDuplicate);
|
||||
updateLangFiles(key, cnVal, validateDuplicate);
|
||||
}
|
||||
// 若更新成功再替换代码
|
||||
// // 若更新成功再替换代码
|
||||
return file_1.writeFile(filePath, newCode);
|
||||
}
|
||||
catch (e) {
|
||||
|
2
dist/extract/replace.js.map
vendored
2
dist/extract/replace.js.map
vendored
File diff suppressed because one or more lines are too long
6
dist/index.js
vendored
6
dist/index.js
vendored
@ -128,12 +128,12 @@ if (commander.translate) {
|
||||
}));
|
||||
}
|
||||
if (commander.extract) {
|
||||
console.log(lodash_1.isString(commander.prefix));
|
||||
if (commander.prefix === true) {
|
||||
console.log('请指定翻译后文案 key 值的前缀 --prefix xxxx');
|
||||
}
|
||||
else if (lodash_1.isString(commander.prefix) && !new RegExp(/^I18N(\.[-_a-zA-Z1-9$]+)+$/).test(commander.prefix)) {
|
||||
console.log('前缀必须以I18N开头,后续跟上字母、下滑线、破折号、$ 字符组成的变量名');
|
||||
// 正则可视化 https://c.runoob.com/front-end/7625/#!flags=&re=%5EI18N(%5C.%5B_a-zA-Z1-9%24%5D%2B)%2B%24
|
||||
else if (lodash_1.isString(commander.prefix) && !new RegExp(/^I18N(\.[_a-zA-Z1-9$]+)+$/).test(commander.prefix)) {
|
||||
console.log('前缀必须以I18N开头,后续跟上字母、下滑线、破折号、$ 字符组成的变量名,如:I18N.yingbo');
|
||||
}
|
||||
else {
|
||||
const extractAllParams = {
|
||||
|
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
10
dist/init.js
vendored
10
dist/init.js
vendored
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
/**
|
||||
* @author zhaoyingbo
|
||||
* @desc 初始化 kiwi 项目的文件以及配置
|
||||
* @desc 初始化 canary 项目的文件以及配置
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.initProject = void 0;
|
||||
@ -28,10 +28,10 @@ function createConfigFile(existDir) {
|
||||
fs.writeFile(configDir, JSON.stringify(config, null, 2), err => err && console.log(err));
|
||||
}
|
||||
/**
|
||||
* 创建中文配置示例文件
|
||||
* 创建中文语言示例文件
|
||||
*/
|
||||
function createCnFile() {
|
||||
// 中文配置文件夹地址
|
||||
// 中文语言文件夹地址
|
||||
const cnDir = `${const_1.PROJECT_CONFIG.dir}/zh-CN`;
|
||||
// 没有则创建
|
||||
if (!fs.existsSync(cnDir)) {
|
||||
@ -42,7 +42,7 @@ function createCnFile() {
|
||||
}
|
||||
/**
|
||||
* 初始化国际化项目
|
||||
* @param existDir 配置文件夹地址
|
||||
* @param existDir 多语言文件夹地址
|
||||
*/
|
||||
function initProject(existDir) {
|
||||
// 有用户输入的文件夹,不存在默认位置创建
|
||||
@ -56,7 +56,7 @@ function initProject(existDir) {
|
||||
}
|
||||
// 创建配置文件
|
||||
createConfigFile(existDir);
|
||||
// 没有已经存在的配置文件夹,创建默认的配置文件示例
|
||||
// 没有已经存在的多语言文件夹,创建默认的中文语言示例文件
|
||||
if (!(existDir && fs.existsSync(existDir))) {
|
||||
createCnFile();
|
||||
}
|
||||
|
2
dist/init.js.map
vendored
2
dist/init.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,6BAA6B;AAC7B,yBAAyB;AACzB,mCAA6D;AAE7D;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,QAAiB;IACzC,aAAa;IACb,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,0BAAkB,EAAE,CAAC,CAAC;IACzE,iBAAiB;IACjB,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO;IACrC,MAAM,MAAM,qBAAQ,sBAAc,CAAC,aAAa,CAAE,CAAC;IACnD,SAAS;IACT,IAAI,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QACvC,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC;KAC7B;IACD,YAAY;IACZ,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED;;GAEG;AACH,SAAS,YAAY;IACnB,YAAY;IACZ,MAAM,KAAK,GAAG,GAAG,sBAAc,CAAC,GAAG,QAAQ,CAAC;IAC5C,QAAQ;IACR,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;QACzB,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACpB,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,WAAW,EAAE,sBAAc,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9F,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,YAAY,EAAE,sBAAc,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;KAC/F;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,QAAiB;IACpC,sBAAsB;IACtB,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QACxC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACrC,EAAE,CAAC,SAAS,CAAC,sBAAc,CAAC,GAAG,CAAC,CAAC;KAClC;IACD,cAAc;SACT,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,sBAAc,CAAC,GAAG,CAAC,EAAE;QACxD,EAAE,CAAC,SAAS,CAAC,sBAAc,CAAC,GAAG,CAAC,CAAC;KAClC;IACD,SAAS;IACT,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,2BAA2B;IAC3B,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE;QAC1C,YAAY,EAAE,CAAC;KAChB;AACH,CAAC;AAEQ,kCAAW"}
|
||||
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,6BAA4B;AAC5B,yBAAwB;AACxB,mCAA4D;AAE5D;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,QAAiB;IACzC,aAAa;IACb,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,0BAAkB,EAAE,CAAC,CAAA;IACxE,iBAAiB;IACjB,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAM;IACpC,MAAM,MAAM,qBAAQ,sBAAc,CAAC,aAAa,CAAE,CAAA;IAClD,SAAS;IACT,IAAI,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QACvC,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAA;KAC5B;IACD,YAAY;IACZ,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;AAC1F,CAAC;AAED;;GAEG;AACH,SAAS,YAAY;IACnB,YAAY;IACZ,MAAM,KAAK,GAAG,GAAG,sBAAc,CAAC,GAAG,QAAQ,CAAA;IAC3C,QAAQ;IACR,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;QACzB,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACnB,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,WAAW,EAAE,sBAAc,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7F,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,YAAY,EAAE,sBAAc,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;KAC9F;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,QAAiB;IACpC,sBAAsB;IACtB,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QACxC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;QACpC,EAAE,CAAC,SAAS,CAAC,sBAAc,CAAC,GAAG,CAAC,CAAA;KACjC;IACD,cAAc;SACT,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,sBAAc,CAAC,GAAG,CAAC,EAAE;QACxD,EAAE,CAAC,SAAS,CAAC,sBAAc,CAAC,GAAG,CAAC,CAAA;KACjC;IACD,SAAS;IACT,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAC1B,8BAA8B;IAC9B,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE;QAC1C,YAAY,EAAE,CAAA;KACf;AACH,CAAC;AAEQ,kCAAW"}
|
2
dist/unused.js
vendored
2
dist/unused.js
vendored
@ -10,7 +10,7 @@ const path = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
const lookingForString = '';
|
||||
function findUnUsed() {
|
||||
const srcLangDir = path.resolve(utils_1.getKiwiDir(), 'zh-CN');
|
||||
const srcLangDir = path.resolve(utils_1.getCanaryDir(), 'zh-CN');
|
||||
let files = fs.readdirSync(srcLangDir);
|
||||
files = files.filter(file => file.endsWith('.ts') && file !== 'index.ts');
|
||||
const unUnsedKeys = [];
|
||||
|
2
dist/unused.js.map
vendored
2
dist/unused.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"unused.js","sourceRoot":"","sources":["../src/unused.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"unused.js","sourceRoot":"","sources":["../src/unused.ts"],"names":[],"mappings":"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"}
|
24
dist/utils.js
vendored
24
dist/utils.js
vendored
@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.highlightText = exports.failInfo = exports.successInfo = exports.translateKeyText = exports.getTranslateOriginType = exports.lookForFiles = exports.flatten = exports.findMatchValue = exports.findMatchKey = exports.translateText = exports.getProjectConfig = exports.getAllMessages = exports.withTimeout = exports.retry = exports.traverse = exports.getLangDir = exports.getKiwiDir = void 0;
|
||||
exports.highlightText = exports.failInfo = exports.successInfo = exports.translateWithBaiduPinyin = exports.getTranslateOriginType = exports.lookForFiles = exports.flatten = exports.findMatchKey = exports.translateText = exports.getProjectConfig = exports.getAllMessages = exports.withTimeout = exports.retry = exports.traverse = exports.getLangDir = exports.getCanaryDir = void 0;
|
||||
/**
|
||||
* @author linhuiw
|
||||
* @desc 工具方法
|
||||
@ -56,19 +56,19 @@ exports.getProjectConfig = getProjectConfig;
|
||||
/**
|
||||
* 获取语言资源的根目录
|
||||
*/
|
||||
function getKiwiDir() {
|
||||
function getCanaryDir() {
|
||||
const config = getProjectConfig();
|
||||
if (config) {
|
||||
return config.canaryDir;
|
||||
}
|
||||
}
|
||||
exports.getKiwiDir = getKiwiDir;
|
||||
exports.getCanaryDir = getCanaryDir;
|
||||
/**
|
||||
* 获取对应语言的目录位置
|
||||
* @param lang
|
||||
*/
|
||||
function getLangDir(lang) {
|
||||
const langsDir = getKiwiDir();
|
||||
const langsDir = getCanaryDir();
|
||||
return path.resolve(langsDir, lang);
|
||||
}
|
||||
exports.getLangDir = getLangDir;
|
||||
@ -164,9 +164,9 @@ function translateText(text, toLang) {
|
||||
}
|
||||
exports.translateText = translateText;
|
||||
/**
|
||||
* 翻译中文
|
||||
* 使用百度或者拼音翻译中文
|
||||
*/
|
||||
function translateKeyText(text, origin) {
|
||||
function translateWithBaiduPinyin(text, origin) {
|
||||
const CONFIG = getProjectConfig();
|
||||
const { appId, appKey } = CONFIG.baiduApiKey;
|
||||
const baiduTranslate = require('baidu-translate');
|
||||
@ -194,20 +194,16 @@ function translateKeyText(text, origin) {
|
||||
}
|
||||
return retry(_translateText, 3);
|
||||
}
|
||||
exports.translateKeyText = translateKeyText;
|
||||
exports.translateWithBaiduPinyin = translateWithBaiduPinyin;
|
||||
function findMatchKey(langObj, text) {
|
||||
for (const key in langObj) {
|
||||
if (langObj[key] === text) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
exports.findMatchKey = findMatchKey;
|
||||
function findMatchValue(langObj, key) {
|
||||
return langObj[key];
|
||||
}
|
||||
exports.findMatchValue = findMatchValue;
|
||||
/**
|
||||
* 将对象拍平
|
||||
* @param obj 原始对象
|
||||
@ -274,8 +270,8 @@ exports.getTranslateOriginType = getTranslateOriginType;
|
||||
/**
|
||||
* 成功的提示
|
||||
*/
|
||||
function successInfo(message) {
|
||||
console.log('successInfo: ', colors.green(message));
|
||||
function successInfo(message, needEnter = false) {
|
||||
console.log(`${needEnter ? '\n' : ''}successInfo: `, colors.green(message));
|
||||
}
|
||||
exports.successInfo = successInfo;
|
||||
/**
|
||||
|
2
dist/utils.js.map
vendored
2
dist/utils.js.map
vendored
File diff suppressed because one or more lines are too long
@ -8,6 +8,7 @@ export const CANARY_CONFIG_FILE = 'canary-config.json';
|
||||
export const PROJECT_CONFIG = {
|
||||
dir: './.canary',
|
||||
defaultConfig: {
|
||||
isJsProj: false,
|
||||
canaryDir: './.canary',
|
||||
srcLang: 'zh-CN',
|
||||
distLangs: ['en-US', 'zh-CN'],
|
||||
|
@ -1,69 +1,78 @@
|
||||
/**
|
||||
* @author doubledream
|
||||
* @author zhaoyingbo
|
||||
* @desc 提取指定文件夹下的中文
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import * as slash from 'slash2';
|
||||
import * as path from 'path';
|
||||
import * as colors from 'colors';
|
||||
import * as _ from 'lodash'
|
||||
import * as slash from 'slash2'
|
||||
import * as path from 'path'
|
||||
import * as colors from 'colors'
|
||||
|
||||
import { getSpecifiedFiles, readFile, writeFile, isFile, isDirectory } from './file';
|
||||
import { findChineseText } from './findChineseText';
|
||||
import { getSuggestLangObj } from './getLangData';
|
||||
import { getSpecifiedFiles, readFile, writeFile, isFile, isDirectory, getSuffix } from './file'
|
||||
import { findChineseText } from './findChineseText'
|
||||
import { getSrcLangObj } from './getLangData'
|
||||
import {
|
||||
translateText,
|
||||
findMatchKey,
|
||||
findMatchValue,
|
||||
translateKeyText,
|
||||
translateWithBaiduPinyin,
|
||||
successInfo,
|
||||
failInfo,
|
||||
highlightText
|
||||
} from '../utils';
|
||||
import { replaceAndUpdate, hasImportI18N, createImportI18N } from './replace';
|
||||
import { getProjectConfig } from '../utils';
|
||||
} from '../utils'
|
||||
import { replaceAndUpdate, hasImportI18N, createImportI18N } from './replace'
|
||||
import { getProjectConfig } from '../utils'
|
||||
|
||||
const CONFIG = getProjectConfig();
|
||||
const CONFIG = getProjectConfig()
|
||||
|
||||
/**
|
||||
* 剔除 kiwiDir 下的文件
|
||||
* 剔除配置文件夹下的文件
|
||||
*/
|
||||
function removeLangsFiles(files: string[]) {
|
||||
const langsDir = path.resolve(process.cwd(), CONFIG.canaryDir);
|
||||
const langsDir = path.resolve(process.cwd(), CONFIG.canaryDir)
|
||||
return files.filter(file => {
|
||||
const completeFile = path.resolve(process.cwd(), file);
|
||||
return !completeFile.includes(langsDir);
|
||||
});
|
||||
const completeFile = path.resolve(process.cwd(), file)
|
||||
return !completeFile.includes(langsDir)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归匹配项目中所有的代码的中文
|
||||
*/
|
||||
function findAllChineseText(dir: string) {
|
||||
const first = dir.split(',')[0];
|
||||
let files = [];
|
||||
if (isDirectory(first)) {
|
||||
const dirPath = path.resolve(process.cwd(), dir);
|
||||
files = getSpecifiedFiles(dirPath, CONFIG.ignoreDir, CONFIG.ignoreFile);
|
||||
} else {
|
||||
files = removeLangsFiles(dir.split(','));
|
||||
}
|
||||
const filterFiles = files.filter(file => {
|
||||
return (isFile(file) && file.endsWith('.ts')) || (isFile(file) && file.endsWith('.js')) || file.endsWith('.tsx') || file.endsWith('.vue');
|
||||
});
|
||||
const allTexts = filterFiles.reduce((pre, file) => {
|
||||
const code = readFile(file);
|
||||
const texts = findChineseText(code, file);
|
||||
// 使用逗号分割用户输入,可以混合输入文件和文件夹
|
||||
const dirList = dir.split(' ')
|
||||
let files = []
|
||||
// 获取用户输入的全部文件
|
||||
dirList.forEach(dir => {
|
||||
const dirPath = path.resolve(process.cwd(), dir)
|
||||
// 输入文件夹
|
||||
if (isDirectory(dir)) {
|
||||
files.push(...getSpecifiedFiles(dirPath, CONFIG.ignoreDir, CONFIG.ignoreFile))
|
||||
}
|
||||
// 输入文件
|
||||
else {
|
||||
files.push(dirPath)
|
||||
}
|
||||
})
|
||||
// 过滤输入中包含的配置文件夹下的文件
|
||||
files = removeLangsFiles(files)
|
||||
// 过滤非代码文件
|
||||
files = files.filter(file => {
|
||||
return (isFile(file) && ['ts', 'js', 'tsx', 'jsx', 'vue'].includes(getSuffix(file)))
|
||||
})
|
||||
// 匹配代码文件中的中文
|
||||
const allTexts = files.reduce((pre, file) => {
|
||||
const code = readFile(file)
|
||||
const texts = findChineseText(code, file)
|
||||
// 调整文案顺序,保证从后面的文案往前替换,避免位置更新导致替换出错
|
||||
const sortTexts = _.sortBy(texts, obj => -obj.range.start);
|
||||
const sortTexts = _.sortBy(texts, obj => -obj.range.start)
|
||||
if (texts.length > 0) {
|
||||
console.log(`${highlightText(file)} 发现 ${highlightText(texts.length)} 处中文文案`);
|
||||
console.log(`${highlightText(file)} 发现 ${highlightText(texts.length)} 处中文文案`)
|
||||
}
|
||||
|
||||
return texts.length > 0 ? pre.concat({ file, texts: sortTexts }) : pre;
|
||||
}, []);
|
||||
|
||||
return allTexts;
|
||||
return texts.length > 0 ? pre.concat({ file, texts: sortTexts }) : pre
|
||||
}, [])
|
||||
return allTexts
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,223 +80,251 @@ function findAllChineseText(dir: string) {
|
||||
*/
|
||||
function getTransOriginText(text: string) {
|
||||
// 避免翻译的字符里包含数字或者特殊字符等情况,只过滤出汉字和字母
|
||||
const reg = /[a-zA-Z\u4e00-\u9fa5]+/g;
|
||||
const findText = text.match(reg) || [];
|
||||
const transOriginText = findText ? findText.join('').slice(0, 5) : '中文符号';
|
||||
const reg = /[a-zA-Z\u4e00-\u9fa5]+/g
|
||||
const findText = text.match(reg) || []
|
||||
const transOriginText = findText ? findText.join('').slice(0, 5) : '中文符号'
|
||||
|
||||
return transOriginText;
|
||||
return transOriginText
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currentFilename 文件路径
|
||||
* @returns string[]
|
||||
* 根据文件名和上级目录生成对应的前缀
|
||||
* @param fileName 文件路径
|
||||
* @returns 例如:home.index. | ''
|
||||
*/
|
||||
function getSuggestion(currentFilename: string) {
|
||||
let suggestion = [];
|
||||
const suggestPageRegex = /\/pages\/\w+\/([^\/]+)\/([^\/\.]+)/;
|
||||
|
||||
if (currentFilename.includes('/pages/')) {
|
||||
suggestion = currentFilename.match(suggestPageRegex);
|
||||
function getFilePrefix(fileName: string) {
|
||||
// 反斜杠路由替换成正斜杠
|
||||
const forwardFileName = slash(fileName)
|
||||
let filePrefix = []
|
||||
// 如果是在page下的目录直接匹配前缀
|
||||
// oldRegex : \/pages\/\w+\/([^\/]+)\/([^\/\.]+)
|
||||
const suggestPageRegex = /\/pages\/([^\/]+)\/([^\/\.]+)/
|
||||
if (forwardFileName.includes('/pages/')) {
|
||||
filePrefix = forwardFileName.match(suggestPageRegex)
|
||||
filePrefix && filePrefix.shift()
|
||||
}
|
||||
if (suggestion) {
|
||||
suggestion.shift();
|
||||
}
|
||||
/** 如果没有匹配到 Key */
|
||||
if (!(suggestion && suggestion.length)) {
|
||||
const names = slash(currentFilename).split('/');
|
||||
const fileName = _.last(names) as any;
|
||||
const fileKey = fileName.split('.')[0].replace(new RegExp('-', 'g'), '_');
|
||||
const dir = names[names.length - 2].replace(new RegExp('-', 'g'), '_');
|
||||
// 如果没有匹配到前缀
|
||||
if (!(filePrefix && filePrefix.length)) {
|
||||
const names = forwardFileName.split('/')
|
||||
const fileName = _.last(names) as any
|
||||
// 去除拓展名
|
||||
const fileKey = fileName.split('.')[0].replace(new RegExp('-', 'g'), '_')
|
||||
// 上级目录名
|
||||
const dir = names[names.length - 2].replace(new RegExp('-', 'g'), '_')
|
||||
if (dir === fileKey) {
|
||||
suggestion = [dir];
|
||||
filePrefix = [dir]
|
||||
} else {
|
||||
suggestion = [dir, fileKey];
|
||||
filePrefix = [dir, fileKey]
|
||||
}
|
||||
}
|
||||
|
||||
return suggestion;
|
||||
return filePrefix.length ? `${filePrefix.join('.')}.` : ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理key值,已提取过的文案直接替换,翻译后的key若相同,加上出现次数
|
||||
* @param currentFilename 文件路径
|
||||
* @param filePath 文件路径
|
||||
* @param langsPrefix 替换后的前缀
|
||||
* @param translateTexts 翻译后的key值
|
||||
* @param targetStrs 当前文件提取后的文案
|
||||
* @returns any[] 最终可用于替换的key值和文案
|
||||
*/
|
||||
function getReplaceableStrs(currentFilename: string, langsPrefix: string, translateTexts: string[], targetStrs: any[]) {
|
||||
const finalLangObj = getSuggestLangObj();
|
||||
const virtualMemory = {};
|
||||
const suggestion = getSuggestion(currentFilename);
|
||||
const replaceableStrs = targetStrs.reduce((prev, curr, i) => {
|
||||
const _text = curr.text;
|
||||
let key = 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] as string);
|
||||
let transKey = `${suggestion.length ? suggestion.join('.') + '.' : ''}${transText}`;
|
||||
transKey = transKey.replace(/-/g, '_');
|
||||
if (langsPrefix) {
|
||||
transKey = `${langsPrefix}.${transText}`;
|
||||
}
|
||||
let occurTime = 1;
|
||||
// 防止出现前四位相同但是整体文案不同的情况
|
||||
while (
|
||||
findMatchValue(finalLangObj, transKey) !== _text &&
|
||||
_.keys(finalLangObj).includes(`${transKey}${occurTime >= 2 ? occurTime : ''}`)
|
||||
) {
|
||||
occurTime++;
|
||||
}
|
||||
if (occurTime >= 2) {
|
||||
transKey = `${transKey}${occurTime}`;
|
||||
}
|
||||
virtualMemory[_text] = transKey;
|
||||
finalLangObj[transKey] = _text;
|
||||
function getLangKeys(filePath: string, langsPrefix: string, translateTexts: string[], targetStrs: any[]) {
|
||||
// 获取项目原语言的配置并拍平
|
||||
const srcLangObj = getSrcLangObj()
|
||||
// 是区分是否是本函数生成而非原始数据,从而确定是否需要needWrite
|
||||
const genKeys = {}
|
||||
// 为文案生成对应的键值
|
||||
return targetStrs.reduce((prev, curr, i) => {
|
||||
const { text } = curr
|
||||
// 如果已生成过对应的键则直接返回
|
||||
if (genKeys[text]) {
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key: transKey,
|
||||
key: genKeys[text],
|
||||
needWrite: true
|
||||
});
|
||||
} else {
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key: virtualMemory[_text],
|
||||
needWrite: true
|
||||
});
|
||||
})
|
||||
}
|
||||
}, []);
|
||||
|
||||
return replaceableStrs;
|
||||
// 在原配置中找对应的键
|
||||
let key = findMatchKey(srcLangObj, text)
|
||||
// 如果找到了就替换中划线为下划线并暂存
|
||||
if (key) {
|
||||
key = key.replace(/-/g, '_')
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key,
|
||||
// 这里由于原语言配置就有键,所以不需要重写进配置文件
|
||||
needWrite: false
|
||||
})
|
||||
}
|
||||
|
||||
// 没有生成过,原配置也没有,重新生成一个
|
||||
|
||||
// 获取翻译后的键并驼峰化
|
||||
const transKey = translateTexts[i] && _.camelCase(translateTexts[i] as string)
|
||||
// 如果用户定义了前缀就直接加上
|
||||
if (langsPrefix) {
|
||||
key = `${langsPrefix}.${transKey}`
|
||||
}
|
||||
// 没前缀就加上文件名和上级目录作为前缀
|
||||
else {
|
||||
key = `${getFilePrefix(filePath)}${transKey}`
|
||||
}
|
||||
key = key.replace(/-/g, '_')
|
||||
|
||||
// 防止出现前五位相同但是整体文案不同的情况
|
||||
if (srcLangObj[key] && srcLangObj[key] !== text) {
|
||||
// 已经存在的Key
|
||||
const existKeys = _.keys(srcLangObj)
|
||||
let occurTime = 2
|
||||
// 获取已经重复的次数
|
||||
while (existKeys.includes(`${key}${occurTime}`)) {
|
||||
occurTime += 1
|
||||
}
|
||||
// 拼接重复次数,例如:common.index.key1
|
||||
key = key + occurTime
|
||||
}
|
||||
// 写进新生成的键列表
|
||||
genKeys[text] = key
|
||||
// 写入原配置,方便后续比较重复
|
||||
srcLangObj[key] = text
|
||||
return prev.concat({
|
||||
target: curr,
|
||||
key,
|
||||
needWrite: true
|
||||
})
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归匹配项目中所有的代码的中文
|
||||
* @param {dirPath} 文件夹路径
|
||||
* @returns
|
||||
*/
|
||||
function extractAll({ dirPath, prefix }: { dirPath?: string; prefix?: string }) {
|
||||
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 仅支持 ${highlightText('Pinyin、Google、Baidu')},请修改 ${highlightText('defaultTranslateKeyApi')} 配置项`
|
||||
);
|
||||
return;
|
||||
// 翻译源配置校验
|
||||
let origin = CONFIG.defaultTranslateKeyApi
|
||||
// 用户设置了空字符串
|
||||
if (!origin) {
|
||||
origin = 'Pinyin'
|
||||
console.log(`配置文件未配置 ${highlightText('defaultTranslateKeyApi')} , 使用默认值 ${highlightText('Pinyin')}\n`)
|
||||
}
|
||||
// 对用户填入值进行校验
|
||||
else if (!['Pinyin', 'Google', 'Baidu'].includes(origin)) {
|
||||
console.log(
|
||||
`Canary 仅支持 ${highlightText('Pinyin、Google、Baidu')},请修改 ${highlightText('defaultTranslateKeyApi')} 配置项`
|
||||
)
|
||||
return
|
||||
}
|
||||
// 地址为用户输入,默认为src文件夹
|
||||
const dir = dirPath || './src'
|
||||
// 去除I18N前缀,后续全局加
|
||||
const langsPrefix = prefix ? prefix.replace(/^I18N\./, '') : null
|
||||
|
||||
const allTargetStrs = findAllChineseText(dir);
|
||||
// 获取目标文件下全部中文文案
|
||||
const allTargetStrs = findAllChineseText(dir)
|
||||
if (allTargetStrs.length === 0) {
|
||||
console.log(highlightText('没有发现可替换的文案!'));
|
||||
return;
|
||||
console.log(highlightText('没有发现可替换的文案!'))
|
||||
return
|
||||
}
|
||||
|
||||
// 提示翻译源
|
||||
if (CONFIG.defaultTranslateKeyApi === 'Pinyin') {
|
||||
if (origin === 'Pinyin') {
|
||||
console.log(
|
||||
`当前使用 ${highlightText('Pinyin')} 作为key值的翻译源,若想得到更好的体验,可配置 ${highlightText(
|
||||
`\n当前使用 ${highlightText('Pinyin')} 作为key值的翻译源,若想得到更好的体验,可配置 ${highlightText(
|
||||
'googleApiKey'
|
||||
)} 或 ${highlightText('baiduApiKey')},并切换 ${highlightText('defaultTranslateKeyApi')}`
|
||||
);
|
||||
)
|
||||
} else {
|
||||
console.log(`当前使用 ${highlightText(CONFIG.defaultTranslateKeyApi)} 作为key值的翻译源`);
|
||||
console.log(`\n当前使用 ${highlightText(origin)} 作为key值的翻译源`)
|
||||
}
|
||||
|
||||
console.log('即将截取每个中文文案的前5位翻译生成key值,并替换中...');
|
||||
console.log('即将截取每个中文文案的前5位翻译生成key值,并替换中...\n')
|
||||
|
||||
// 对当前文件进行文案key生成和替换
|
||||
const generateKeyAndReplace = async item => {
|
||||
const currentFilename = item.file;
|
||||
console.log(`${currentFilename} 替换中...`);
|
||||
// 过滤掉模板字符串内的中文,避免替换时出现异常
|
||||
const targetStrs = item.texts.reduce((pre, strObj, i) => {
|
||||
const { texts, file: filePath } = item
|
||||
console.log(`${highlightText(filePath)} 替换中...`)
|
||||
// 过滤掉模板字符串内的中文,避免替换时出现异常,示例:https://imoaix.cn/canary-clis/1.png
|
||||
const targetStrs = texts.reduce((pre, strObj, i) => {
|
||||
// 因为文案已经根据位置倒排,所以比较时只需要比较剩下的文案即可
|
||||
const afterStrs = item.texts.slice(i + 1);
|
||||
const afterStrs = texts.slice(i + 1)
|
||||
// 如果是在模板字符串中的中文,其结束位置必然小于等于模板字符串本身的结束位置
|
||||
if (afterStrs.some(obj => strObj.range.end <= obj.range.end)) {
|
||||
return pre;
|
||||
return pre
|
||||
}
|
||||
return pre.concat(strObj);
|
||||
}, []);
|
||||
const len = item.texts.length - targetStrs.length;
|
||||
return pre.concat(strObj)
|
||||
}, [])
|
||||
// 比对前后数量提示用户,不存在过滤后长度为0的情况
|
||||
const len = texts.length - targetStrs.length
|
||||
if (len > 0) {
|
||||
console.log(colors.red(`存在 ${highlightText(len)} 处文案无法替换,请避免在模板字符串的变量中嵌套中文`));
|
||||
console.log(colors.red(`存在 ${highlightText(len)} 处文案无法替换,请避免在模板字符串的变量中嵌套中文`))
|
||||
}
|
||||
|
||||
let translateTexts;
|
||||
|
||||
// 翻译后的文案
|
||||
let translateTexts
|
||||
// 翻译中文文案,百度和pinyin将文案拼接成一条统一翻译
|
||||
if (origin !== 'Google') {
|
||||
// 翻译中文文案,百度和pinyin将文案进行拼接统一翻译
|
||||
const delimiter = origin === 'Baidu' ? '\n' : '$';
|
||||
// 使用不同的分割符
|
||||
const delimiter = origin === 'Baidu' ? '\n' : '$'
|
||||
// 拼接字符串
|
||||
const translateOriginTexts = targetStrs.reduce((prev, curr, i) => {
|
||||
const transOriginText = getTransOriginText(curr.text);
|
||||
// 截取文案前5位,仅包含中文和字母
|
||||
const transOriginText = getTransOriginText(curr.text)
|
||||
if (i === 0) {
|
||||
return transOriginText;
|
||||
return transOriginText
|
||||
}
|
||||
return `${prev}${delimiter}${transOriginText}`;
|
||||
}, []);
|
||||
|
||||
translateTexts = await translateKeyText(translateOriginTexts, origin);
|
||||
return `${prev}${delimiter}${transOriginText}`
|
||||
}, [])
|
||||
// 翻译后的文案
|
||||
translateTexts = await translateWithBaiduPinyin(translateOriginTexts, origin)
|
||||
} else {
|
||||
// google并发性较好,且未找到有效的分隔符,故仍然逐个文案进行翻译
|
||||
const translatePromises = targetStrs.reduce((prev, curr) => {
|
||||
const transOriginText = getTransOriginText(curr.text);
|
||||
return prev.concat(translateText(transOriginText, 'en_US'));
|
||||
}, []);
|
||||
const transOriginText = getTransOriginText(curr.text)
|
||||
return prev.concat(translateText(transOriginText, 'en_US'))
|
||||
}, [])
|
||||
|
||||
[...translateTexts] = await Promise.all(translatePromises);
|
||||
;[...translateTexts] = await Promise.all(translatePromises)
|
||||
}
|
||||
|
||||
// 翻译结果示例:https://imoaix.cn/canary-clis/2.png
|
||||
if (translateTexts.length === 0) {
|
||||
failInfo(`未得到翻译结果,${currentFilename}替换失败!`);
|
||||
return;
|
||||
failInfo(`未得到翻译结果,${filePath}替换失败!`)
|
||||
return
|
||||
}
|
||||
|
||||
const replaceableStrs = getReplaceableStrs(currentFilename, langsPrefix, translateTexts, targetStrs);
|
||||
|
||||
await replaceableStrs
|
||||
.reduce((prev, obj) => {
|
||||
|
||||
// 统一处理Key值,如翻译后结果相同加上出现次数,结果示例:https://imoaix.cn/canary-clis/3.png
|
||||
const langKeys = getLangKeys(filePath, langsPrefix, translateTexts, targetStrs)
|
||||
await langKeys
|
||||
.reduce((prev, {target, key, needWrite}) => {
|
||||
return prev.then(() => {
|
||||
return replaceAndUpdate(currentFilename, obj.target, `I18N.${obj.key}`, false, obj.needWrite);
|
||||
});
|
||||
// 根据生成的键值对更新源码文件以及语言文件
|
||||
return replaceAndUpdate(filePath, target, `I18N.${key}`, false, needWrite)
|
||||
})
|
||||
}, Promise.resolve())
|
||||
.then(() => {
|
||||
// 添加 import I18N
|
||||
if (!hasImportI18N(currentFilename)) {
|
||||
const code = createImportI18N(currentFilename);
|
||||
|
||||
writeFile(currentFilename, code);
|
||||
if (!hasImportI18N(filePath)) {
|
||||
const code = createImportI18N(filePath)
|
||||
writeFile(filePath, code)
|
||||
}
|
||||
successInfo(`${currentFilename} 替换完成,共替换 ${targetStrs.length} 处文案!`);
|
||||
successInfo(`${filePath} 替换完成,共替换 ${targetStrs.length} 处文案!\n`)
|
||||
})
|
||||
.catch(e => {
|
||||
failInfo(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
failInfo(e.message)
|
||||
})
|
||||
}
|
||||
allTargetStrs
|
||||
.reduce((prev, current) => {
|
||||
return prev.then(() => {
|
||||
return generateKeyAndReplace(current);
|
||||
});
|
||||
return generateKeyAndReplace(current)
|
||||
})
|
||||
}, Promise.resolve())
|
||||
.then(() => {
|
||||
successInfo('全部替换完成!');
|
||||
successInfo('全部替换完成!')
|
||||
})
|
||||
.catch((e: any) => {
|
||||
failInfo(e.message);
|
||||
});
|
||||
failInfo(e.message)
|
||||
})
|
||||
}
|
||||
|
||||
export { extractAll };
|
||||
export { extractAll }
|
||||
|
@ -1,11 +1,11 @@
|
||||
/**
|
||||
* @author doubledream
|
||||
* @author zhaoyingbo
|
||||
* @desc 文件处理方法
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as _ from 'lodash';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path'
|
||||
import * as _ from 'lodash'
|
||||
import * as fs from 'fs'
|
||||
|
||||
/**
|
||||
* 获取文件夹下符合要求的所有文件
|
||||
@ -15,12 +15,12 @@ import * as fs from 'fs';
|
||||
*/
|
||||
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();
|
||||
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));
|
||||
return files.concat(getSpecifiedFiles(name, ignoreDirectory, ignoreFile))
|
||||
}
|
||||
|
||||
const isIgnoreDirectory =
|
||||
@ -29,14 +29,14 @@ function getSpecifiedFiles(dir, ignoreDirectory = '', ignoreFile = '') {
|
||||
!path
|
||||
.dirname(name)
|
||||
.split('/')
|
||||
.includes(ignoreDirectory));
|
||||
const isIgnoreFile = !ignoreFile || (ignoreFile && path.basename(name) !== ignoreFile);
|
||||
.includes(ignoreDirectory))
|
||||
const isIgnoreFile = !ignoreFile || (ignoreFile && path.basename(name) !== ignoreFile)
|
||||
|
||||
if (isFile && isIgnoreDirectory && isIgnoreFile) {
|
||||
return files.concat(name);
|
||||
return files.concat(name)
|
||||
}
|
||||
return files;
|
||||
}, []);
|
||||
return files
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,7 +45,7 @@ function getSpecifiedFiles(dir, ignoreDirectory = '', ignoreFile = '') {
|
||||
*/
|
||||
function readFile(fileName) {
|
||||
if (fs.existsSync(fileName)) {
|
||||
return fs.readFileSync(fileName, 'utf-8');
|
||||
return fs.readFileSync(fileName, 'utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ function readFile(fileName) {
|
||||
*/
|
||||
function writeFile(filePath, file) {
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.writeFileSync(filePath, file);
|
||||
fs.writeFileSync(filePath, file)
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ function writeFile(filePath, file) {
|
||||
* @param path
|
||||
*/
|
||||
function isFile(path) {
|
||||
return fs.statSync(path).isFile();
|
||||
return fs.statSync(path).isFile()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +72,15 @@ function isFile(path) {
|
||||
* @param path
|
||||
*/
|
||||
function isDirectory(path) {
|
||||
return fs.statSync(path).isDirectory();
|
||||
return fs.statSync(path).isDirectory()
|
||||
}
|
||||
|
||||
export { getSpecifiedFiles, readFile, writeFile, isFile, isDirectory };
|
||||
/**
|
||||
* 获取文件尾缀
|
||||
* @param path
|
||||
*/
|
||||
function getSuffix(path) {
|
||||
return path.split('.').pop()
|
||||
}
|
||||
|
||||
export { getSpecifiedFiles, readFile, writeFile, isFile, isDirectory, getSuffix }
|
||||
|
@ -3,23 +3,25 @@
|
||||
* @desc 获取语言文件
|
||||
*/
|
||||
|
||||
import * as globby from 'globby';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getProjectConfig, flatten } from '../utils';
|
||||
import * as globby from 'globby'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import { getProjectConfig, flatten } from '../utils'
|
||||
|
||||
const CONFIG = getProjectConfig();
|
||||
const LANG_DIR = path.resolve(CONFIG.canaryDir, CONFIG.srcLang);
|
||||
const I18N_GLOB = `${LANG_DIR}/**/*.ts`;
|
||||
const CONFIG = getProjectConfig()
|
||||
// 项目对应语言配置文件夹
|
||||
const LANG_DIR = path.resolve(CONFIG.canaryDir, CONFIG.srcLang)
|
||||
// 匹配文件
|
||||
const I18N_GLOB = `${LANG_DIR}/**/*.${CONFIG.isJsProj ? 'js' : 'ts'}`
|
||||
|
||||
/**
|
||||
* 获取对应文件的语言
|
||||
* 获取制定文件JSON内容
|
||||
*/
|
||||
function getLangData(fileName) {
|
||||
if (fs.existsSync(fileName)) {
|
||||
return getLangJson(fileName);
|
||||
return getLangJson(fileName)
|
||||
} else {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,52 +29,59 @@ function getLangData(fileName) {
|
||||
* 获取文件 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 = {};
|
||||
const fileContent = fs.readFileSync(fileName, { encoding: 'utf8' })
|
||||
let obj = fileContent.match(/export\s*default\s*({[\s\S]+)?$/)[1]
|
||||
obj = obj.replace(/\s*\s*$/, '')
|
||||
obj = obj.replace(';', '')
|
||||
let jsObj = {}
|
||||
try {
|
||||
jsObj = eval('(' + obj + ')');
|
||||
jsObj = eval('(' + obj + ')')
|
||||
} catch (err) {
|
||||
console.log(obj);
|
||||
console.error(err);
|
||||
console.log(obj)
|
||||
console.error(err)
|
||||
}
|
||||
return jsObj;
|
||||
return jsObj
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目原语言的I18N内容
|
||||
* @return 示例:{ 'common.ts': { test: '测试', chinese: '中国' } }
|
||||
*/
|
||||
function getI18N() {
|
||||
const paths = globby.sync(I18N_GLOB);
|
||||
// 项目对应语言配置文件集合
|
||||
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;
|
||||
.split('.')
|
||||
.shift()
|
||||
// 排除index文件
|
||||
if (filename === 'index') {
|
||||
return prev
|
||||
}
|
||||
|
||||
const fileContent = getLangData(curr);
|
||||
let jsObj = fileContent;
|
||||
|
||||
// 获取文件内容
|
||||
const fileContent = getLangData(curr)
|
||||
let jsObj = fileContent
|
||||
// 空对象,说明获取失败
|
||||
if (Object.keys(jsObj).length === 0) {
|
||||
console.log(`\`${curr}\` 解析失败,该文件包含的文案无法自动补全`);
|
||||
console.log(`\`${curr}\` 解析失败,该文件包含的文案无法自动补全`)
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[filename]: jsObj
|
||||
};
|
||||
}, {});
|
||||
return langObj;
|
||||
}
|
||||
}, {})
|
||||
return langObj
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部语言, 展平
|
||||
* 获取项目原语言的配置, 展平
|
||||
* @return 示例:{ 'common.test': '测试', 'common.chinese': '中国' }
|
||||
*/
|
||||
function getSuggestLangObj() {
|
||||
const langObj = getI18N();
|
||||
const finalLangObj = flatten(langObj);
|
||||
return finalLangObj;
|
||||
function getSrcLangObj() {
|
||||
return flatten(getI18N())
|
||||
}
|
||||
|
||||
export { getSuggestLangObj, getLangData };
|
||||
export { getSrcLangObj, getLangData }
|
||||
|
@ -3,49 +3,16 @@
|
||||
* @desc 更新文件
|
||||
*/
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
import * as _ from 'lodash';
|
||||
import * as prettier from 'prettier';
|
||||
import * as ts from 'typescript';
|
||||
import { readFile, writeFile } from './file';
|
||||
import { getLangData } from './getLangData';
|
||||
import { getProjectConfig, getLangDir, successInfo, failInfo, highlightText } from '../utils';
|
||||
import * as fs from 'fs-extra'
|
||||
import * as _ from 'lodash'
|
||||
import * as prettier from 'prettier'
|
||||
import * as ts from 'typescript'
|
||||
import { readFile, writeFile, getSuffix } from './file'
|
||||
import { getLangData } from './getLangData'
|
||||
import { getProjectConfig, getLangDir, successInfo, failInfo, highlightText } from '../utils'
|
||||
|
||||
const CONFIG = getProjectConfig();
|
||||
const srcLangDir = 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);
|
||||
successInfo(`成功新建语言文件 ${targetFilename}`);
|
||||
} else {
|
||||
// 清除 require 缓存,解决手动更新语言文件后再自动抽取,导致之前更新失效的问题
|
||||
const mainContent = getLangData(targetFilename);
|
||||
const obj = mainContent;
|
||||
|
||||
if (Object.keys(obj).length === 0) {
|
||||
failInfo(`${filename} 解析失败,该文件包含的文案无法自动补全`);
|
||||
}
|
||||
|
||||
if (validateDuplicate && _.get(obj, fullKey) !== undefined) {
|
||||
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)}`));
|
||||
}
|
||||
}
|
||||
const CONFIG = getProjectConfig()
|
||||
const srcLangDir = getLangDir(CONFIG.srcLang)
|
||||
|
||||
/**
|
||||
* 使用 Prettier 格式化文件
|
||||
@ -57,83 +24,145 @@ function prettierFile(fileContent) {
|
||||
parser: 'typescript',
|
||||
trailingComma: 'all',
|
||||
singleQuote: true
|
||||
});
|
||||
})
|
||||
} catch (e) {
|
||||
failInfo(`代码格式化报错!${e.toString()}\n代码为:${fileContent}`);
|
||||
return fileContent;
|
||||
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 key 语言文件中的I80N值
|
||||
* @param value 语言文件中的I18N值
|
||||
*/
|
||||
function generateNewLangFile(key, value) {
|
||||
const obj = _.set({}, key, value)
|
||||
|
||||
return prettierFile(`export default ${JSON.stringify(obj, null, 2)}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在语言index文件中新增语言文件引入
|
||||
* @param newFilename 新的语言文件名
|
||||
*/
|
||||
function addImportToMainLangFile(newFilename) {
|
||||
const indexFileSrc = `${srcLangDir}/index.${CONFIG.isJsProj ? 'js' : 'ts'}`
|
||||
let mainContent = ''
|
||||
// 如果已经存在index文件则更新内容
|
||||
if (fs.existsSync(indexFileSrc)) {
|
||||
mainContent = fs.readFileSync(indexFileSrc, '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`)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 还未存在index文件,生成初始值
|
||||
else {
|
||||
mainContent = `import ${newFilename} from './${newFilename}'\n\nexport default Object.assign({}, {\n ${newFilename},\n})`
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
fs.writeFileSync(indexFileSrc, mainContent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新语言文件
|
||||
* @param key 代码文件中的I18N键
|
||||
* @param val 语言文件中的I80N值
|
||||
* @param validateDuplicate 是否校验重复
|
||||
*/
|
||||
function updateLangFiles(key, val, validateDuplicate) {
|
||||
// 过滤掉不是I18N开头的Key
|
||||
if (!_.startsWith(key, 'I18N.')) {
|
||||
return
|
||||
}
|
||||
// 拆分代码文件中的键
|
||||
const [, filename, ...restPath] = key.split('.')
|
||||
// 语言文件中的I18N键
|
||||
const fullKey = restPath.join('.')
|
||||
// 语言文件路径
|
||||
const langFilePath = `${srcLangDir}/${filename}.${CONFIG.isJsProj ? 'js' : 'ts'}`
|
||||
|
||||
// 还未生成对应的语言文件
|
||||
if (!fs.existsSync(langFilePath)) {
|
||||
// 生成新的语言文件内容并写入
|
||||
fs.writeFileSync(langFilePath, generateNewLangFile(fullKey, val))
|
||||
// 在index文件中增加语言文件引入
|
||||
addImportToMainLangFile(filename)
|
||||
successInfo(`成功新建语言文件 ${langFilePath}`)
|
||||
}
|
||||
else {
|
||||
// 清除 require 缓存,解决手动更新语言文件后再自动抽取,导致之前更新失效的问题
|
||||
const mainContent = getLangData(langFilePath)
|
||||
|
||||
// 解析失败会返回{},需要过滤下
|
||||
if (Object.keys(mainContent).length === 0) {
|
||||
failInfo(`${filename} 解析失败,该文件包含的文案无法自动补全`)
|
||||
}
|
||||
|
||||
// 校验是否有重复的Key
|
||||
if (validateDuplicate && _.get(mainContent, fullKey) !== undefined) {
|
||||
failInfo(`${langFilePath} 中已存在 key 为 \`${fullKey}\` 的翻译,请重新命名变量`)
|
||||
throw new Error('duplicate')
|
||||
}
|
||||
// \n 会被自动转义成 \\n,这里转回来
|
||||
val = val.replace(/\\n/gm, '\n')
|
||||
// 写入语言文件
|
||||
_.set(mainContent, fullKey, val)
|
||||
fs.writeFileSync(langFilePath, prettierFile(`export default ${JSON.stringify(mainContent, null, 2)}`))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查代码文件是否添加过 import I18N 命令
|
||||
* @param filePath 文件路径
|
||||
*/
|
||||
function hasImportI18N(filePath) {
|
||||
const code = readFile(filePath);
|
||||
const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TSX);
|
||||
let hasImportI18N = false;
|
||||
const code = 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;
|
||||
const importClause = node.importClause
|
||||
|
||||
// import I18N from 'src/utils/I18N';
|
||||
// import I18N from 'src/utils/I18N'
|
||||
if (_.get(importClause, 'kind') === ts.SyntaxKind.ImportClause) {
|
||||
if (importClause.name) {
|
||||
if (importClause.name.escapedText === 'I18N') {
|
||||
hasImportI18N = true;
|
||||
hasImportI18N = true
|
||||
}
|
||||
} else {
|
||||
const namedBindings = importClause.namedBindings;
|
||||
// import { I18N } from 'src/utils/I18N';
|
||||
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;
|
||||
hasImportI18N = true
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
// import * as I18N from 'src/utils/I18N';
|
||||
// import * as I18N from 'src/utils/I18N'
|
||||
if (namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
|
||||
if (_.get(namedBindings, 'name.escapedText') === 'I18N') {
|
||||
hasImportI18N = true;
|
||||
hasImportI18N = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,9 +170,8 @@ function hasImportI18N(filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(ast, visit);
|
||||
|
||||
return hasImportI18N;
|
||||
ts.forEachChild(ast, visit)
|
||||
return hasImportI18N
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,89 +179,109 @@ function hasImportI18N(filePath) {
|
||||
* @param filePath 文件路径
|
||||
*/
|
||||
function createImportI18N(filePath) {
|
||||
const code = 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);
|
||||
const code = readFile(filePath)
|
||||
const ast = ts.createSourceFile('', code, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TSX)
|
||||
const isNomalFile = ['ts', 'js', 'tsx', 'jsx'].includes(getSuffix(filePath))
|
||||
const isVueFile = _.endsWith(filePath, '.vue')
|
||||
// 正常文件开头添加import
|
||||
if (isNomalFile) {
|
||||
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(/<script>/g, `<script>\n${importStatement}`);
|
||||
return updateCode;
|
||||
return updateCode
|
||||
}
|
||||
// Vue文件开头添加import
|
||||
else if (isVueFile) {
|
||||
const importStatement = `${CONFIG.importI18N}\n`
|
||||
const updateCode = code.replace(/<script>/g, `<script>\n${importStatement}`)
|
||||
return updateCode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文件
|
||||
* @param filePath 当前文件路径
|
||||
* @param arg 目标字符串对象
|
||||
* @param val 目标 key
|
||||
* 更新语言文件以及源代码
|
||||
* @param filePath 当前代码文件路径
|
||||
* @param target 目标字符串对象
|
||||
* @param key 目标 key
|
||||
* @param validateDuplicate 是否校验文件中已经存在要写入的 key
|
||||
* @param needWrite 是否只需要替换不需要更新 langs 文件
|
||||
*/
|
||||
function replaceAndUpdate(filePath, arg, val, validateDuplicate, needWrite = true) {
|
||||
const code = readFile(filePath);
|
||||
const isHtmlFile = _.endsWith(filePath, '.html');
|
||||
const isVueFile = _.endsWith(filePath, '.vue');
|
||||
let newCode = code;
|
||||
let finalReplaceText = arg.text;
|
||||
const { start, end } = arg.range;
|
||||
function replaceAndUpdate(filePath, target, key, validateDuplicate, needWrite = true) {
|
||||
// 获取目标字段内容
|
||||
const { text, range: { start, end }, isString } = target
|
||||
// 获取当前代码内容
|
||||
const code = readFile(filePath)
|
||||
const isHtmlFile = _.endsWith(filePath, '.html')
|
||||
const isVueFile = _.endsWith(filePath, '.vue')
|
||||
// 写回代码文件的内容
|
||||
let newCode = code
|
||||
// 中文语言结果
|
||||
let cnVal = text
|
||||
|
||||
// 若是字符串,删掉两侧的引号
|
||||
if (arg.isString) {
|
||||
if (isString) {
|
||||
// 写回代码文件的I18N键
|
||||
let codeKey = key
|
||||
// 如果引号左侧是 等号,则可能是 jsx 的 props,此时要替换成 {
|
||||
const preTextStart = start - 1;
|
||||
const [last2Char, last1Char] = code.slice(preTextStart, start + 1).split('');
|
||||
let finalReplaceVal = val;
|
||||
const preTextStart = start - 1
|
||||
const [last2Char, last1Char] = code.slice(preTextStart, start + 1).split('')
|
||||
if (last2Char === '=') {
|
||||
if (isHtmlFile) {
|
||||
finalReplaceVal = '{{' + val + '}}';
|
||||
codeKey = '{{' + key + '}}'
|
||||
} else if (isVueFile) {
|
||||
finalReplaceVal = '{{' + val + '}}';
|
||||
codeKey = '{{' + key + '}}'
|
||||
} else {
|
||||
finalReplaceVal = '{' + val + '}';
|
||||
codeKey = '{' + key + '}'
|
||||
}
|
||||
}
|
||||
// 若是模板字符串,看看其中是否包含变量
|
||||
if (last1Char === '`') {
|
||||
const varInStr = arg.text.match(/(\$\{[^\}]+?\})/g);
|
||||
// 正则可视化 https://c.runoob.com/front-end/7625/#!flags=&re=(%5C%24%5C%7B%5B%5E%5C%7D%5D%2B%3F%5C%7D)
|
||||
// 匹配模板字符串内的全部变量内容
|
||||
const varInStr = text.match(/(\$\{[^\}]+?\})/g)
|
||||
// 如果存在变量
|
||||
if (varInStr) {
|
||||
/**
|
||||
* 取出变量内容并用val{x}指代
|
||||
* 示例:[ 'val1: a', "val2: '全球每个人'" ]
|
||||
*/
|
||||
const kvPair = varInStr.map((str, index) => {
|
||||
return `val${index + 1}: ${str.replace(/^\${([^\}]+)\}$/, '$1')}`;
|
||||
});
|
||||
finalReplaceVal = `I18N.template(${val}, { ${kvPair.join(',\n')} })`;
|
||||
|
||||
return `val${index + 1}: ${str.replace(/^\${([^\}]+)\}$/, '$1')}`
|
||||
})
|
||||
/**
|
||||
* 包装模板字符串的I18N替换代码,用于写回代码文件
|
||||
* 示例:I18N.template(I18N.home.index.shiZhongJianChiZuo, { val1: a, val2: '全球每个人' })
|
||||
*/
|
||||
codeKey = `I18N.template(${key}, { ${kvPair.join(',\n')} })`
|
||||
/**
|
||||
* 使用val{x}替换掉原模板字符串的变量,用于生成语言文件
|
||||
* 示例:始终坚持做“{val1}”的好产品,让{val2}都能享受科技带来的美好生活
|
||||
*/
|
||||
varInStr.forEach((str, index) => {
|
||||
finalReplaceText = finalReplaceText.replace(str, `{val${index + 1}}`);
|
||||
});
|
||||
cnVal = cnVal.replace(str, `{val${index + 1}}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
newCode = `${code.slice(0, start)}${finalReplaceVal}${code.slice(end)}`;
|
||||
// 将I18N替换代码写回代码文件
|
||||
newCode = `${code.slice(0, start)}${codeKey}${code.slice(end)}`
|
||||
} else {
|
||||
if (isHtmlFile || isVueFile) {
|
||||
newCode = `${code.slice(0, start)}{{${val}}}${code.slice(end)}`;
|
||||
newCode = `${code.slice(0, start)}{{${key}}}${code.slice(end)}`
|
||||
} else {
|
||||
newCode = `${code.slice(0, start)}{${val}}${code.slice(end)}`;
|
||||
newCode = `${code.slice(0, start)}{${key}}${code.slice(end)}`
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (needWrite) {
|
||||
// 更新语言文件
|
||||
updateLangFiles(val, finalReplaceText, validateDuplicate);
|
||||
updateLangFiles(key, cnVal, validateDuplicate)
|
||||
}
|
||||
// 若更新成功再替换代码
|
||||
return writeFile(filePath, newCode);
|
||||
// // 若更新成功再替换代码
|
||||
return writeFile(filePath, newCode)
|
||||
} catch (e) {
|
||||
return Promise.reject(e.message);
|
||||
return Promise.reject(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
export { replaceAndUpdate, hasImportI18N, createImportI18N };
|
||||
export { replaceAndUpdate, hasImportI18N, createImportI18N }
|
||||
|
@ -125,11 +125,12 @@ if (commander.translate) {
|
||||
}
|
||||
|
||||
if (commander.extract) {
|
||||
console.log(isString(commander.prefix));
|
||||
if (commander.prefix === true) {
|
||||
console.log('请指定翻译后文案 key 值的前缀 --prefix xxxx');
|
||||
} else if (isString(commander.prefix) && !new RegExp(/^I18N(\.[-_a-zA-Z1-9$]+)+$/).test(commander.prefix)) {
|
||||
console.log('前缀必须以I18N开头,后续跟上字母、下滑线、破折号、$ 字符组成的变量名');
|
||||
}
|
||||
// 正则可视化 https://c.runoob.com/front-end/7625/#!flags=&re=%5EI18N(%5C.%5B_a-zA-Z1-9%24%5D%2B)%2B%24
|
||||
else if (isString(commander.prefix) && !new RegExp(/^I18N(\.[_a-zA-Z1-9$]+)+$/).test(commander.prefix)) {
|
||||
console.log('前缀必须以I18N开头,后续跟上字母、下滑线、破折号、$ 字符组成的变量名,如:I18N.yingbo');
|
||||
} else {
|
||||
const extractAllParams = {
|
||||
prefix: isString(commander.prefix) && commander.prefix,
|
||||
|
48
src/init.ts
48
src/init.ts
@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @author zhaoyingbo
|
||||
* @desc 初始化 kiwi 项目的文件以及配置
|
||||
* @desc 初始化 canary 项目的文件以及配置
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { PROJECT_CONFIG, CANARY_CONFIG_FILE } from './const';
|
||||
import * as _ from 'lodash'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import { PROJECT_CONFIG, CANARY_CONFIG_FILE } from './const'
|
||||
|
||||
/**
|
||||
* 创建配置文件
|
||||
@ -15,52 +15,52 @@ import { PROJECT_CONFIG, CANARY_CONFIG_FILE } from './const';
|
||||
*/
|
||||
function createConfigFile(existDir?: string) {
|
||||
// 在根目录创建配置文件
|
||||
const configDir = path.resolve(process.cwd(), `./${CANARY_CONFIG_FILE}`);
|
||||
const configDir = path.resolve(process.cwd(), `./${CANARY_CONFIG_FILE}`)
|
||||
// 如果已经有配置文件就不要动了
|
||||
if (fs.existsSync(configDir)) return;
|
||||
const config = { ...PROJECT_CONFIG.defaultConfig };
|
||||
if (fs.existsSync(configDir)) return
|
||||
const config = { ...PROJECT_CONFIG.defaultConfig }
|
||||
// 有配置文件夹
|
||||
if (existDir && fs.existsSync(existDir)) {
|
||||
config.canaryDir = existDir;
|
||||
config.canaryDir = existDir
|
||||
}
|
||||
// 创建新的配置文件
|
||||
fs.writeFile(configDir, JSON.stringify(config, null, 2), err => err && console.log(err));
|
||||
fs.writeFile(configDir, JSON.stringify(config, null, 2), err => err && console.log(err))
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建中文配置示例文件
|
||||
* 创建中文语言示例文件
|
||||
*/
|
||||
function createCnFile() {
|
||||
// 中文配置文件夹地址
|
||||
const cnDir = `${PROJECT_CONFIG.dir}/zh-CN`;
|
||||
// 中文语言文件夹地址
|
||||
const cnDir = `${PROJECT_CONFIG.dir}/zh-CN`
|
||||
// 没有则创建
|
||||
if (!fs.existsSync(cnDir)) {
|
||||
fs.mkdirSync(cnDir);
|
||||
fs.writeFile(`${cnDir}/index.ts`, PROJECT_CONFIG.zhIndexFile, err => err && console.log(err));
|
||||
fs.writeFile(`${cnDir}/common.ts`, PROJECT_CONFIG.zhTestFile, err => err && console.log(err));
|
||||
fs.mkdirSync(cnDir)
|
||||
fs.writeFile(`${cnDir}/index.ts`, PROJECT_CONFIG.zhIndexFile, err => err && console.log(err))
|
||||
fs.writeFile(`${cnDir}/common.ts`, PROJECT_CONFIG.zhTestFile, err => err && console.log(err))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化国际化项目
|
||||
* @param existDir 配置文件夹地址
|
||||
* @param existDir 多语言文件夹地址
|
||||
*/
|
||||
function initProject(existDir?: string) {
|
||||
// 有用户输入的文件夹,不存在默认位置创建
|
||||
if (existDir && !fs.existsSync(existDir)) {
|
||||
console.log('\n输入的目录不存在,已为你生成默认文件夹');
|
||||
fs.mkdirSync(PROJECT_CONFIG.dir);
|
||||
console.log('\n输入的目录不存在,已为你生成默认文件夹')
|
||||
fs.mkdirSync(PROJECT_CONFIG.dir)
|
||||
}
|
||||
// 没有输入,默认位置创建
|
||||
else if (!existDir && !fs.existsSync(PROJECT_CONFIG.dir)) {
|
||||
fs.mkdirSync(PROJECT_CONFIG.dir);
|
||||
fs.mkdirSync(PROJECT_CONFIG.dir)
|
||||
}
|
||||
// 创建配置文件
|
||||
createConfigFile(existDir);
|
||||
// 没有已经存在的配置文件夹,创建默认的配置文件示例
|
||||
createConfigFile(existDir)
|
||||
// 没有已经存在的多语言文件夹,创建默认的中文语言示例文件
|
||||
if (!(existDir && fs.existsSync(existDir))) {
|
||||
createCnFile();
|
||||
createCnFile()
|
||||
}
|
||||
}
|
||||
|
||||
export { initProject };
|
||||
export { initProject }
|
||||
|
@ -4,12 +4,12 @@
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getKiwiDir, getLangDir, traverse } from './utils';
|
||||
import { getCanaryDir, getLangDir, traverse } from './utils';
|
||||
|
||||
const lookingForString = '';
|
||||
|
||||
function findUnUsed() {
|
||||
const srcLangDir = path.resolve(getKiwiDir(), 'zh-CN');
|
||||
const srcLangDir = path.resolve(getCanaryDir(), 'zh-CN');
|
||||
let files = fs.readdirSync(srcLangDir);
|
||||
files = files.filter(file => file.endsWith('.ts') && file !== 'index.ts');
|
||||
const unUnsedKeys = [];
|
||||
|
29
src/utils.ts
29
src/utils.ts
@ -49,7 +49,7 @@ function getProjectConfig() {
|
||||
/**
|
||||
* 获取语言资源的根目录
|
||||
*/
|
||||
function getKiwiDir() {
|
||||
function getCanaryDir() {
|
||||
const config = getProjectConfig();
|
||||
|
||||
if (config) {
|
||||
@ -62,7 +62,7 @@ function getKiwiDir() {
|
||||
* @param lang
|
||||
*/
|
||||
function getLangDir(lang) {
|
||||
const langsDir = getKiwiDir();
|
||||
const langsDir = getCanaryDir();
|
||||
return path.resolve(langsDir, lang);
|
||||
}
|
||||
|
||||
@ -162,12 +162,12 @@ function translateText(text, toLang) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译中文
|
||||
* 使用百度或者拼音翻译中文
|
||||
*/
|
||||
function translateKeyText(text: string, origin: string) {
|
||||
const CONFIG = getProjectConfig();
|
||||
const { appId, appKey } = CONFIG.baiduApiKey;
|
||||
const baiduTranslate = require('baidu-translate');
|
||||
function translateWithBaiduPinyin(text: string, origin: string) {
|
||||
const CONFIG = getProjectConfig()
|
||||
const { appId, appKey } = CONFIG.baiduApiKey
|
||||
const baiduTranslate = require('baidu-translate')
|
||||
|
||||
function _translateText() {
|
||||
return withTimeout(
|
||||
@ -205,11 +205,7 @@ function findMatchKey(langObj, text) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findMatchValue(langObj, key) {
|
||||
return langObj[key];
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,8 +272,8 @@ async function getTranslateOriginType() {
|
||||
/**
|
||||
* 成功的提示
|
||||
*/
|
||||
function successInfo(message: string) {
|
||||
console.log('successInfo: ', colors.green(message));
|
||||
function successInfo(message: string, needEnter = false) {
|
||||
console.log(`${needEnter ? '\n' : ''}successInfo: `, colors.green(message));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -295,7 +291,7 @@ function highlightText(message: string | number) {
|
||||
}
|
||||
|
||||
export {
|
||||
getKiwiDir,
|
||||
getCanaryDir,
|
||||
getLangDir,
|
||||
traverse,
|
||||
retry,
|
||||
@ -304,11 +300,10 @@ export {
|
||||
getProjectConfig,
|
||||
translateText,
|
||||
findMatchKey,
|
||||
findMatchValue,
|
||||
flatten,
|
||||
lookForFiles,
|
||||
getTranslateOriginType,
|
||||
translateKeyText,
|
||||
translateWithBaiduPinyin,
|
||||
successInfo,
|
||||
failInfo,
|
||||
highlightText
|
||||
|
Loading…
x
Reference in New Issue
Block a user