build-en-locale.cjs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /**
  2. * 根据 zh.json + 旧项目 common/lang 生成 en.json
  3. * 用法: node scripts/build-en-locale.cjs
  4. */
  5. const fs = require('fs');
  6. const root = 'd:/project/2026项目/DeepAICloud';
  7. const zhPath = `${root}/src/locales/zh.json`;
  8. const enPath = `${root}/src/locales/en.json`;
  9. const oldEnPath = 'd:/project/2021项目/四维看看官网/webSite/common/lang/en.json';
  10. const oldZhPath = 'd:/project/2021项目/四维看看官网/webSite/common/lang/zh.json';
  11. const zh = JSON.parse(fs.readFileSync(zhPath, 'utf8'));
  12. const oldEn = JSON.parse(fs.readFileSync(oldEnPath, 'utf8'));
  13. const oldZh = JSON.parse(fs.readFileSync(oldZhPath, 'utf8'));
  14. const oldPcEn = oldEn.pc || {};
  15. const oldPcZh = oldZh.pc || {};
  16. function buildTextMap(zhObj, enObj, map = new Map()) {
  17. if (typeof zhObj === 'string' && typeof enObj === 'string' && zhObj.trim()) {
  18. map.set(zhObj, enObj);
  19. return map;
  20. }
  21. if (zhObj && enObj && typeof zhObj === 'object' && typeof enObj === 'object' && !Array.isArray(zhObj)) {
  22. for (const key of Object.keys(zhObj)) {
  23. if (enObj[key] !== undefined) buildTextMap(zhObj[key], enObj[key], map);
  24. }
  25. }
  26. return map;
  27. }
  28. const zhToEn = buildTextMap(oldPcZh, oldPcEn);
  29. const zhToEnManual = require('./en-translations-manual.cjs');
  30. const manual = {
  31. ...zhToEnManual,
  32. '欢迎使用深智云': 'Welcome to DeepAI Cloud',
  33. '语言': 'Language',
  34. '首页': 'Home',
  35. '设置': 'Settings',
  36. '操作成功': 'Operation successful',
  37. '失败': 'Failed',
  38. '加载中...': 'Loading...',
  39. '付款成功后5个工作日内发货,默认顺丰快递包邮':
  40. 'Ships within 5 business days after payment. SF Express shipping included.',
  41. '立即购买': 'Buy Now',
  42. '联系客服': 'Contact Support',
  43. '限时免费': 'Limited-time free',
  44. '咨询客服': 'Contact support',
  45. '¥{price}': '¥{price}',
  46. '您的购物车空空如也': 'Your cart is empty',
  47. '赶紧行动吧': 'Start shopping now',
  48. '四维时代商城': '4Dage Mall',
  49. '充分整合硬件、软件、增值服务,打造四维空间智能生态':
  50. 'Integrating hardware, software, and value-added services to build an intelligent 4D spatial ecosystem',
  51. '销售会员': 'Membership',
  52. '耗材配件': 'Consumables & Accessories',
  53. '增值服务': 'Value-added Services',
  54. '相机配件': 'Camera Accessories',
  55. '颜色': 'Color',
  56. '配件': 'Accessories',
  57. '简介': 'Introduction',
  58. '联系我们': 'Contact Us',
  59. '我的账户': 'My Account',
  60. '退出登录': 'Log Out',
  61. '常见问题': 'FAQ',
  62. '元/年': 'CNY/year',
  63. '云容量': 'Cloud Storage',
  64. '品牌信息': 'Brand Info',
  65. '优先计算': 'Priority Computing',
  66. '离线包下载': 'Offline Package Download',
  67. '立即体验': 'Try Now',
  68. '激光+全景': 'LiDAR + Panorama',
  69. '全景': 'Panorama',
  70. '激光': 'LiDAR',
  71. '免费10次': '10 free downloads',
  72. '{price}元/次': '{price} CNY/time',
  73. '我已支付': 'I have paid',
  74. '请在新窗口完成支付,完成后可点击下方按钮确认':
  75. 'Please complete payment in the new window, then click the button below to confirm.',
  76. '重新打开支付页': 'Reopen payment page',
  77. '同意《四维看看销售协议》': 'I agree to the 4DKanKan Sales Agreement',
  78. };
  79. function translateString(s) {
  80. if (typeof s !== 'string') return s;
  81. if (manual[s]) return manual[s];
  82. if (zhToEn.has(s)) return zhToEn.get(s);
  83. return s;
  84. }
  85. function deepTranslate(obj) {
  86. if (typeof obj === 'string') return translateString(obj);
  87. if (Array.isArray(obj)) return obj.map(deepTranslate);
  88. if (obj && typeof obj === 'object') {
  89. const out = {};
  90. for (const [k, v] of Object.entries(obj)) out[k] = deepTranslate(v);
  91. return out;
  92. }
  93. return obj;
  94. }
  95. function overlayByKey(zhPart, enPart, refPart) {
  96. if (!zhPart || !enPart || !refPart) return;
  97. for (const key of Object.keys(zhPart)) {
  98. if (refPart[key] === undefined) continue;
  99. if (typeof zhPart[key] === 'string' && typeof refPart[key] === 'string') {
  100. enPart[key] = refPart[key];
  101. } else if (typeof zhPart[key] === 'object' && zhPart[key] !== null && !Array.isArray(zhPart[key])) {
  102. if (!enPart[key] || typeof enPart[key] !== 'object') enPart[key] = {};
  103. overlayByKey(zhPart[key], enPart[key], refPart[key]);
  104. }
  105. }
  106. }
  107. const en = deepTranslate(JSON.parse(JSON.stringify(zh)));
  108. for (const key of Object.keys(zh)) {
  109. if (typeof zh[key] === 'object' && oldPcEn[key]) {
  110. overlayByKey(zh[key], en[key], oldPcEn[key]);
  111. }
  112. }
  113. if (oldPcEn.manage && zh.scene) {
  114. overlayByKey(zh.scene, en.scene, {
  115. sceneAdmin: oldPcEn.manage.sceneAdmin,
  116. myScenes: oldPcEn.manage.sceneAdmin,
  117. sceneDownload: oldPcEn.manage.sceneDownload,
  118. });
  119. }
  120. if (oldPcEn.manage && zh.deviceAdmin) {
  121. overlayByKey(zh.deviceAdmin, en.deviceAdmin, oldPcEn.manage.deviceAdmin || {});
  122. }
  123. if (oldPcEn.manage && zh.member) {
  124. overlayByKey(zh.member, en.member, oldPcEn.manage.member || oldPcEn.member || {});
  125. }
  126. if (en.login && en.login.agreeXieyi) {
  127. en.toast = en.toast || {};
  128. en.toast.agreeXieyi = en.login.agreeXieyi;
  129. }
  130. fs.writeFileSync(enPath, `${JSON.stringify(en, null, 2)}\n`);
  131. const untranslated = [];
  132. function countUntranslated(zhPart, enPart, path = '') {
  133. if (typeof zhPart === 'string' && typeof enPart === 'string') {
  134. if (zhPart === enPart && /[\u4e00-\u9fff]/.test(zhPart)) {
  135. untranslated.push(path);
  136. }
  137. return;
  138. }
  139. if (zhPart && enPart && typeof zhPart === 'object' && !Array.isArray(zhPart)) {
  140. for (const key of Object.keys(zhPart)) {
  141. countUntranslated(zhPart[key], enPart[key], path ? `${path}.${key}` : key);
  142. }
  143. }
  144. }
  145. countUntranslated(zh, en);
  146. console.log(`en.json generated. Untranslated strings: ${untranslated.length}`);
  147. if (untranslated.length > 0 && untranslated.length <= 20) {
  148. console.log(untranslated.join('\n'));
  149. }