UNPKG

i18n-xy

Version:

自动提取React项目中的中文字符串并进行国际化的CLI工具

475 lines (466 loc) 15.8 kB
"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 __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; 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 )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // main/index.ts var index_exports = {}; __export(index_exports, { ConfigManager: () => ConfigManager, ConfigValidator: () => ConfigValidator, Logger: () => Logger, ensureDir: () => ensureDir2, fileExists: () => fileExists, findFiles: () => findFiles, findTargetFiles: () => findTargetFiles, loadConfig: () => loadConfig, readFile: () => readFile2, readJson: () => readJson, readJsonSync: () => readJsonSync2, withCLI: () => withCLI, writeFile: () => writeFile, writeFileWithTempDir: () => writeFileWithTempDir, writeJson: () => writeJson }); module.exports = __toCommonJS(index_exports); // 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/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 findFiles(pattern, options = {}) { return (0, import_fast_glob.default)(pattern, { dot: true, ...options, cwd: process.cwd() }); } 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/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/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/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); } }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ConfigManager, ConfigValidator, Logger, ensureDir, fileExists, findFiles, findTargetFiles, loadConfig, readFile, readJson, readJsonSync, withCLI, writeFile, writeFileWithTempDir, writeJson }); //# sourceMappingURL=index.cjs.map