extract-i18n-json.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * 多语言文件 JS 转 JSON 工具
  3. *
  4. * 功能:将 src/assets/i18n/messages/*.js 文件转换为纯 JSON 格式
  5. * 目的:移除 JavaScript 语法(export default 和结尾的 ;),方便用于其他用途
  6. *
  7. * 使用方法:
  8. * node scripts/extract-i18n-json.js
  9. *
  10. * 输出:
  11. * scripts/output/i18n/zh.json - 中文翻译(纯JSON格式)
  12. * scripts/output/i18n/en.json - 英文翻译(纯JSON格式)
  13. */
  14. const fs = require('fs');
  15. const path = require('path');
  16. const { execSync } = require('child_process');
  17. const readline = require('readline');
  18. // 配置
  19. const CONFIG = {
  20. inputDir: path.join(__dirname, '../src/assets/i18n/messages'),
  21. outputDir: path.join(__dirname, 'output/i18n'),
  22. files: ['zh.js', 'en.js'],
  23. scpHost: '192.168.110.245',
  24. scpUser: 'root',
  25. scpZhPath: '/root/gydr/trans/zh_CN/zh.js',
  26. scpEnPath: '/root/gydr/trans/en_US/en.js'
  27. };
  28. /**
  29. * 从 JS 文件提取 JSON 对象
  30. * @param {string} filePath - JS 文件路径
  31. * @returns {object} - 解析后的 JSON 对象
  32. */
  33. function extractJsonFromJs(filePath) {
  34. try {
  35. // 读取文件内容
  36. const content = fs.readFileSync(filePath, 'utf-8');
  37. // 移除 export default 语句和结尾的分号
  38. let jsonString = content
  39. .replace(/export\s+default\s+/g, '') // 移除 export default
  40. .trim(); // 去除首尾空白
  41. // 移除末尾的分号(如果存在)
  42. if (jsonString.endsWith(';')) {
  43. jsonString = jsonString.slice(0, -1);
  44. }
  45. // 使用 eval 解析对象(在受控环境中使用)
  46. // 注意:这里假设文件内容是可信的
  47. const jsonObj = eval('(' + jsonString + ')');
  48. return jsonObj;
  49. } catch (error) {
  50. console.error(`❌ 解析文件失败: ${filePath}`);
  51. console.error(error.message);
  52. throw error;
  53. }
  54. }
  55. /**
  56. * 确保目录存在
  57. * @param {string} dirPath - 目录路径
  58. */
  59. function ensureDirectoryExists(dirPath) {
  60. if (!fs.existsSync(dirPath)) {
  61. fs.mkdirSync(dirPath, { recursive: true });
  62. console.log(`📁 创建目录: ${dirPath}`);
  63. }
  64. }
  65. /**
  66. * 询问用户SCP配置
  67. * @returns {Promise<Object>} - SCP配置对象
  68. */
  69. function promptScpConfig() {
  70. return new Promise((resolve) => {
  71. const rl = readline.createInterface({
  72. input: process.stdin,
  73. output: process.stdout
  74. });
  75. const config = {
  76. host: CONFIG.scpHost,
  77. user: CONFIG.scpUser,
  78. zhPath: CONFIG.scpZhPath,
  79. enPath: CONFIG.scpEnPath
  80. };
  81. console.log('\n📤 SCP配置 (按Enter使用默认值):');
  82. rl.question(`IP地址 (默认: ${CONFIG.scpHost}): `, (host) => {
  83. config.host = host || CONFIG.scpHost;
  84. rl.question(`用户名 (默认: ${CONFIG.scpUser}): `, (user) => {
  85. config.user = user || CONFIG.scpUser;
  86. rl.question(`中文文件路径 (默认: ${CONFIG.scpZhPath}): `, (zhPath) => {
  87. config.zhPath = zhPath || CONFIG.scpZhPath;
  88. rl.question(`英文文件路径 (默认: ${CONFIG.scpEnPath}): `, (enPath) => {
  89. config.enPath = enPath || CONFIG.scpEnPath;
  90. rl.close();
  91. resolve(config);
  92. });
  93. });
  94. });
  95. });
  96. });
  97. }
  98. /**
  99. * 通过 SCP 将文件上传到远程服务器
  100. * @param {string} localPath - 本地文件路径
  101. * @param {string} remotePath - 远程文件路径
  102. * @param {Object} scpConfig - SCP配置
  103. */
  104. function scpToRemote(localPath, remotePath, scpConfig) {
  105. try {
  106. const command = `scp ${localPath} ${scpConfig.user}@${scpConfig.host}:${remotePath}`;
  107. execSync(command, { stdio: 'inherit' });
  108. console.log(`📤 SCP 成功: ${localPath} -> ${remotePath}`);
  109. } catch (error) {
  110. console.error(`❌ SCP 失败: ${localPath} -> ${remotePath}`);
  111. console.error(error.message);
  112. }
  113. }
  114. /**
  115. * 主函数
  116. */
  117. async function main() {
  118. console.log('🚀 开始提取多语言 JSON...\n');
  119. // 确保输出目录存在
  120. ensureDirectoryExists(CONFIG.outputDir);
  121. let successCount = 0;
  122. let failCount = 0;
  123. // 处理每个文件
  124. CONFIG.files.forEach(filename => {
  125. try {
  126. const inputPath = path.join(CONFIG.inputDir, filename);
  127. const outputFilename = filename; // 保持 .js 扩展名
  128. const outputPath = path.join(CONFIG.outputDir, outputFilename);
  129. console.log(`📄 处理: ${filename}`);
  130. // 检查输入文件是否存在
  131. if (!fs.existsSync(inputPath)) {
  132. console.warn(`⚠️ 跳过: 文件不存在 ${inputPath}\n`);
  133. failCount++;
  134. return;
  135. }
  136. // 提取 JSON
  137. const jsonObj = extractJsonFromJs(inputPath);
  138. // 写入 JSON 文件(格式化输出,缩进2个空格)
  139. fs.writeFileSync(
  140. outputPath,
  141. JSON.stringify(jsonObj, null, 2),
  142. 'utf-8'
  143. );
  144. console.log(`✅ 成功: ${outputFilename} (${Object.keys(jsonObj).length} 条翻译)`);
  145. console.log(` 输出: ${outputPath}\n`);
  146. successCount++;
  147. } catch (error) {
  148. console.error(`❌ 失败: ${filename}\n`);
  149. failCount++;
  150. }
  151. });
  152. // 汇总
  153. console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
  154. console.log(`✨ 完成! 成功: ${successCount}, 失败: ${failCount}`);
  155. console.log(`📂 输出目录: ${CONFIG.outputDir}`);
  156. // 通过 SCP 上传到远程服务器
  157. if (successCount > 0) {
  158. const scpConfig = await promptScpConfig();
  159. console.log('\n📤 开始上传到远程服务器...\n');
  160. const zhOutputPath = path.join(CONFIG.outputDir, 'zh.js');
  161. const enOutputPath = path.join(CONFIG.outputDir, 'en.js');
  162. scpToRemote(zhOutputPath, scpConfig.zhPath, scpConfig);
  163. scpToRemote(enOutputPath, scpConfig.enPath, scpConfig);
  164. console.log('\n📤 远程上传完成!\n');
  165. }
  166. }
  167. // 执行
  168. main();