i18-fe-automator
Version:
前端代码提取中文并替换成$t函数
412 lines (411 loc) • 17.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const inquirer_1 = __importDefault(require("inquirer"));
const path_1 = __importDefault(require("path"));
const prettier_1 = __importDefault(require("prettier"));
const glob_1 = __importDefault(require("glob"));
const merge_1 = __importDefault(require("lodash/merge"));
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
const transform_1 = __importDefault(require("./transform"));
const log_1 = __importDefault(require("./utils/log"));
const getAbsolutePath_1 = require("./utils/getAbsolutePath");
const collector_1 = __importDefault(require("./collector"));
const translate_1 = __importDefault(require("./translate"));
const getLang_1 = __importDefault(require("./utils/getLang"));
const constants_1 = require("./utils/constants");
const stateManager_1 = __importDefault(require("./utils/stateManager"));
const exportExcelEnAndCn_1 = __importDefault(require("./exportExcelEnAndCn"));
const initConfig_1 = require("./utils/initConfig");
const saveLocaleFile_1 = require("./utils/saveLocaleFile");
const assertType_1 = require("./utils/assertType");
function isValidInput(input) {
const inputPath = (0, getAbsolutePath_1.getAbsolutePath)(process.cwd(), input);
if (!fs_extra_1.default.existsSync(inputPath)) {
log_1.default.error(`路径${inputPath}不存在,请重新设置input参数`);
process.exit(1);
}
// if (!fs.statSync(inputPath).isDirectory()) {
// log.error(`路径${inputPath}不是一个目录,请重新设置input参数`)
// process.exit(1)
// }
return true;
}
function getSourceFilePaths(input, exclude) {
if (isValidInput(input)) {
// 先判断是否是单个文件
if (path_1.default.extname(input)) {
return [input];
}
return glob_1.default.sync(`${input}/**/*.{cjs,mjs,js,ts,tsx,jsx,vue}`, {
ignore: exclude,
});
}
else {
return [];
}
}
function saveLocale(localePath, collector) {
const keyMap = collector.getKeyMap();
const localeAbsolutePath = (0, getAbsolutePath_1.getAbsolutePath)(process.cwd(), localePath);
if (!fs_extra_1.default.existsSync(localeAbsolutePath)) {
fs_extra_1.default.ensureFileSync(localeAbsolutePath);
}
if (!fs_extra_1.default.statSync(localeAbsolutePath).isFile()) {
log_1.default.error(`路径${localePath}不是一个文件,请重新设置localePath参数`);
process.exit(1);
}
(0, saveLocaleFile_1.saveLocaleFile)(keyMap, localeAbsolutePath);
log_1.default.verbose(`输出中文语言包到指定位置:`, localeAbsolutePath);
}
function getPrettierParser(ext) {
switch (ext) {
case 'vue':
return 'vue';
case 'ts':
case 'tsx':
return 'babel-ts';
default:
return 'babel';
}
}
function getOutputPath(input, output, sourceFilePath) {
let outputPath;
if (output) {
const filePath = sourceFilePath.replace(input + '/', '');
outputPath = (0, getAbsolutePath_1.getAbsolutePath)(process.cwd(), output, filePath);
fs_extra_1.default.ensureFileSync(outputPath);
}
else {
outputPath = (0, getAbsolutePath_1.getAbsolutePath)(process.cwd(), sourceFilePath);
}
return outputPath;
}
function formatInquirerResult(answers) {
if (answers.translator === constants_1.YOUDAO) {
return {
translator: answers.translator,
youdao: {
key: answers.key,
secret: answers.secret,
},
};
}
else if (answers.translator === constants_1.BAIDU) {
return {
translator: answers.translator,
baidu: {
key: answers.key,
secret: answers.secret,
},
};
}
else if (answers.translator === constants_1.CHAT_GPT) {
return {
translator: answers.translator,
chatGPT: {
key: answers.key,
proxy: answers.proxy,
},
};
}
else if (answers.translator === constants_1.GOOGLE) {
return {
translator: answers.translator,
google: {
proxy: answers.proxy,
},
};
}
else {
return {
translator: answers.translator,
baiduAI: {
access_token: answers.access_token,
},
};
}
}
async function getTranslationConfig() {
const cachePath = (0, getAbsolutePath_1.getAbsolutePath)(__dirname, '../.cache/configCache.json');
fs_extra_1.default.ensureFileSync(cachePath);
const cache = fs_extra_1.default.readFileSync(cachePath, 'utf8') || '{}';
const oldConfigCache = JSON.parse(cache);
const answers = await inquirer_1.default.prompt([
{
type: 'list',
name: 'translator',
message: '请选择翻译接口',
default: constants_1.BAIDU,
choices: [
{ name: '百度AI翻译', value: constants_1.BAIDU_AI },
{ name: '百度翻译', value: constants_1.BAIDU },
{ name: '有道翻译', value: constants_1.YOUDAO },
{ name: '谷歌翻译', value: constants_1.GOOGLE },
{ name: 'chatGPT翻译', value: constants_1.CHAT_GPT },
],
when(answers) {
return !answers.skipTranslate;
},
},
{
type: 'input',
name: 'proxy',
message: '使用谷歌服务需要翻墙,请输入代理地址(如果已经翻墙,直接回车)',
default: oldConfigCache.proxy || '',
when(answers) {
return answers.translator === constants_1.GOOGLE;
},
// validate(input) {
// return input.length === 0 ? '代理地址不能为空' : true
// },
},
{
type: 'input',
name: 'proxy',
message: '使用chatGPT服务需要翻墙,请输入代理地址(如果已经翻墙,直接回车)',
default: oldConfigCache.proxy || '',
when(answers) {
return answers.translator === constants_1.CHAT_GPT;
},
// validate(input) {
// return input.length === 0 ? '代理地址不能为空' : true
// },
},
{
type: 'input',
name: 'key',
message: '使用chatGPT的OPENAI_API_KEY',
default: oldConfigCache.key || '',
when(answers) {
return answers.translator === constants_1.CHAT_GPT;
},
validate(input) {
return input.length === 0 ? 'OPENAI_API_KEY不能为空' : true;
},
},
{
type: 'input',
name: 'key',
message: '请输入有道翻译appKey',
default: oldConfigCache.key || '',
when(answers) {
return answers.translator === constants_1.YOUDAO;
},
validate(input) {
return input.length === 0 ? 'appKey不能为空' : true;
},
},
{
type: 'input',
name: 'secret',
message: '请输入有道翻译appSecret',
default: oldConfigCache.secret || '',
when(answers) {
return answers.translator === constants_1.YOUDAO;
},
validate(input) {
return input.length === 0 ? 'appSecret不能为空' : true;
},
},
{
type: 'input',
name: 'key',
message: '请输入百度翻译appId',
default: oldConfigCache.key || '',
when(answers) {
return answers.translator === constants_1.BAIDU;
},
validate(input) {
return input.length === 0 ? 'appKey不能为空' : true;
},
},
{
type: 'input',
name: 'secret',
message: '请输入百度翻译appSecret',
default: oldConfigCache.secret || '',
when(answers) {
return answers.translator === constants_1.BAIDU;
},
validate(input) {
return input.length === 0 ? 'appSecret不能为空' : true;
},
},
{
type: 'input',
name: 'access_token',
message: '请输入百度AI翻译access_token',
default: oldConfigCache.access_token || '',
when(answers) {
return answers.translator === constants_1.BAIDU_AI;
},
validate(input) {
return input.length === 0 ? 'access_token不能为空' : true;
},
},
]);
const newConfigCache = Object.assign(oldConfigCache, answers);
fs_extra_1.default.writeFileSync(cachePath, JSON.stringify(newConfigCache), 'utf8');
const result = formatInquirerResult(answers);
return result;
}
function formatCode(code, ext, prettierConfig) {
let stylizedCode = code;
if ((0, assertType_1.isObject)(prettierConfig)) {
stylizedCode = prettier_1.default.format(code, {
...prettierConfig,
parser: getPrettierParser(ext),
});
log_1.default.verbose(`格式化代码完成`);
}
return stylizedCode;
}
async function default_1(options) {
let i18nConfig = (0, initConfig_1.getI18nConfig)(options);
if (!i18nConfig.skipTranslate) {
const translationConfig = await getTranslationConfig();
i18nConfig = (0, merge_1.default)(i18nConfig, translationConfig);
}
// 全局缓存脚手架配置
stateManager_1.default.setToolConfig(i18nConfig);
const { input, exclude, output, rules, localePath, locales, skipExtract, skipTranslate, adjustKeyMap, } = i18nConfig;
log_1.default.debug(`命令行配置信息:`, i18nConfig);
let oldPrimaryLang = {};
// 主语言,中文json
const primaryLangPath = (0, getAbsolutePath_1.getAbsolutePath)(process.cwd(), localePath);
oldPrimaryLang = (0, getLang_1.default)(primaryLangPath);
const skipFiles = [];
if (!skipExtract) {
const cnCollector = collector_1.default.getInstance();
log_1.default.info('正在转换中文,请稍等...');
const sourceFilePaths = getSourceFilePaths(input, exclude);
// const bar = new cliProgress.SingleBar(
// {
// format: `${chalk.cyan('提取进度:')} [{bar}] {percentage}% {value}/{total}`,
// },
// cliProgress.Presets.shades_classic
// )
const startTime = new Date().getTime();
// bar.start(sourceFilePaths.length, 0)
sourceFilePaths.forEach((sourceFilePath) => {
log_1.default.verbose(`正在提取文件中的中文:`, sourceFilePath);
const sourceCode = fs_extra_1.default.readFileSync(sourceFilePath, 'utf8');
const ext = path_1.default.extname(sourceFilePath).replace('.', '');
cnCollector.resetCountOfAdditions();
cnCollector.setCurrentCollectorPath(sourceFilePath);
// 判断sourceCode中是否包含$hxt
if (sourceCode.indexOf('$hxt') > -1) {
log_1.default.info(`当前文件包含$hxt,说明已经执行过脚本,跳过提取中文:` + sourceFilePath);
skipFiles.push(sourceFilePath);
return;
}
const { code } = (0, transform_1.default)(sourceCode, ext, rules, sourceFilePath, {
collector: cnCollector,
});
log_1.default.verbose(`完成中文提取和语法转换:`, sourceFilePath);
// 只有文件提取过中文,或文件规则forceImport为true时,才重新写入文件
if (cnCollector.getCountOfAdditions() > 0 || rules[ext].forceImport) {
const stylizedCode = formatCode(code, ext, i18nConfig.prettier);
const outputPath = getOutputPath(input, output, sourceFilePath);
fs_extra_1.default.writeFileSync(outputPath, stylizedCode, 'utf8');
log_1.default.verbose(`生成文件:`, outputPath);
}
// 自定义当前文件的keyMap
if (adjustKeyMap) {
const newkeyMap = adjustKeyMap((0, cloneDeep_1.default)(cnCollector.getKeyMap()), cnCollector.getCurrentFileKeyMap(), sourceFilePath);
cnCollector.setKeyMap(newkeyMap);
cnCollector.resetCurrentFileKeyMap();
}
// bar.increment()
});
// 增量转换时,保留之前的提取的中文结果
if (i18nConfig.incremental) {
const newkeyMap = (0, merge_1.default)(oldPrimaryLang, cnCollector.getKeyMap());
cnCollector.setKeyMap(newkeyMap);
}
saveLocale(localePath, cnCollector);
// bar.stop()
const endTime = new Date().getTime();
log_1.default.info(`耗时${((endTime - startTime) / 1000).toFixed(2)}s`);
}
log_1.default.success('中文转换完毕!');
// 以上这些是把中文提取出来,并且更新zh-CN.json,还有cnCollector
console.log(''); // 空一行
let targetContent = {};
if (!skipTranslate) {
// localePath: 'src/locales/zh-CN.json',
// locales: ['en-US'],
// oldPrimaryLang,现在中文的内容
targetContent = await (0, translate_1.default)(localePath, locales, oldPrimaryLang, {
translator: i18nConfig.translator,
google: i18nConfig.google,
youdao: i18nConfig.youdao,
baidu: i18nConfig.baidu,
chatGPT: i18nConfig.chatGPT,
baiduAI: i18nConfig.baiduAI,
});
log_1.default.success('英文转换完毕!');
const enCollector = collector_1.default.getInstance();
// log.success('英文JSON')
// 全驼峰,无空格的英文key,hump-en-US.json
// {你好世界: "HelloWorld"}
log_1.default.success(JSON.stringify(targetContent));
// 将$t中的中文翻译成英文后再写入项目中
log_1.default.info('正在将$t代码中的key转成该中文(desc)对应的英文,请稍等...');
const sourceFilePaths = getSourceFilePaths(input, exclude);
// const bar = new cliProgress.SingleBar(
// {
// format: `${chalk.cyan('提取进度:')} [{bar}] {percentage}% {value}/{total}`,
// },
// cliProgress.Presets.shades_classic
// )
// bar.start(sourceFilePaths.length, 0)
sourceFilePaths.forEach((sourceFilePath, i) => {
if (skipFiles.includes(sourceFilePath)) {
return;
}
// log.verbose(`正在提取文件中的英文:`, sourceFilePath)
const sourceCode = fs_extra_1.default.readFileSync(sourceFilePath, 'utf8');
const ext = path_1.default.extname(sourceFilePath).replace('.', '');
enCollector.resetCountOfAdditions();
enCollector.setCurrentCollectorPath(sourceFilePath);
log_1.default.debug(`当前操作第${i + 1}个文件,路径是:${sourceFilePath}`);
const { code } = (0, transform_1.default)(sourceCode, ext, rules, sourceFilePath, {
sourceContent: targetContent,
collector: enCollector,
});
// log.verbose(`完成英文提取和语法转换:`, sourceFilePath)
log_1.default.debug('enCollector.getCountOfAdditions()数量', enCollector.getCountOfAdditions());
if (enCollector.getCountOfAdditions() > 0) {
log_1.default.debug('开始写入');
const stylizedCode = formatCode(code, ext, i18nConfig.prettier);
const outputPath = getOutputPath(input, output, sourceFilePath);
fs_extra_1.default.writeFileSync(outputPath, stylizedCode, 'utf8');
// log.verbose(`生成文件:`, outputPath)
}
// 自定义当前文件的keyMap
if (adjustKeyMap) {
const newkeyMap = adjustKeyMap((0, cloneDeep_1.default)(enCollector.getKeyMap()), enCollector.getCurrentFileKeyMap(), sourceFilePath);
enCollector.setKeyMap(newkeyMap);
enCollector.resetCurrentFileKeyMap();
}
// bar.increment()
});
// bar.stop()
(0, exportExcelEnAndCn_1.default)(enCollector.getKeyValueMap());
log_1.default.success(`导出完毕!`);
// 第三次处理
// 找到目录下所有$hxt方法,并且包含desc等于''的,用正则匹配
// sourceFilePaths.forEach((sourceFilePath, i) => {}
}
// if (i18nConfig.exportExcel) {
// log.info(`正在导出excel翻译文件`)
// exportExcel()
// log.success(`导出完毕!`)
// }
}
exports.default = default_1;