i18n-auto-react
Version:
基于百度翻译API服务的自动翻译插件
186 lines (183 loc) • 7.48 kB
JavaScript
import { g as getDefault, r as readLanguages, i as isCallExpression, m as md5Hash, t as tplRegexp } from './index.js';
import path from 'path';
import ignore from 'ignore';
import t from '@babel/types';
import { parse } from '@babel/parser';
import { addNamed } from '@babel/helper-module-imports';
import chalk from 'chalk';
import _traverse from '@babel/traverse';
import _generator from '@babel/generator';
import { D as DisableRule } from './disableRule.js';
import { z as zhExt } from './tpl.js';
let traverse = getDefault(_traverse);
let generator = getDefault(_generator);
let baseLocale;
let _options;
function i18nPlugin(content, options) {
options.__rootPath = options.__rootPath || process.cwd();
if (!_options)
_options = options;
let relativePath = path.relative(options.__rootPath, options.filePath);
try {
let includes = ignore().add(options.include);
let included = includes.ignores(relativePath);
let ig = ignore().add(options.exclude || []);
if (ig.ignores(relativePath) && !included)
return;
}
catch (error) {
return;
}
const { source: importSource, imported: importImported, local: importLocal } = options.importInfo;
let plugins = ['jsx', 'typescript', 'decorators-legacy'];
let ast = parse(content, {
sourceType: 'module',
errorRecovery: true,
plugins: plugins
});
if (ast.errors && ast.errors.length > 0) {
console.warn(ast.errors);
return;
}
// 注释禁用
let disableRule = new DisableRule(ast.comments || []);
if (disableRule.entireFileDisabled)
return;
if (!baseLocale)
baseLocale = readLanguages('zh', options, true);
let needI18n = false;
traverse(ast, {
// 增加导入翻译函数
Program: {
exit(path) {
// 如果需要国际化
if (needI18n) {
let addI18n_1 = true;
path.traverse({
ImportDeclaration(importPath) {
if (importPath.node.source.value.includes(importSource)) {
let specifiers = importPath.node.specifiers;
if (specifiers.length > 0) {
let registerLocaleIndex = specifiers.findIndex((n) => n.local.name === importLocal);
addI18n_1 = registerLocaleIndex === -1;
}
}
}
});
if (addI18n_1) {
addNamed(path, importImported, importSource, {
nameHint: importLocal,
importPosition: 'after'
});
}
}
}
},
// jsx文本: <div>花飘万家雪</div>
JSXText: function (path) {
if (isCallExpression(path, options.importInfo))
return;
if (disableRule.test(path.node.loc))
return;
if (zhExt.test(path.toString())) {
let value = path.toString().trim();
let id = md5Hash(value);
if (noLocale(value, id, relativePath, path.node.loc))
return;
let origininalValue = path.node.value;
let trimmedValue = origininalValue.trim();
let valueIndex = origininalValue.indexOf(trimmedValue);
let spacesLeft = origininalValue.substring(0, valueIndex);
let spacesRight = origininalValue.substring(valueIndex + trimmedValue.length);
path.replaceWithMultiple([
t.jsxText(spacesLeft),
t.jsxExpressionContainer(t.callExpression(t.identifier(importLocal), [t.stringLiteral(id)])),
t.jsxText(spacesRight)
]);
needI18n = true;
}
},
// 模板文本: `花飘万家雪${xxx}`
TemplateLiteral: function (path) {
if (isCallExpression(path, options.importInfo))
return;
if (disableRule.test(path.node.loc))
return;
if (zhExt.test(path.toString())) {
let node = path.node;
let isCh = false;
node.quasis &&
node.quasis.forEach((item) => {
if (zhExt.test(item.value.raw))
isCh = true;
});
if (isCh) {
let i = 0;
let value = path
.toString()
.replace(/^`|`$/g, '')
.replace(tplRegexp, () => `{{@${++i}}}`);
let id = md5Hash(value);
if (noLocale(value, id, relativePath, path.node.loc))
return;
path.replaceWith(t.callExpression(t.identifier(importLocal), [
t.stringLiteral(id),
t.objectExpression(node.expressions.map((item, index) => t.objectProperty(t.stringLiteral(`@${index + 1}`), item)))
]));
needI18n = true;
}
}
},
// 普通文本: '花飘万家雪'
StringLiteral: function (path) {
// 如果父节点是 ts 则不处理, 如 type AA = '你好' | '大家好'
if (t.isTSLiteralType(path.parent))
return;
if (isCallExpression(path, options.importInfo))
return;
if (disableRule.test(path.node.loc))
return;
let value = path?.node?.value?.toString();
if (zhExt.test(value)) {
let id = md5Hash(value);
if (noLocale(value, id, relativePath, path.node.loc))
return;
// 如果是属性中有中文,需要加上{}
if (t.isJSXAttribute(path.parent)) {
path.replaceWith(t.jsxExpressionContainer(t.callExpression(t.identifier(importLocal), [t.stringLiteral(id)])));
}
else {
// 普通文本
path.replaceWith(t.callExpression(t.identifier(importLocal), [t.stringLiteral(id)]));
}
needI18n = true;
}
}
});
if (needI18n) {
let codeRes = generator(ast, {
retainLines: true,
jsescOption: { minimal: true },
decoratorsBeforeExport: true,
sourceMaps: true,
sourceFileName: options.filePath
}, content);
return {
code: codeRes.code,
map: codeRes.map
};
}
}
function noLocale(value, id, relativePath, loc) {
if (!baseLocale)
return false;
if (!baseLocale[id]) {
let line = chalk.bold(`Line ${loc.start.line}:${loc.start.column}:`);
let content = `${line} 在语言包中未发现以下字段【${chalk.blue(value)}】请更新语言包`;
let res = new Error(`[i18n-auto-react]\n${relativePath}\n ${content}`);
// @ts-ignore
_options.warning && _options.emitWarning(_options.isVite ? content : res);
return true;
}
}
export { i18nPlugin as i };