i18n-xy
Version:
自动提取React项目中的中文字符串并进行国际化的CLI工具
1,343 lines (1,326 loc) • 182 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// main/cli/index.ts
var import_commander = require("commander");
// package.json
var version = "0.0.30";
// main/utils/fs.ts
var fs = __toESM(require("fs-extra"), 1);
var path = __toESM(require("path"), 1);
var import_fast_glob = __toESM(require("fast-glob"), 1);
function readFile2(filePath, encoding = "utf-8") {
const absPath = path.resolve(process.cwd(), filePath);
return fs.readFile(absPath, encoding);
}
function writeFile(filePath, data) {
const absPath = path.resolve(process.cwd(), filePath);
return fs.outputFile(absPath, data);
}
function writeFileWithTempDir(filePath, data, tempDir) {
let targetPath;
if (tempDir) {
const relativePath = path.relative(process.cwd(), filePath);
targetPath = path.resolve(process.cwd(), tempDir, relativePath);
} else {
targetPath = path.resolve(process.cwd(), filePath);
}
return fs.outputFile(targetPath, data);
}
function ensureDir2(dirPath) {
const absPath = path.resolve(process.cwd(), dirPath);
return fs.ensureDir(absPath);
}
function findTargetFiles(include, exclude = []) {
return (0, import_fast_glob.default)(include, { ignore: exclude, dot: true, cwd: process.cwd() });
}
function fileExists(filePath) {
const absPath = path.resolve(process.cwd(), filePath);
return fs.existsSync(absPath);
}
function readJsonSync2(filePath) {
const absPath = path.resolve(process.cwd(), filePath);
return fs.readJsonSync(absPath);
}
async function readJson(filePath, defaultValue) {
try {
const content = await readFile2(filePath, "utf-8");
return JSON.parse(content);
} catch (error) {
if (defaultValue !== void 0) {
return defaultValue;
}
throw error;
}
}
function writeJson(filePath, data, pretty = true) {
const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
return writeFile(filePath, content);
}
// main/config/index.ts
var import_lodash = require("lodash");
// main/config/defaults.ts
var defaultConfig = {
// 基础语言配置
locale: "zh-CN",
displayLanguage: "en-US",
outputDir: "locales",
// tempDir: undefined, // 可选,不设置则直接修改源文件
// 文件处理配置
include: [
"src/**/*.{js,jsx,ts,tsx}"
],
exclude: [
"node_modules/**",
"dist/**",
"build/**",
"**/*.test.{js,jsx,ts,tsx}",
"**/*.spec.{js,jsx,ts,tsx}"
],
// Key 生成配置
keyGeneration: {
maxChineseLength: 10,
// 最大汉字长度
hashLength: 6,
// 哈希长度
maxRetryCount: 5,
// 最大重试次数
reuseExistingKey: true,
// 默认重复使用相同文案的key
duplicateKeySuffix: "hash",
// 重复key后缀模式,默认hash
keyPrefix: "",
// key前缀,默认为空
separator: "_",
// 连接符,默认下划线
pinyinOptions: {
// 拼音转换选项
toneType: "none",
// 不带声调
type: "array"
// 返回数组格式
}
},
// 输出配置
output: {
prettyJson: true,
// 格式化 JSON 输出
localeFileName: "{locale}.json"
// 语言文件名格式
},
// 日志配置
logging: {
enabled: true,
// 默认启用日志
level: "normal"
// 默认正常级别
},
// 替换配置
replacement: {
functionName: "$t",
// 默认替换函数名
quoteType: "single",
// 默认使用单引号
useOriginalTextAsKey: false,
// 默认使用生成的key
templateString: {
enabled: true,
// 默认启用模板字符串智能处理
preserveExpressions: true,
// 默认保留原始表达式
splitStrategy: "smart"
// 默认智能拆分策略
},
autoImport: {
enabled: false,
// 默认不启用自动引入
insertPosition: "afterImports",
// 默认在import语句后插入
imports: {}
}
},
// 翻译配置
translation: {
enabled: false,
// 默认不启用翻译功能
provider: "baidu",
// 默认使用百度翻译
defaultSourceLang: "zh",
// 默认源语言:中文
defaultTargetLang: "en",
// 默认目标语言:英文
concurrency: 5,
// 默认并发数:5 (降低并发数避免API限制)
retryTimes: 3,
// 默认重试次数:3次(不包括第一次)
retryDelay: 1e3,
// 默认重试延迟:1秒
batchDelay: 500,
// 默认批次延迟:0.5秒
baidu: {
appid: "",
// 需要用户配置
key: ""
// 需要用户配置
}
}
};
// main/config/manager.ts
var ConfigManagerClass = class {
config = null;
init(config) {
this.config = config;
}
get() {
if (!this.config) {
throw new Error("ConfigManager: config not initialized!");
}
return this.config;
}
reset() {
this.config = null;
}
isInitialized() {
return this.config !== null;
}
};
var ConfigManager = new ConfigManagerClass();
// main/config/validator.ts
var ConfigValidator = class {
/**
* 检查配置的一致性和完整性
*/
static validateConfigUsage() {
var _a, _b, _c, _d, _e;
const errors = [];
const warnings = [];
try {
const config = ConfigManager.get();
if (!config.include || config.include.length === 0) {
errors.push("\u914D\u7F6E\u9879 include \u4E0D\u80FD\u4E3A\u7A7A");
}
if (!config.outputDir) {
errors.push("\u914D\u7F6E\u9879 outputDir \u4E0D\u80FD\u4E3A\u7A7A");
}
if (!config.locale) {
errors.push("\u914D\u7F6E\u9879 locale \u4E0D\u80FD\u4E3A\u7A7A");
}
if ((_a = config.translation) == null ? void 0 : _a.enabled) {
if (!config.translation.provider) {
errors.push("\u542F\u7528\u7FFB\u8BD1\u65F6\u5FC5\u987B\u6307\u5B9A provider");
}
if (config.translation.provider === "baidu") {
if (!((_b = config.translation.baidu) == null ? void 0 : _b.appid) || !((_c = config.translation.baidu) == null ? void 0 : _c.key)) {
warnings.push("\u767E\u5EA6\u7FFB\u8BD1\u670D\u52A1\u7F3A\u5C11 appid \u6216 key \u914D\u7F6E");
}
}
}
if (config.keyGeneration) {
if (config.keyGeneration.maxChineseLength && config.keyGeneration.maxChineseLength <= 0) {
warnings.push("maxChineseLength \u5E94\u8BE5\u5927\u4E8E0");
}
if (config.keyGeneration.hashLength && config.keyGeneration.hashLength <= 0) {
warnings.push("hashLength \u5E94\u8BE5\u5927\u4E8E0");
}
}
if (((_d = config.logging) == null ? void 0 : _d.level) && !["minimal", "normal", "verbose"].includes(config.logging.level)) {
errors.push("logging.level \u5FC5\u987B\u662F minimal\u3001normal \u6216 verbose \u4E4B\u4E00");
}
if (((_e = config.output) == null ? void 0 : _e.localeFileName) && !config.output.localeFileName.includes("{locale}")) {
warnings.push("localeFileName \u5EFA\u8BAE\u5305\u542B {locale} \u5360\u4F4D\u7B26");
}
} catch (error) {
errors.push(`\u914D\u7F6E\u68C0\u67E5\u65F6\u53D1\u751F\u9519\u8BEF: ${error}`);
}
const isValid = errors.length === 0;
if (!isValid) {
Logger.error("\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25:", "minimal");
errors.forEach((error) => Logger.error(` - ${error}`, "minimal"));
}
if (warnings.length > 0) {
Logger.warn("\u914D\u7F6E\u8B66\u544A:", "normal");
warnings.forEach((warning) => Logger.warn(` - ${warning}`, "normal"));
}
return { isValid, errors, warnings };
}
/**
* 检查是否存在配置与代码不一致的问题
*/
static checkConfigConsistency() {
var _a, _b;
const config = ConfigManager.get();
Logger.verbose("\u68C0\u67E5\u914D\u7F6E\u4E00\u81F4\u6027...");
const expectedFileName = (((_a = config.output) == null ? void 0 : _a.localeFileName) ?? "{locale}.json").replace(
"{locale}",
config.locale
);
Logger.verbose(`\u9884\u671F\u7684\u8BED\u8A00\u6587\u4EF6\u540D: ${expectedFileName}`);
const functionName = ((_b = config.replacement) == null ? void 0 : _b.functionName) ?? "$t";
Logger.verbose(`\u9884\u671F\u7684\u66FF\u6362\u51FD\u6570\u540D: ${functionName}`);
Logger.verbose(`\u8F93\u51FA\u76EE\u5F55: ${config.outputDir}`);
if (config.tempDir) {
Logger.verbose(`\u4E34\u65F6\u76EE\u5F55: ${config.tempDir}`);
}
Logger.verbose("\u914D\u7F6E\u4E00\u81F4\u6027\u68C0\u67E5\u5B8C\u6210");
}
};
// main/config/index.ts
function validateConfig(config) {
var _a, _b, _c, _d;
const errors = [];
if (!config.include || !Array.isArray(config.include) || config.include.length === 0) {
errors.push("include \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4");
}
if (!config.outputDir || typeof config.outputDir !== "string") {
errors.push("outputDir \u5B57\u6BB5\u5FC5\u987B\u662F\u6709\u6548\u7684\u5B57\u7B26\u4E32\u8DEF\u5F84");
}
if (!config.locale || typeof config.locale !== "string") {
errors.push("locale \u5B57\u6BB5\u5FC5\u987B\u662F\u6709\u6548\u7684\u8BED\u8A00\u4EE3\u7801");
}
if ((_a = config.translation) == null ? void 0 : _a.enabled) {
if (!config.translation.provider) {
errors.push("\u542F\u7528\u7FFB\u8BD1\u65F6\u5FC5\u987B\u6307\u5B9A translation.provider");
}
if (config.translation.provider === "baidu") {
if (!((_b = config.translation.baidu) == null ? void 0 : _b.appid) || !((_c = config.translation.baidu) == null ? void 0 : _c.key)) {
errors.push("\u4F7F\u7528\u767E\u5EA6\u7FFB\u8BD1\u65F6\u5FC5\u987B\u914D\u7F6E translation.baidu.appid \u548C translation.baidu.key");
}
}
}
if (((_d = config.logging) == null ? void 0 : _d.level) && !["minimal", "normal", "verbose"].includes(config.logging.level)) {
errors.push("logging.level \u5FC5\u987B\u662F minimal\u3001normal \u6216 verbose \u4E4B\u4E00");
}
if (errors.length > 0) {
Logger.error("\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25:", "minimal");
errors.forEach((error) => Logger.error(` - ${error}`, "minimal"));
throw new Error(`\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25: ${errors.join("; ")}`);
}
}
function loadConfig(customConfigPath) {
let userConfig = {};
if (customConfigPath) {
if (fileExists(customConfigPath)) {
Logger.verbose(`\u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6: ${customConfigPath}`);
try {
userConfig = readJsonSync2(customConfigPath);
Logger.verbose("\u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u6210\u529F");
} catch (error) {
throw new Error(`\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${error}`);
}
} else {
Logger.warn(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${customConfigPath}\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E`);
}
}
const finalConfig = (0, import_lodash.merge)({}, defaultConfig, userConfig);
validateConfig(finalConfig);
Logger.verbose("\u914D\u7F6E\u9A8C\u8BC1\u901A\u8FC7");
return finalConfig;
}
// main/utils/logger.ts
var Logger = class {
static temporaryLevel = null;
// 临时日志级别
static getLogLevel() {
var _a;
try {
if (this.temporaryLevel) {
return this.temporaryLevel;
}
const config = ConfigManager.get();
return ((_a = config.logging) == null ? void 0 : _a.level) ?? "normal";
} catch {
return "normal";
}
}
/**
* 临时设置日志级别(用于进度显示期间)
*/
static setTemporaryLevel(level) {
this.temporaryLevel = level;
}
static shouldLog(level) {
const currentLevel = this.getLogLevel();
if (currentLevel === "minimal") {
return level === "minimal";
} else if (currentLevel === "normal") {
return level !== "verbose";
} else if (currentLevel === "verbose") {
return true;
}
return false;
}
static success(message, level = "normal") {
if (this.shouldLog(level)) {
console.log(`\u2705 ${message}`);
}
}
static info(message, level = "normal") {
if (this.shouldLog(level)) {
console.log(`\u2139\uFE0F ${message}`);
}
}
static warn(message, level = "normal") {
if (this.shouldLog(level)) {
console.warn(`\u26A0\uFE0F ${message}`);
}
}
static error(message, level = "normal") {
if (this.shouldLog(level)) {
console.error(`\u274C ${message}`);
}
}
static verbose(message) {
this.info(message, "verbose");
}
};
// main/cli/commands/init.ts
async function initHandler(options) {
var _a;
const outputDir = options.outputDir || ".test-output/locales";
const tempDir = options.tempDir || ".test-output/temp";
const configPath = options.configPath || "./i18n.config.json";
const locale = options.locale || defaultConfig.locale;
const functionName = options.functionName || ((_a = defaultConfig.replacement) == null ? void 0 : _a.functionName);
const config = {
...defaultConfig,
outputDir,
tempDir,
locale,
replacement: {
...defaultConfig.replacement,
functionName
}
};
try {
await writeJson(configPath, config, true);
Logger.success(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210: ${configPath}`, "minimal");
Logger.info("\u4F60\u53EF\u4EE5\u6839\u636E\u9879\u76EE\u9700\u6C42\u4FEE\u6539\u914D\u7F6E\u6587\u4EF6\u3002", "normal");
} catch (error) {
Logger.error(`\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5931\u8D25: ${error}`, "minimal");
process.exit(1);
}
}
var initCommand = initHandler;
// main/cli/wrapper.ts
function withCLI(handler, wrapperOptions = {}) {
return async (options) => {
var _a;
try {
const processedOptions = ((_a = wrapperOptions.preProcess) == null ? void 0 : _a.call(wrapperOptions, options)) ?? options;
let config = null;
if (wrapperOptions.configRequired !== false) {
const configPath = processedOptions.config ?? "./i18n.config.json";
config = loadConfig(configPath);
ConfigManager.init(config);
const validation = ConfigValidator.validateConfigUsage();
if (!validation.isValid) {
Logger.error("\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u6267\u884C", "minimal");
process.exit(1);
}
ConfigValidator.checkConfigConsistency();
}
if (config && wrapperOptions.validator) {
wrapperOptions.validator(config);
}
await handler(processedOptions, config);
} catch (error) {
Logger.error(`\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${error}`, "minimal");
process.exit(1);
}
};
}
// main/core/ast/index.ts
var import_parser2 = require("@babel/parser");
var t2 = __toESM(require("@babel/types"), 1);
// main/core/key-generator/index.ts
var import_pinyin_pro = require("pinyin-pro");
var path2 = __toESM(require("path"), 1);
var import_string_hash = __toESM(require("string-hash"), 1);
var keyValueCache = {};
var valueKeyCache = {};
var outputFilePath = "";
async function initI18nCache() {
var _a;
const config = ConfigManager.get();
const outputDir = config.outputDir ?? "locales";
await ensureDir2(outputDir);
const localeFileName = ((_a = config.output) == null ? void 0 : _a.localeFileName) ?? "{locale}.json";
const fileName = localeFileName.replace("{locale}", config.locale ?? "zh-CN");
outputFilePath = path2.join(outputDir, fileName);
keyValueCache = await readJson(outputFilePath, {});
valueKeyCache = {};
for (const [key, value] of Object.entries(keyValueCache)) {
valueKeyCache[value] = key;
}
const existingKeyCount = Object.keys(keyValueCache).length;
if (existingKeyCount > 0) {
Logger.verbose(`\u52A0\u8F7D\u4E86 ${existingKeyCount} \u4E2A\u5DF2\u5B58\u5728\u7684\u7FFB\u8BD1\u952E`);
}
await writeJson(outputFilePath, keyValueCache);
}
function toShortHash(text) {
var _a;
const config = ConfigManager.get();
const hashLength = ((_a = config.keyGeneration) == null ? void 0 : _a.hashLength) ?? 6;
return (0, import_string_hash.default)(text).toString(36).slice(0, hashLength);
}
function generateUniqueKey(baseKey, text) {
var _a, _b;
const config = ConfigManager.get();
const separator = ((_a = config.keyGeneration) == null ? void 0 : _a.separator) ?? "_";
const maxRetry = ((_b = config.keyGeneration) == null ? void 0 : _b.maxRetryCount) ?? 5;
if (!keyValueCache[baseKey]) {
return baseKey;
}
let finalKey = baseKey + separator + toShortHash(text);
let tryCount = 0;
while (keyValueCache[finalKey] && tryCount < maxRetry) {
finalKey = baseKey + separator + toShortHash(text + Math.random().toString());
tryCount++;
}
if (tryCount >= maxRetry) {
Logger.warn(`\u751F\u6210\u552F\u4E00key\u5931\u8D25\uFF0C\u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570: ${baseKey}`);
finalKey = baseKey + separator + Date.now().toString(36);
}
return finalKey;
}
function createI18nKey(text) {
var _a, _b, _c, _d, _e, _f;
const config = ConfigManager.get();
const reuseExistingKey = ((_a = config.keyGeneration) == null ? void 0 : _a.reuseExistingKey) ?? true;
if (reuseExistingKey && valueKeyCache[text]) {
Logger.verbose(`\u91CD\u590D\u4F7F\u7528\u5DF2\u5B58\u5728\u6587\u6848\u7684key: "${valueKeyCache[text]}" for "${text}"`);
return valueKeyCache[text];
}
const maxLength = ((_b = config.keyGeneration) == null ? void 0 : _b.maxChineseLength) ?? 10;
const han = (((_c = text.match(/[\u4e00-\u9fa5]+/g)) == null ? void 0 : _c.join("")) ?? "").slice(0, maxLength);
if (!han) {
return "";
}
const pinyinOptions = ((_d = config.keyGeneration) == null ? void 0 : _d.pinyinOptions) ?? { toneType: "none", type: "array" };
const separator = ((_e = config.keyGeneration) == null ? void 0 : _e.separator) ?? "_";
const keyPrefix = ((_f = config.keyGeneration) == null ? void 0 : _f.keyPrefix) ?? "";
const baseKey = ((0, import_pinyin_pro.pinyin)(han, {
toneType: pinyinOptions.toneType ?? "none",
type: "array"
}) ?? []).join(separator);
if (!baseKey) {
return "";
}
const keyWithPrefix = keyPrefix ? `${keyPrefix}${separator}${baseKey}` : baseKey;
const finalKey = generateUniqueKey(keyWithPrefix, text);
keyValueCache[finalKey] = text;
valueKeyCache[text] = finalKey;
return finalKey;
}
async function flushI18nCache() {
var _a;
if (outputFilePath) {
const config = ConfigManager.get();
const prettyJson = ((_a = config.output) == null ? void 0 : _a.prettyJson) ?? true;
await writeJson(outputFilePath, keyValueCache, prettyJson);
const keyCount = Object.keys(keyValueCache).length;
Logger.info(`\u5DF2\u4FDD\u5B58 ${keyCount} \u4E2A\u7FFB\u8BD1\u952E\u5230 ${outputFilePath}`, "normal");
}
}
// main/utils/import-manager.ts
var import_parser = require("@babel/parser");
var t = __toESM(require("@babel/types"), 1);
// main/utils/pattern.ts
var import_micromatch = __toESM(require("micromatch"), 1);
function matchPattern(pattern, filePath) {
try {
return import_micromatch.default.isMatch(filePath, pattern);
} catch (error) {
Logger.warn(`\u6A21\u5F0F\u5339\u914D\u51FA\u9519 - pattern: ${pattern}, filePath: ${filePath}, error: ${error}`);
return false;
}
}
function findMatchingImport(filePath, imports = {}) {
var _a;
Logger.verbose(`findMatchingImport: ${filePath}`);
const patterns = Object.keys(imports).sort((a, b) => {
const scoreA = getPatternSpecificity(a);
const scoreB = getPatternSpecificity(b);
return scoreB - scoreA;
});
for (const pattern of patterns) {
if (matchPattern(pattern, filePath)) {
Logger.verbose(`\u5339\u914D\u5230\u6A21\u5F0F: ${pattern} -> ${filePath}`);
return ((_a = imports[pattern]) == null ? void 0 : _a.importStatement) || null;
}
}
Logger.verbose(`\u672A\u627E\u5230\u5339\u914D\u7684import\u914D\u7F6E: ${filePath}`);
return null;
}
function getPatternSpecificity(pattern) {
var _a, _b, _c;
let score = 0;
if (!pattern.includes("*")) {
score += 100;
}
if (pattern.includes(".{") || pattern.match(/\.\w+$/)) {
score += 50;
}
score += (((_a = pattern.match(/\//g)) == null ? void 0 : _a.length) || 0) * 10;
score -= (((_b = pattern.match(/\*\*/g)) == null ? void 0 : _b.length) || 0) * 20;
score -= (((_c = pattern.match(/(?<!\*)\*(?!\*)/g)) == null ? void 0 : _c.length) || 0) * 5;
return score;
}
// main/utils/import-manager.ts
var generate = require("@babel/generator").default;
function hasExistingImport(ast, importStatement) {
var _a;
try {
const importAst = (0, import_parser.parse)(importStatement, {
sourceType: "module",
plugins: ["jsx", "typescript"]
});
if (!((_a = importAst.program) == null ? void 0 : _a.body) || importAst.program.body.length === 0) {
return false;
}
const newStatements = importAst.program.body;
const existingStatements = ast.program.body;
for (const newStmt of newStatements) {
let found = false;
for (const existingStmt of existingStatements) {
const newCode = generate(newStmt, { minified: true }).code;
const existingCode = generate(existingStmt, { minified: true }).code;
if (newCode === existingCode) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
} catch (error) {
Logger.verbose(`\u68C0\u67E5import\u91CD\u590D\u65F6\u51FA\u9519: ${error}`);
return false;
}
}
function addImportToAST(ast, importStatement, insertPosition = "afterImports") {
var _a;
try {
if (hasExistingImport(ast, importStatement)) {
Logger.verbose("Import\u8BED\u53E5\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u63D2\u5165");
return;
}
const importAst = (0, import_parser.parse)(importStatement, {
sourceType: "module",
plugins: ["jsx", "typescript"]
});
if (((_a = importAst.program) == null ? void 0 : _a.body) && importAst.program.body.length > 0) {
let insertIndex = 0;
if (insertPosition === "afterImports") {
for (let i = 0; i < ast.program.body.length; i++) {
if (t.isImportDeclaration(ast.program.body[i])) {
insertIndex = i + 1;
} else {
break;
}
}
} else {
insertIndex = 0;
}
ast.program.body.splice(insertIndex, 0, ...importAst.program.body);
Logger.verbose(`Import\u8BED\u53E5\u5DF2\u63D2\u5165\u5230\u4F4D\u7F6E: ${insertPosition}`);
}
} catch (error) {
Logger.warn(`\u65E0\u6CD5\u89E3\u6790import\u8BED\u53E5: ${importStatement}`);
Logger.verbose(`\u9519\u8BEF\u8BE6\u60C5: ${error}`);
}
}
function addEmptyLineAfterImport(code, importStatement) {
const cleanImportStatement = importStatement.replace(/;\s*$/g, ";").trim();
const importIndex = code.indexOf(cleanImportStatement);
if (importIndex === -1) {
Logger.verbose("\u672A\u5728\u751F\u6210\u7684\u4EE3\u7801\u4E2D\u627E\u5230import\u8BED\u53E5\uFF0C\u8DF3\u8FC7\u7A7A\u884C\u6DFB\u52A0");
return code;
}
const importEndIndex = importIndex + cleanImportStatement.length;
const charAfterImport = code.charAt(importEndIndex);
if (charAfterImport && charAfterImport !== "\n" && charAfterImport !== "\r") {
const restCode = code.substring(importEndIndex);
const trimmedRestCode = restCode.replace(/^[ \t]*/, "");
return code.substring(0, importEndIndex) + "\n\n" + trimmedRestCode;
}
let lineEndIndex = importEndIndex;
while (lineEndIndex < code.length && code.charAt(lineEndIndex) !== "\n") {
lineEndIndex++;
}
if (lineEndIndex >= code.length) {
return code + "\n";
}
const nextLineStart = lineEndIndex + 1;
if (nextLineStart >= code.length) {
return code + "\n";
}
const nextLineEnd = code.indexOf("\n", nextLineStart);
const nextLine = nextLineEnd === -1 ? code.substring(nextLineStart) : code.substring(nextLineStart, nextLineEnd);
if (nextLine.trim() !== "") {
return code.substring(0, lineEndIndex + 1) + "\n" + code.substring(lineEndIndex + 1);
}
return code;
}
var ImportManager = class {
hasImportInserted = false;
insertedImportStatement = "";
/**
* 重置状态(处理新文件时调用)
*/
reset() {
this.hasImportInserted = false;
this.insertedImportStatement = "";
}
/**
* 按需插入import语句
*/
insertImportIfNeeded(ast, file, autoImportEnabled, hasReplacement, imports, insertPosition) {
if (!this.hasImportInserted && autoImportEnabled && hasReplacement) {
const importStatement = findMatchingImport(file, imports);
if (importStatement) {
Logger.verbose(`\u4E3A\u6587\u4EF6 ${file} \u6DFB\u52A0import\u8BED\u53E5: ${importStatement.trim()}`);
addImportToAST(ast, importStatement, insertPosition);
this.hasImportInserted = true;
this.insertedImportStatement = importStatement;
} else {
Logger.warn(`\u6587\u4EF6 ${file} \u9700\u8981import\u8BED\u53E5\u4F46\u672A\u627E\u5230\u5339\u914D\u7684import\u914D\u7F6E`, "normal");
}
}
}
/**
* 为输出代码添加空行(如果插入了import语句)
*/
addEmptyLineToOutput(output) {
if (this.hasImportInserted && this.insertedImportStatement) {
const result = addEmptyLineAfterImport(output, this.insertedImportStatement);
if (result !== output) {
Logger.verbose(`\u5DF2\u4E3Aimport\u8BED\u53E5\u540E\u6DFB\u52A0\u7A7A\u884C`);
}
return result;
}
return output;
}
/**
* 检查是否已插入import语句
*/
get isImportInserted() {
return this.hasImportInserted;
}
/**
* 获取插入的import语句内容
*/
get insertedStatement() {
return this.insertedImportStatement;
}
};
// main/core/ast/index.ts
var traverse = require("@babel/traverse").default;
var generate2 = require("@babel/generator").default;
var CHINESE_RE = /[\u4e00-\u9fa5]/;
function isInComment(path8, codeLines) {
const loc = path8.node.loc;
if (!loc || !codeLines) return false;
const lineIndex = loc.start.line - 1;
if (lineIndex >= 0 && lineIndex < codeLines.length) {
const line = codeLines[lineIndex];
const beforeText = line.substring(0, loc.start.column);
const afterText = line.substring(loc.end.column);
if (beforeText.includes("//")) {
return true;
}
if (beforeText.includes("/*") && !beforeText.includes("*/")) {
return true;
}
for (let i = lineIndex - 1; i >= 0; i--) {
const prevLine = codeLines[i];
if (prevLine.includes("*/")) {
break;
}
if (prevLine.includes("/*")) {
return true;
}
}
if (beforeText.includes("/**") || beforeText.includes("*")) {
return true;
}
}
return false;
}
function analyzeContext(path8, codeLines) {
var _a;
if (codeLines && isInComment(path8, codeLines)) {
return {
shouldProcess: false,
contextType: "comment",
reason: "\u6CE8\u91CA\u4E2D\u7684\u6587\u672C"
};
}
let current = path8;
while (current) {
const parent = current.parent;
const parentPath = current.parentPath;
if (!parent || !parentPath) break;
if (t2.isObjectProperty(parent) && parent.key === current.node) {
return {
shouldProcess: false,
contextType: "object-key",
reason: "\u5BF9\u8C61\u5C5E\u6027\u952E\u540D"
};
}
if (t2.isObjectMethod(parent) && parent.key === current.node) {
return {
shouldProcess: false,
contextType: "object-key",
reason: "\u5BF9\u8C61\u65B9\u6CD5\u540D"
};
}
if (t2.isImportDeclaration(parent) || t2.isImportSpecifier(parent) || t2.isExportSpecifier(parent) || // 只有当字符串直接在export声明中时才忽略(如 export { "key" as alias })
// 而不是在export function/class内部
t2.isExportDeclaration(parent) && (t2.isExportAllDeclaration(parent) || t2.isExportDefaultDeclaration(parent) && current.node === parent.declaration || t2.isExportNamedDeclaration(parent) && ((_a = parent.specifiers) == null ? void 0 : _a.some((spec) => spec === current.node)))) {
return {
shouldProcess: false,
contextType: "import-export",
reason: "import/export\u8BED\u53E5"
};
}
if (t2.isTSTypeAnnotation(parent) || // : string
t2.isTSLiteralType(parent) || // type T = "literal"
t2.isTSIntersectionType(parent) || // A & B
t2.isTSTypeReference(parent) || // SomeType<T>
t2.isTSTypeLiteral(parent) || // { key: "value" }
t2.isTSInterfaceDeclaration(parent) || // interface I {}
t2.isTSTypeAliasDeclaration(parent) || // type T = ...
t2.isTSEnumDeclaration(parent) || // enum E {}
t2.isTSPropertySignature(parent) || // { prop: "type" }
t2.isTSMethodSignature(parent) || // { method(): "return" }
t2.isTSCallSignatureDeclaration(parent) || // { (): "return" }
t2.isTSConstructSignatureDeclaration(parent) || // { new(): "type" }
t2.isTSIndexSignature(parent) || // { [key: string]: "value" }
t2.isTSMappedType(parent) || // { [K in keyof T]: "value" }
t2.isTSConditionalType(parent) || // T extends "literal" ? A : B
t2.isTSInferType(parent) || // infer R
t2.isTSTypeParameter(parent) || // <T extends "literal">
t2.isTSTypeParameterDeclaration(parent)) {
if (t2.isTSEnumMember(parent) && parent.initializer === current.node) {
return {
shouldProcess: true,
contextType: "enum-value",
reason: "\u679A\u4E3E\u503C"
};
}
return {
shouldProcess: false,
contextType: "ts-definition",
reason: "TypeScript\u7C7B\u578B\u5B9A\u4E49"
};
}
if (t2.isTSUnionType(parent)) {
if (parent.types && parent.types.includes(current.node)) {
return {
shouldProcess: false,
contextType: "type-annotation",
reason: "\u8054\u5408\u7C7B\u578B\u6210\u5458"
};
}
if (t2.isTSLiteralType(current.parent) && parent.types.includes(current.parent)) {
return {
shouldProcess: false,
contextType: "type-annotation",
reason: "\u8054\u5408\u7C7B\u578B\u5B57\u9762\u91CF"
};
}
}
if (t2.isTSPropertySignature(parent)) {
if (parent.typeAnnotation && parent.typeAnnotation.typeAnnotation === current.node) {
return {
shouldProcess: false,
contextType: "type-annotation",
reason: "\u63A5\u53E3\u5C5E\u6027\u7C7B\u578B\u6CE8\u89E3"
};
}
if (t2.isTSTypeAnnotation(current.parent) && parent.typeAnnotation === current.parent) {
return {
shouldProcess: false,
contextType: "type-annotation",
reason: "\u63A5\u53E3\u5C5E\u6027\u7C7B\u578B\u6CE8\u89E3"
};
}
}
if (t2.isTSTypeAliasDeclaration(parent)) {
if (parent.typeAnnotation === current.node) {
return {
shouldProcess: false,
contextType: "ts-definition",
reason: "\u7C7B\u578B\u522B\u540D\u5B9A\u4E49"
};
}
}
if (t2.isTSTypeParameterInstantiation(parent)) {
return {
shouldProcess: false,
contextType: "type-annotation",
reason: "\u6CDB\u578B\u53C2\u6570"
};
}
if (t2.isClassProperty(parent) && parent.key === current.node) {
return {
shouldProcess: false,
contextType: "object-key",
reason: "\u7C7B\u5C5E\u6027\u540D"
};
}
if (t2.isMemberExpression(parent) && parent.property === current.node && !parent.computed) {
return {
shouldProcess: false,
contextType: "object-key",
reason: "\u6210\u5458\u8868\u8FBE\u5F0F\u5C5E\u6027\u540D"
};
}
if (t2.isFunctionDeclaration(parent) || t2.isFunctionExpression(parent) || t2.isArrowFunctionExpression(parent)) {
if (parent.params) {
for (const param of parent.params) {
if (t2.isIdentifier(param) && param.typeAnnotation) {
const typeNode = param.typeAnnotation.typeAnnotation;
if (isNodeInTypeAnnotation(current.node, typeNode)) {
return {
shouldProcess: false,
contextType: "type-annotation",
reason: "\u51FD\u6570\u53C2\u6570\u7C7B\u578B\u6CE8\u89E3"
};
}
}
}
}
}
current = parentPath;
}
return {
shouldProcess: true,
contextType: "code",
reason: "\u4EE3\u7801\u4E2D\u7684\u5B57\u7B26\u4E32"
};
}
function isInTypePosition(path8) {
return !analyzeContext(path8).shouldProcess;
}
function isNodeInTypeAnnotation(targetNode, typeAnnotation) {
if (!typeAnnotation) return false;
if (typeAnnotation === targetNode) return true;
if (t2.isTSUnionType(typeAnnotation)) {
return typeAnnotation.types.some((type) => isNodeInTypeAnnotation(targetNode, type));
}
if (t2.isTSLiteralType(typeAnnotation)) {
return typeAnnotation.literal === targetNode;
}
return false;
}
function containsChinese(str) {
return CHINESE_RE.test(str);
}
function createI18nCallExpression(functionName, key, quoteType) {
const keyNode = t2.stringLiteral(key);
if (quoteType === "double" && keyNode.extra) {
keyNode.extra = { ...keyNode.extra, raw: `"${key}"`, rawValue: key };
}
return t2.callExpression(t2.identifier(functionName), [keyNode]);
}
function isI18nFunctionCall(path8, functionName) {
const node = path8.node;
if (!t2.isCallExpression(node)) {
return false;
}
if (!t2.isIdentifier(node.callee) || node.callee.name !== functionName) {
return false;
}
if (node.arguments.length !== 1) {
return false;
}
if (!t2.isStringLiteral(node.arguments[0])) {
return false;
}
return true;
}
function handleStringLiteral(path8, functionName, quoteType) {
if (isInTypePosition(path8)) {
return false;
}
const parentPath = path8.parentPath;
if (parentPath && isI18nFunctionCall(parentPath, functionName)) {
Logger.verbose(`\u8DF3\u8FC7\u5DF2\u7ECF\u56FD\u9645\u5316\u7684\u51FD\u6570\u8C03\u7528: ${functionName}(${path8.node.value})`);
return false;
}
const stringValue = path8.node.value;
if (stringValue && typeof stringValue === "string" && containsChinese(stringValue)) {
const key = createI18nKey(stringValue);
const callExpression2 = createI18nCallExpression(functionName, key, quoteType);
path8.replaceWith(callExpression2);
return true;
}
return false;
}
async function checkUnwrappedChinese() {
var _a;
const config = ConfigManager.get();
const functionName = ((_a = config.replacement) == null ? void 0 : _a.functionName) ?? "$t";
Logger.info("\u5F00\u59CB\u68C0\u67E5\u672A\u56FD\u9645\u5316\u7684\u4E2D\u6587\u5B57\u7B26\u4E32...", "normal");
Logger.verbose(`\u914D\u7F6E\u4FE1\u606F:
- include: ${JSON.stringify(config.include)}
- exclude: ${JSON.stringify(config.exclude)}
- tempDir: ${config.tempDir || "\u65E0"}
- functionName: ${functionName}`);
Logger.info("\u641C\u7D22\u76EE\u6807\u6587\u4EF6...", "verbose");
let files;
let sourceDescription;
if (config.tempDir) {
try {
const tempFiles = await findTargetFiles([`${config.tempDir}/**/*.{js,jsx,ts,tsx}`], config.exclude);
if (tempFiles.length > 0) {
files = tempFiles;
sourceDescription = `\u4E34\u65F6\u76EE\u5F55 (${config.tempDir})`;
Logger.info(`\u4F7F\u7528\u4E34\u65F6\u76EE\u5F55\u6587\u4EF6\u8FDB\u884C\u68C0\u67E5: ${config.tempDir}`, "verbose");
} else {
files = await findTargetFiles(config.include, config.exclude);
sourceDescription = "\u539F\u59CB\u6587\u4EF6";
Logger.info("\u4E34\u65F6\u76EE\u5F55\u65E0\u6587\u4EF6\uFF0C\u4F7F\u7528\u539F\u59CB\u6587\u4EF6\u8FDB\u884C\u68C0\u67E5", "verbose");
}
} catch (error) {
files = await findTargetFiles(config.include, config.exclude);
sourceDescription = "\u539F\u59CB\u6587\u4EF6";
Logger.verbose(`\u4E34\u65F6\u76EE\u5F55\u8BBF\u95EE\u5931\u8D25: ${error}, \u4F7F\u7528\u539F\u59CB\u6587\u4EF6\u8FDB\u884C\u68C0\u67E5`);
}
} else {
files = await findTargetFiles(config.include, config.exclude);
sourceDescription = "\u539F\u59CB\u6587\u4EF6";
}
if (files.length === 0) {
Logger.warn(`\u5728${sourceDescription}\u4E2D\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6`, "normal");
return [];
}
Logger.info(`\u627E\u5230 ${files.length} \u4E2A\u6587\u4EF6\u9700\u8981\u68C0\u67E5 (\u6765\u6E90: ${sourceDescription})`, "normal");
const results = [];
let processedCount = 0;
let totalIssues = 0;
for (const file of files) {
processedCount++;
Logger.info(`[${processedCount}/${files.length}] \u68C0\u67E5\u6587\u4EF6: ${file}`, "verbose");
const code = await readFile2(file, "utf-8");
const fileResult = { file, issues: [] };
let ast;
try {
ast = (0, import_parser2.parse)(code, {
sourceType: "unambiguous",
plugins: ["jsx", "typescript", "classProperties", "decorators-legacy", "dynamicImport"]
});
} catch (error) {
Logger.warn(`\u89E3\u6790\u6587\u4EF6\u5931\u8D25: ${file}`, "minimal");
Logger.verbose(`\u9519\u8BEF\u8BE6\u60C5: ${error}`);
continue;
}
const codeLines = code.split("\n");
traverse(ast, {
// 检查字符串字面量
StringLiteral(path8) {
const contextAnalysis = analyzeContext(path8, codeLines);
const parentPath = path8.parentPath;
if (parentPath && isI18nFunctionCall(parentPath, functionName)) return;
const stringValue = path8.node.value;
if (stringValue && typeof stringValue === "string" && containsChinese(stringValue)) {
const loc = path8.node.loc;
if (loc) {
fileResult.issues.push({
line: loc.start.line,
column: loc.start.column,
text: stringValue,
type: "string",
context: getContextInfo(path8, codeLines),
shouldProcess: contextAnalysis.shouldProcess,
contextType: contextAnalysis.contextType,
reason: contextAnalysis.reason
});
}
}
},
// 检查模板字符串
TemplateLiteral(path8) {
const contextAnalysis = analyzeContext(path8, codeLines);
const parentPath = path8.parentPath;
if (parentPath && isI18nFunctionCall(parentPath, functionName)) return;
path8.node.quasis.forEach((quasi) => {
var _a2;
const raw = (_a2 = quasi.value) == null ? void 0 : _a2.raw;
if (raw && typeof raw === "string" && containsChinese(raw)) {
const loc = path8.node.loc;
if (loc) {
fileResult.issues.push({
line: loc.start.line,
column: loc.start.column,
text: raw,
type: "template",
context: getContextInfo(path8, codeLines),
shouldProcess: contextAnalysis.shouldProcess,
contextType: contextAnalysis.contextType,
reason: contextAnalysis.reason
});
}
}
});
path8.node.expressions.forEach((expr) => {
if (t2.isStringLiteral(expr) && containsChinese(expr.value)) {
const loc = expr.loc;
if (loc) {
fileResult.issues.push({
line: loc.start.line,
column: loc.start.column,
text: expr.value,
type: "template",
context: getContextInfo(path8, codeLines),
shouldProcess: contextAnalysis.shouldProcess,
contextType: contextAnalysis.contextType,
reason: contextAnalysis.reason
});
}
}
});
},
// 检查JSX文本
JSXText(path8) {
const textValue = path8.node.value;
if (textValue && typeof textValue === "string" && containsChinese(textValue)) {
const contextAnalysis = analyzeContext(path8, codeLines);
const parentPath = path8.parentPath;
if ((parentPath == null ? void 0 : parentPath.isJSXExpressionContainer()) && parentPath.node.expression && isI18nFunctionCall({ node: parentPath.node.expression }, functionName)) {
return;
}
const trimmedValue = textValue.trim();
if (trimmedValue) {
const loc = path8.node.loc;
if (loc) {
fileResult.issues.push({
line: loc.start.line,
column: loc.start.column,
text: trimmedValue,
type: "jsx-text",
context: getContextInfo(path8, codeLines),
shouldProcess: contextAnalysis.shouldProcess,
contextType: contextAnalysis.contextType,
reason: contextAnalysis.reason
});
}
}
}
},
// 检查JSX属性
JSXAttribute(path8) {
if (t2.isStringLiteral(path8.node.value) && path8.node.value.value && typeof path8.node.value.value === "string" && containsChinese(path8.node.value.value)) {
const contextAnalysis = analyzeContext(path8, codeLines);
if (t2.isJSXExpressionContainer(path8.node.value) && path8.node.value.expression && isI18nFunctionCall({ node: path8.node.value.expression }, functionName)) {
return;
}
const attributeValue = path8.node.value.value;
const loc = path8.node.loc;
if (loc) {
fileResult.issues.push({
line: loc.start.line,
column: loc.start.column,
text: attributeValue,
type: "jsx-attribute",
context: getContextInfo(path8, codeLines),
shouldProcess: contextAnalysis.shouldProcess,
contextType: contextAnalysis.contextType,
reason: contextAnalysis.reason
});
}
}
}
});
if (fileResult.issues.length > 0) {
results.push(fileResult);
totalIssues += fileResult.issues.length;
Logger.info(`\u6587\u4EF6 ${file} \u53D1\u73B0 ${fileResult.issues.length} \u4E2A\u672A\u56FD\u9645\u5316\u7684\u4E2D\u6587\u5B57\u7B26\u4E32`, "normal");
} else {
Logger.verbose(`\u6587\u4EF6 ${file} \u65E0\u672A\u56FD\u9645\u5316\u7684\u4E2D\u6587\u5B57\u7B26\u4E32`);
}
}
Logger.success(`\u68C0\u67E5\u5B8C\u6210\uFF01`, "minimal");
Logger.info(`\u7EDF\u8BA1\u4FE1\u606F:`, "normal");
Logger.info(` - \u68C0\u67E5\u6587\u4EF6\u603B\u6570: ${processedCount}`, "normal");
Logger.info(` - \u6709\u95EE\u9898\u7684\u6587\u4EF6\u6570: ${results.length}`, "normal");
Logger.info(` - \u672A\u56FD\u9645\u5316\u5B57\u7B26\u4E32\u603B\u6570: ${totalIssues}`, "normal");
return results;
}
function getContextInfo(path8, codeLines) {
var _a;
const loc = path8.node.loc;
if (!loc || !codeLines) return "";
const lineIndex = loc.start.line - 1;
if (lineIndex >= 0 && lineIndex < codeLines.length) {
return ((_a = codeLines[lineIndex]) == null ? void 0 : _a.trim()) ?? "";
}
return "";
}
async function scanAndReplaceAll() {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const config = ConfigManager.get();
Logger.info("\u5F00\u59CB\u626B\u63CF\u548C\u66FF\u6362\u4E2D\u6587\u5B57\u7B26\u4E32...", "normal");
Logger.verbose(`\u914D\u7F6E\u4FE1\u606F:
- include: ${JSON.stringify(config.include)}
- exclude: ${JSON.stringify(config.exclude)}
- outputDir: ${config.outputDir}
- locale: ${config.locale}
- tempDir: ${config.tempDir || "\u65E0"}`);
Logger.info("\u521D\u59CB\u5316i18n\u7F13\u5B58...", "verbose");
await initI18nCache();
Logger.info("\u641C\u7D22\u76EE\u6807\u6587\u4EF6...", "verbose");
const files = await findTargetFiles(config.include, config.exclude);
if (files.length === 0) {
Logger.warn("\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6", "normal");
return;
}
Logger.info(`\u627E\u5230 ${files.length} \u4E2A\u6587\u4EF6\u9700\u8981\u5904\u7406`, "normal");
Logger.verbose(`\u6587\u4EF6\u5217\u8868: ${files.join(", ")}`);
const functionName = ((_a = config.replacement) == null ? void 0 : _a.functionName) ?? "$t";
const quoteType = ((_b = config.replacement) == null ? void 0 : _b.quoteType) ?? "single";
const autoImportEnabled = ((_d = (_c = config.replacement) == null ? void 0 : _c.autoImport) == null ? void 0 : _d.enabled) ?? false;
const insertPosition = ((_f = (_e = config.replacement) == null ? void 0 : _e.autoImport) == null ? void 0 : _f.insertPosition) ?? "afterImports";
const imports = ((_h = (_g = config.replacement) == null ? void 0 : _g.autoImport) == null ? void 0 : _h.imports) ?? {};
const templateConfig = ((_i = config.replacement) == null ? void 0 : _i.templateString) ?? {
enabled: true,
preserveExpressions: true,
splitStrategy: "smart"
};
Logger.verbose(`\u66FF\u6362\u914D\u7F6E:
- \u51FD\u6570\u540D: ${functionName}
- \u5F15\u53F7\u7C7B\u578B: ${quoteType}
- \u81EA\u52A8\u5BFC\u5165: ${autoImportEnabled ? "\u542F\u7528" : "\u7981\u7528"}
- \u63D2\u5165\u4F4D\u7F6E: ${insertPosition}
- \u6A21\u677F\u5B57\u7B26\u4E32\u5904\u7406: ${templateConfig.enabled ? "\u542F\u7528" : "\u7981\u7528"}
- \u62C6\u5206\u7B56\u7565: ${templateConfig.splitStrategy}`);
let processedCount = 0;
let modifiedCount = 0;
let totalReplacements = 0;
const importManager = new ImportManager();
for (const file of files) {
processedCount++;
Logger.info(`[${processedCount}/${files.length}] \u5904\u7406\u6587\u4EF6: ${file}`, "verbose");
Logger.verbose(`\u6B63\u5728\u5904\u7406\u6587\u4EF6: ${file}`);
const code = await readFile2(file, "utf-8");
let ast;
try {
ast = (0, import_parser2.parse)(code, {
sourceType: "unambiguous",
plugins: ["jsx", "typescript", "classProperties", "decorators-legacy", "dynamicImport"]
});
} catch (error) {
Logger.warn(`\u89E3\u6790\u6587\u4EF6\u5931\u8D25: ${file}`, "minimal");
Logger.verbose(`\u9519\u8BEF\u8BE6\u60C5: ${error}`);
continue;
}
importManager.reset();
let hasReplacement = false;
let fileReplacements = 0;
traverse(ast, {
// 处理普通字符串字面量
StringLiteral(path8) {
if (handleStringLiteral(path8, functionName, quoteType)) {
hasReplacement = true;
fileReplacements++;
const stringValue = path8.node.value;
Logger.verbose(
`\u66FF\u6362\u5B57\u7B26\u4E32: "${stringValue}" -> ${functionName}(${quoteType === "single" ? "'" : '"'}${stringValue}${quoteType === "single" ? "'" : '"'})`
);
}
},
// 处理模板字符串元素
TemplateElement(path8) {
var _a2;
if (!templateConfig.enabled) {
return;
}
if (isInTypePosition(path8)) {
return;
}
const parentPath = path8.parentPath;
if ((parentPath == null ? void 0 : parentPath.parentPath) && isI18nFunctionCall(parentPath.parentPath, functionName)) {
Logger.verbose(`\u8DF3\u8FC7\u5DF2\u7ECF\u56FD\u9645\u5316\u7684\u6A21\u677F\u5B57\u7B26\u4E32\u5143\u7D20`);
return;
}
const raw = (_a2 = path8.node.value) == null ? void 0 : _a2.raw;
if (raw && typeof raw === "string" && containsChinese(raw)) {
const chineseMatches = raw.match(/[\u4e00-\u9fa5]+/g);
if (chineseMatches && chineseMatches.length > 0) {
if (templateConfig.splitStrategy === "conservative") {
const result = raw.replace(/[\u4e00-\u9fa5]+/g, (match) => {
const key = createI18nKey(match);
const quoteChar = quoteType === "single" ? "'" : '"';
return "${" + functionName + "(" + quoteChar + key + quoteChar + ")}";
});
path8.node.value.raw = result;
path8.node.value.cooked = result;
hasReplacement = true;
fileReplacements++;
Logger.verbose(`\u4FDD\u5B88\u7B56\u7565\u66FF\u6362\u6A21\u677F\u5B57\u7B26\u4E32\u4E2D\u7684\u4E2D\u6587\u7247\u6BB5`);
} else {
const SEPARATOR = "\u{1F4D6}";
const replaced = raw.replace(
/[\u4e00-\u9fa5]+/g,
(match) => `${SEPARATOR}${match}${SEPARATOR}`
);
const parts = replaced.split(new RegExp(`${SEPARATOR}([\\u4e00-\\u9fa5]+)${SEPARATOR}`, "g")).filter(Boolean);
let result = "";
const quoteChar = quoteType === "single" ? "'" : '"';
for (const part of parts) {
if (/^[\u4e00-\u9fa5]+$/.test(part)) {
const key = createI18nKey(part);
result += "${" + functionName + "(" + quoteChar + key + quoteChar + ")}";
} else {
result += part;
}
}
path8.node.value.raw = result;
path8.node.value.cooked = result;
hasReplacement = true;
fileReplacements++;
Logger.verbose(`${templateConfig.splitStrategy}\u7B56\u7565\u66FF\u6362\u6A21\u677F\u5B57\u7B26\u4E32\u4E2D\u7684\u4E2D\u6587\u7247\u6BB5`);
}
}
}
},
// 处理模板字符串
TemplateLiteral(path8) {
const parentPath = path8.parentPath;
if (parentPath && isI18nFunctionCall(parentPath, functionName)) {
Logger.verbose(`\u8DF3\u8FC7\u5DF2\u7ECF\u56FD\u9645\u5316\u7684\u6A21\u677F\u5B57\u7B26\u4E32`);
return;
}
if (isInTypePosition(path8)) {
return;
}
if (!templateConfig.enabled) {
return;
}
let hasModified = false