xshell 0.0.54 → 0.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/chalk.browser.d.ts +1 -1
  2. package/chalk.browser.js +3 -19
  3. package/chalk.browser.js.map +1 -1
  4. package/extension.cjs +55780 -0
  5. package/extension.cjs.map +1 -0
  6. package/file.d.ts +3 -3
  7. package/file.js +74 -97
  8. package/file.js.map +1 -1
  9. package/i18n/dict.js +1 -5
  10. package/i18n/dict.js.map +1 -1
  11. package/i18n/i18n-scan.js +9 -12
  12. package/i18n/i18n-scan.js.map +1 -1
  13. package/i18n/index.js +11 -16
  14. package/i18n/index.js.map +1 -1
  15. package/i18n/rwdict.js +3 -7
  16. package/i18n/rwdict.js.map +1 -1
  17. package/i18n/scanner/checker.js +6 -11
  18. package/i18n/scanner/checker.js.map +1 -1
  19. package/i18n/scanner/index.js +39 -44
  20. package/i18n/scanner/index.js.map +1 -1
  21. package/i18n/scanner/parser.js +15 -19
  22. package/i18n/scanner/parser.js.map +1 -1
  23. package/i18n/utils.js +3 -7
  24. package/i18n/utils.js.map +1 -1
  25. package/index.js +5 -8
  26. package/index.js.map +1 -1
  27. package/net.browser.js +6 -13
  28. package/net.browser.js.map +1 -1
  29. package/net.d.ts +2 -2
  30. package/net.js +50 -63
  31. package/net.js.map +1 -1
  32. package/package.json +14 -14
  33. package/process.d.ts +3 -3
  34. package/process.js +18 -23
  35. package/process.js.map +1 -1
  36. package/prototype.browser.js +24 -32
  37. package/prototype.browser.js.map +1 -1
  38. package/prototype.d.ts +1 -1
  39. package/prototype.js +37 -46
  40. package/prototype.js.map +1 -1
  41. package/repl.js +66 -80
  42. package/repl.js.map +1 -1
  43. package/server.d.ts +3 -3
  44. package/server.js +32 -37
  45. package/server.js.map +1 -1
  46. package/toaster.browser.js +6 -9
  47. package/toaster.browser.js.map +1 -1
  48. package/toaster.sass +1 -1
  49. package/tsconfig.json +3 -3
  50. package/ufs.d.ts +1 -1
  51. package/ufs.js +5 -9
  52. package/ufs.js.map +1 -1
  53. package/utils.browser.js +4 -11
  54. package/utils.browser.js.map +1 -1
  55. package/utils.d.ts +3 -3
  56. package/utils.js +41 -62
  57. package/utils.js.map +1 -1
  58. package/xshell.js +2 -4
  59. package/xshell.js.map +1 -1
package/i18n/index.js CHANGED
@@ -1,17 +1,13 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.I18N = exports.LANGUAGES = void 0;
4
- const tslib_1 = require("tslib");
5
- const qs_1 = tslib_1.__importDefault(require("qs"));
6
- const js_cookie_1 = tslib_1.__importDefault(require("js-cookie"));
7
- const i18next_1 = tslib_1.__importDefault(require("i18next"));
8
- const dict_js_1 = require("./dict.js");
9
- exports.LANGUAGES = ['zh', 'en', 'ja', 'ko'];
1
+ import qs from 'qs';
2
+ import Cookies from 'js-cookie';
3
+ import { default as i18next } from 'i18next';
4
+ import { Dict, } from './dict.js';
5
+ export const LANGUAGES = ['zh', 'en', 'ja', 'ko'];
10
6
  /**
11
7
  提供翻译文本功能,在浏览器环境下自动解析当前语言、国际/国内用户
12
8
  @see https://github.com/ShenHongFei/xshell/tree/master/i18n
13
9
  */
14
- class I18N {
10
+ export class I18N {
15
11
  static LANGUAGE_REGEXP = /^(zh|en|ja|jp|ko)$/;
16
12
  /** (ISO 639-1 标准语言代码) 可能取 zh, en, ja, ko */
17
13
  language;
@@ -37,16 +33,16 @@ class I18N {
37
33
  */
38
34
  constructor(_dict, language) {
39
35
  const is_browser = typeof document !== 'undefined' && typeof window !== 'undefined';
40
- const dict = new dict_js_1.Dict(_dict);
36
+ const dict = new Dict(_dict);
41
37
  // --- if in bowser then detect language & intl
42
38
  if (is_browser) {
43
39
  const { search = '' } = document.location || {};
44
- const queries = qs_1.default.parse(search, { ignoreQueryPrefix: true });
40
+ const queries = qs.parse(search, { ignoreQueryPrefix: true });
45
41
  if (!language) {
46
42
  const lquery = queries.language; // 暂时不考虑是数组的情况
47
43
  const lwindow = window.language;
48
44
  const lbrowser = typeof navigator !== 'undefined' && navigator.language.slice(0, 2);
49
- language = (lquery || lwindow || js_cookie_1.default.get('language') || lbrowser || 'en');
45
+ language = (lquery || lwindow || Cookies.get('language') || lbrowser || 'en');
50
46
  }
51
47
  if (!I18N.LANGUAGE_REGEXP.test(language))
52
48
  language = 'en';
@@ -64,7 +60,7 @@ class I18N {
64
60
  :
65
61
  field || '';
66
62
  // --- init i18next
67
- this.i18next = i18next_1.default.createInstance();
63
+ this.i18next = i18next.createInstance();
68
64
  if (is_browser)
69
65
  try {
70
66
  // 在无 React 的浏览器环境下避免 react-i18next 中执行 React.createContext() 报错
@@ -114,7 +110,7 @@ class I18N {
114
110
  i18n.init(dict)
115
111
  */
116
112
  init(dict) {
117
- const resources = new dict_js_1.Dict(dict).to_resources();
113
+ const resources = new Dict(dict).to_resources();
118
114
  for (const language in resources)
119
115
  this.i18next.addResources(language, 'translation', resources[language].translation);
120
116
  }
@@ -124,5 +120,4 @@ class I18N {
124
120
  };
125
121
  }
126
122
  }
127
- exports.I18N = I18N;
128
123
  //# sourceMappingURL=index.js.map
package/i18n/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import qs from 'qs'\nimport Cookies from 'js-cookie'\nimport {\n default as i18next,\n type i18n as I18Next\n} from 'i18next'\nimport type { Trans } from 'react-i18next'\n\nimport {\n Dict,\n type _Dict,\n type Item,\n} from './dict.js'\n\n\nexport type Language = 'zh' | 'en' | 'ja' | 'ko'\n\nexport const LANGUAGES = ['zh', 'en', 'ja', 'ko'] as const\n\ndeclare global {\n interface Window {\n language: Language\n }\n}\n\n\n/**\n 提供翻译文本功能,在浏览器环境下自动解析当前语言、国际/国内用户\n @see https://github.com/ShenHongFei/xshell/tree/master/i18n\n*/\nexport class I18N {\n static LANGUAGE_REGEXP = /^(zh|en|ja|jp|ko)$/\n \n \n /** (ISO 639-1 标准语言代码) 可能取 zh, en, ja, ko */\n language: Language\n \n /** hostname shortcuts */\n hosts: Hosts\n \n /** url prefix shortcuts */\n roots: Roots\n \n /** 标记静态文本,以便扫描词条,并在运行时根据当前语言获取翻译 */\n t : (text: string, options?: { language?: Language, context?: string, count?: number, [key: string]: any }) => string\n \n /** render: 翻译配置字段 */\n r : (field: Item | undefined | null, language?: Language) => string\n \n i18next: I18Next\n \n /** react-i18next <Trans/> 组件 */\n Trans: typeof Trans = ({ children }) => children as any\n \n \n /** ```ts\n import dict from './dict.json' // { \"添加\": { \"en\": \"Add\", \"ja\": \"追加\", \"ko\": \"추가\" } }\n \n const i18n = new I18N(dict, 'zh') // 创建实例,传入词典 dict 并指定语言(NodeJS 环境),\n const i18n = new I18N(dict) // 创建实例,传入词典 dict 并自动判断当前语言(浏览器环境),\n const i18n = new I18N({ }) // 创建实例,传入空词典\n ```\n @see https://github.com/ShenHongFei/xshell/tree/master/i18n\n */\n constructor (_dict: _Dict, language?: Language) {\n const is_browser = typeof document !== 'undefined' && typeof window !== 'undefined'\n \n const dict = new Dict(_dict)\n \n // --- if in bowser then detect language & intl\n if (is_browser) {\n const { search = '' } = document.location || { }\n const queries = qs.parse(search, { ignoreQueryPrefix: true })\n \n if (!language) {\n const lquery = queries.language as string // 暂时不考虑是数组的情况\n const lwindow = window.language\n const lbrowser = typeof navigator !== 'undefined' && navigator.language.slice(0, 2)\n \n language = (lquery || lwindow || Cookies.get('language') || lbrowser || 'en') as Language\n }\n \n if (!I18N.LANGUAGE_REGEXP.test(language))\n language = 'en'\n \n // console.log(`language = ${language}`)\n }\n \n language ||= 'en'\n \n this.language = language\n \n this.t = (text, options) => {\n options = options || { }\n \n const language = options.language || this.language\n \n return this.i18next.t(text, { ...options, lng: language, defaultValue: text })\n }\n \n this.r = (field, language = this.language) => \n field ?\n field[language] || field.en || field.zh || field as any || ''\n :\n field || ''\n \n // --- init i18next\n this.i18next = i18next.createInstance()\n \n if (is_browser)\n try {\n // 在无 React 的浏览器环境下避免 react-i18next 中执行 React.createContext() 报错\n // const React = require('react') as typeof import('react')\n const { initReactI18next, Trans: I18NextTrans } = require('react-i18next') as typeof import('react-i18next')\n this.i18next.use(initReactI18next)\n const _i18next = this.i18next\n // 绑定 Trans 组件的 i18n 到 this.i18next, 解决多个 i18next 冲突的问题\n // react-i18next/context.js 中 i18n 实例只在模块级别维护,多次 this.i18next.use(initReactI18next) 会覆盖前面的 i18n,导致 Trans 无法翻译\n // https://github.com/i18next/react-i18next/issues/726\n this.Trans = function Trans ({ i18n = _i18next, ...others }) {\n // 简单转发,性能更好\n return I18NextTrans({ i18n, ...others })\n // return React.createElement(I18NextTrans, { i18n, ...others } as any, children)\n // return <I18NextTrans {...{ i18n, ...others } }>{children}</I18NextTrans>\n }\n } catch { }\n \n this.i18next.init({\n lng: this.language,\n // LOCAL\n // debug: true,\n debug: false,\n fallbackLng: {\n en: ['zh'],\n ja: ['en', 'zh'],\n ko: ['en', 'zh'],\n },\n // 禁用 : 和 . 作为 seperator\n keySeparator: false,\n nsSeparator: false,\n resources: dict.to_resources(),\n interpolation: {\n escapeValue: false\n },\n react: {\n transKeepBasicHtmlNodesFor: []\n },\n })\n \n \n if (typeof window !== 'undefined' && window && !('i18n' in window))\n (window as any).i18n = this\n }\n \n \n /** 加载词典文件 (需要将这两行单独放一个文件里,以保证在 import 其他文件之前执行) \n \n @example\n import dict from './dict.json' // { \"添加\": { \"en\": \"Add\", \"ja\": \"追加\", \"ko\": \"추가\" } }\n i18n.init(dict)\n */\n init (dict: _Dict) {\n const resources = new Dict(dict).to_resources()\n for (const language in resources)\n this.i18next.addResources(language, 'translation', resources[language].translation)\n }\n \n toJSON () {\n return {\n language: this.language,\n }\n }\n}\n\n\nexport interface Hosts {\n \n}\n\n\nexport interface Roots {\n \n}\n\nexport type { _Dict, Item }\n\nexport interface I18NBasic {\n intl: boolean\n language: Language\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import qs from 'qs'\nimport Cookies from 'js-cookie'\nimport {\n default as i18next,\n type i18n as I18Next\n} from 'i18next'\nimport type { Trans } from 'react-i18next'\n\nimport {\n Dict,\n type _Dict,\n type Item,\n} from './dict.js'\n\n\nexport type Language = 'zh' | 'en' | 'ja' | 'ko'\n\nexport const LANGUAGES = ['zh', 'en', 'ja', 'ko'] as const\n\ndeclare global {\n interface Window {\n language: Language\n }\n}\n\n\n/**\n 提供翻译文本功能,在浏览器环境下自动解析当前语言、国际/国内用户\n @see https://github.com/ShenHongFei/xshell/tree/master/i18n\n*/\nexport class I18N {\n static LANGUAGE_REGEXP = /^(zh|en|ja|jp|ko)$/\n \n \n /** (ISO 639-1 标准语言代码) 可能取 zh, en, ja, ko */\n language: Language\n \n /** hostname shortcuts */\n hosts: Hosts\n \n /** url prefix shortcuts */\n roots: Roots\n \n /** 标记静态文本,以便扫描词条,并在运行时根据当前语言获取翻译 */\n t : (text: string, options?: { language?: Language, context?: string, count?: number, [key: string]: any }) => string\n \n /** render: 翻译配置字段 */\n r : (field: Item | undefined | null, language?: Language) => string\n \n i18next: I18Next\n \n /** react-i18next <Trans/> 组件 */\n Trans: typeof Trans = ({ children }) => children as any\n \n \n /** ```ts\n import dict from './dict.json' // { \"添加\": { \"en\": \"Add\", \"ja\": \"追加\", \"ko\": \"추가\" } }\n \n const i18n = new I18N(dict, 'zh') // 创建实例,传入词典 dict 并指定语言(NodeJS 环境),\n const i18n = new I18N(dict) // 创建实例,传入词典 dict 并自动判断当前语言(浏览器环境),\n const i18n = new I18N({ }) // 创建实例,传入空词典\n ```\n @see https://github.com/ShenHongFei/xshell/tree/master/i18n\n */\n constructor (_dict: _Dict, language?: Language) {\n const is_browser = typeof document !== 'undefined' && typeof window !== 'undefined'\n \n const dict = new Dict(_dict)\n \n // --- if in bowser then detect language & intl\n if (is_browser) {\n const { search = '' } = document.location || { }\n const queries = qs.parse(search, { ignoreQueryPrefix: true })\n \n if (!language) {\n const lquery = queries.language as string // 暂时不考虑是数组的情况\n const lwindow = window.language\n const lbrowser = typeof navigator !== 'undefined' && navigator.language.slice(0, 2)\n \n language = (lquery || lwindow || Cookies.get('language') || lbrowser || 'en') as Language\n }\n \n if (!I18N.LANGUAGE_REGEXP.test(language))\n language = 'en'\n \n // console.log(`language = ${language}`)\n }\n \n language ||= 'en'\n \n this.language = language\n \n this.t = (text, options) => {\n options = options || { }\n \n const language = options.language || this.language\n \n return this.i18next.t(text, { ...options, lng: language, defaultValue: text })\n }\n \n this.r = (field, language = this.language) => \n field ?\n field[language] || field.en || field.zh || field as any || ''\n :\n field || ''\n \n // --- init i18next\n this.i18next = i18next.createInstance()\n \n if (is_browser)\n try {\n // 在无 React 的浏览器环境下避免 react-i18next 中执行 React.createContext() 报错\n // const React = require('react') as typeof import('react')\n const { initReactI18next, Trans: I18NextTrans } = require('react-i18next') as typeof import('react-i18next')\n this.i18next.use(initReactI18next)\n const _i18next = this.i18next\n // 绑定 Trans 组件的 i18n 到 this.i18next, 解决多个 i18next 冲突的问题\n // react-i18next/context.js 中 i18n 实例只在模块级别维护,多次 this.i18next.use(initReactI18next) 会覆盖前面的 i18n,导致 Trans 无法翻译\n // https://github.com/i18next/react-i18next/issues/726\n this.Trans = function Trans ({ i18n = _i18next, ...others }) {\n // 简单转发,性能更好\n return I18NextTrans({ i18n, ...others })\n // return React.createElement(I18NextTrans, { i18n, ...others } as any, children)\n // return <I18NextTrans {...{ i18n, ...others } }>{children}</I18NextTrans>\n }\n } catch { }\n \n this.i18next.init({\n lng: this.language,\n // LOCAL\n // debug: true,\n debug: false,\n fallbackLng: {\n en: ['zh'],\n ja: ['en', 'zh'],\n ko: ['en', 'zh'],\n },\n // 禁用 : 和 . 作为 seperator\n keySeparator: false,\n nsSeparator: false,\n resources: dict.to_resources(),\n interpolation: {\n escapeValue: false\n },\n react: {\n transKeepBasicHtmlNodesFor: []\n },\n })\n \n \n if (typeof window !== 'undefined' && window && !('i18n' in window))\n (window as any).i18n = this\n }\n \n \n /** 加载词典文件 (需要将这两行单独放一个文件里,以保证在 import 其他文件之前执行) \n \n @example\n import dict from './dict.json' // { \"添加\": { \"en\": \"Add\", \"ja\": \"追加\", \"ko\": \"추가\" } }\n i18n.init(dict)\n */\n init (dict: _Dict) {\n const resources = new Dict(dict).to_resources()\n for (const language in resources)\n this.i18next.addResources(language, 'translation', resources[language].translation)\n }\n \n toJSON () {\n return {\n language: this.language,\n }\n }\n}\n\n\nexport interface Hosts {\n \n}\n\n\nexport interface Roots {\n \n}\n\nexport type { _Dict, Item }\n\nexport interface I18NBasic {\n intl: boolean\n language: Language\n}\n"]}
package/i18n/rwdict.js CHANGED
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RWDict = void 0;
4
- require("../prototype.js");
5
- const dict_js_1 = require("./dict.js");
1
+ import '../prototype.js';
2
+ import { Dict, } from './dict.js';
6
3
  /** read write Dict for scanner */
7
- class RWDict extends dict_js_1.Dict {
4
+ export class RWDict extends Dict {
8
5
  /** 更新词条 */
9
6
  set_item(key, item, { print = true, placeholder = false, overwrite = false, dryrun = false, create = false } = {}) {
10
7
  if (!key || !item || typeof item !== 'object')
@@ -104,5 +101,4 @@ class RWDict extends dict_js_1.Dict {
104
101
  return JSON.stringify(this._dict, null, 4);
105
102
  }
106
103
  }
107
- exports.RWDict = RWDict;
108
104
  //# sourceMappingURL=rwdict.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rwdict.js","sourceRoot":"","sources":["rwdict.ts"],"names":[],"mappings":"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","sourcesContent":["import '../prototype.js'\n\nimport type { Language } from './index.js'\nimport {\n Dict,\n type Item,\n type _Dict,\n} from './dict.js'\n\n\n/** read write Dict for scanner */\nexport class RWDict extends Dict {\n /** 更新词条 */\n set_item (key: string, item: Item, { print = true, placeholder = false, overwrite = false, dryrun = false, create = false } = { }) {\n if (!key || !item || typeof item !== 'object') throw new Error('key/item 错误')\n \n if (!item.zh && !item.en && !item.ja && !item.ko) {\n if (placeholder) {\n const empty_item = { en: '', ja: '', ko: '' }\n if (!dryrun && create)\n this._dict[key] = empty_item\n if (print)\n console.log(`${'+ '.green + key}: ${JSON.stringify(empty_item)}`)\n } else\n if (print)\n console.log(`${'! 未翻译:'.red}${key}`)\n \n return\n }\n \n Object.entries(item).forEach( ([language, translation]: [Language, string]) => {\n this.set_translation(key, language, translation, { print, overwrite, dryrun, create })\n })\n }\n \n \n /** 更新词条翻译 */\n set_translation (key: string, language: Language, translation: string, {\n /** 允许新增词条 */\n create = true, \n /** 允许更新翻译 */\n overwrite = false,\n print = true, \n placeholder = false, \n dryrun = false\n } = { }) {\n if (!key || !language) throw new Error('key/language 不能为空')\n if (!translation) return\n \n // --- add item\n let item = this._dict[key]\n \n const id = `(${key.replace(/\\n/g, '\\\\n')}).${language}`\n \n if (!item) {\n if (!create) {\n console.log(`${'+ '.red + id} ${translation.replace(/\\n/g, '\\\\n')}`)\n return\n }\n if (print)\n console.log(`${'+ '.green}${key.replace(/\\n/g, '\\\\n')}`)\n \n item = { }\n \n if (!dryrun)\n this._dict[key] = item\n }\n \n \n // --- update translation\n const _translation = item[language]\n \n \n // add\n if (!_translation) {\n if (print)\n console.log(`${'+ '.green + id}: ${translation.replace(/\\n/g, '\\\\n')}`)\n if (!dryrun)\n item[language] = translation\n return\n }\n \n // modify\n if (_translation !== translation)\n if (!overwrite) {\n console.error(`${`已存在 ${id} 词条:`.red} ${JSON.stringify(item)}`)\n console.error(`${'M? '.yellow}${_translation.replace(/\\n/g, '\\\\n')} → ${translation.replace(/\\n/g, '\\\\n')}`)\n if (!dryrun)\n console.error(`如要更新翻译请设置 { overwrite: true },否则使用 i18n.t('text', { context: 'xxx' }) 标记文本以区分。\\n`)\n return\n } else {\n if (print)\n console.log(`${'M '.yellow}${_translation.replace(/\\n/g, '\\\\n')} → ${translation.replace(/\\n/g, '\\\\n')}`)\n if (!dryrun)\n item[language] = translation\n }\n }\n \n /** 合并、更新词典 \n print?: true \n dryrun?: false \n overwrite?: false \n create?: true \n */\n merge (_dict: _Dict, { print = true, overwrite = false, dryrun = false, create = true } = { }) {\n Object.entries(_dict).forEach( ([key, item]) => {\n this.set_item(key, item, { print, overwrite, dryrun, create })\n })\n \n if (dryrun) {\n console.log('dry run completed'.green)\n return\n }\n \n if (print)\n console.log('词典合并完成'.green)\n \n return this\n }\n \n \n \n /** trim?: [true] 是否过滤掉空词条及空翻译 */ \n to_json (trim: boolean = true) {\n if (trim)\n this._dict = Object.fromEntries(\n (Object.entries(this._dict)\n .map( ([key, item]) => ([\n key,\n Object.fromEntries(\n Object.entries(item).filter( ([ language, translation ]) => translation )\n )\n ])\n ).filter( ([ key, item ]: [string, Item]) => Object.keys(item).length )\n )\n )\n \n return JSON.stringify(this._dict, null, 4)\n }\n}\n"]}
1
+ {"version":3,"file":"rwdict.js","sourceRoot":"","sources":["rwdict.ts"],"names":[],"mappings":"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","sourcesContent":["import '../prototype.js'\n\nimport type { Language } from './index.js'\nimport {\n Dict,\n type Item,\n type _Dict,\n} from './dict.js'\n\n\n/** read write Dict for scanner */\nexport class RWDict extends Dict {\n /** 更新词条 */\n set_item (key: string, item: Item, { print = true, placeholder = false, overwrite = false, dryrun = false, create = false } = { }) {\n if (!key || !item || typeof item !== 'object') throw new Error('key/item 错误')\n \n if (!item.zh && !item.en && !item.ja && !item.ko) {\n if (placeholder) {\n const empty_item = { en: '', ja: '', ko: '' }\n if (!dryrun && create)\n this._dict[key] = empty_item\n if (print)\n console.log(`${'+ '.green + key}: ${JSON.stringify(empty_item)}`)\n } else\n if (print)\n console.log(`${'! 未翻译:'.red}${key}`)\n \n return\n }\n \n Object.entries(item).forEach( ([language, translation]: [Language, string]) => {\n this.set_translation(key, language, translation, { print, overwrite, dryrun, create })\n })\n }\n \n \n /** 更新词条翻译 */\n set_translation (key: string, language: Language, translation: string, {\n /** 允许新增词条 */\n create = true, \n /** 允许更新翻译 */\n overwrite = false,\n print = true, \n placeholder = false, \n dryrun = false\n } = { }) {\n if (!key || !language) throw new Error('key/language 不能为空')\n if (!translation) return\n \n // --- add item\n let item = this._dict[key]\n \n const id = `(${key.replace(/\\n/g, '\\\\n')}).${language}`\n \n if (!item) {\n if (!create) {\n console.log(`${'+ '.red + id} ${translation.replace(/\\n/g, '\\\\n')}`)\n return\n }\n if (print)\n console.log(`${'+ '.green}${key.replace(/\\n/g, '\\\\n')}`)\n \n item = { }\n \n if (!dryrun)\n this._dict[key] = item\n }\n \n \n // --- update translation\n const _translation = item[language]\n \n \n // add\n if (!_translation) {\n if (print)\n console.log(`${'+ '.green + id}: ${translation.replace(/\\n/g, '\\\\n')}`)\n if (!dryrun)\n item[language] = translation\n return\n }\n \n // modify\n if (_translation !== translation)\n if (!overwrite) {\n console.error(`${`已存在 ${id} 词条:`.red} ${JSON.stringify(item)}`)\n console.error(`${'M? '.yellow}${_translation.replace(/\\n/g, '\\\\n')} → ${translation.replace(/\\n/g, '\\\\n')}`)\n if (!dryrun)\n console.error(`如要更新翻译请设置 { overwrite: true },否则使用 i18n.t('text', { context: 'xxx' }) 标记文本以区分。\\n`)\n return\n } else {\n if (print)\n console.log(`${'M '.yellow}${_translation.replace(/\\n/g, '\\\\n')} → ${translation.replace(/\\n/g, '\\\\n')}`)\n if (!dryrun)\n item[language] = translation\n }\n }\n \n /** 合并、更新词典 \n print?: true \n dryrun?: false \n overwrite?: false \n create?: true \n */\n merge (_dict: _Dict, { print = true, overwrite = false, dryrun = false, create = true } = { }) {\n Object.entries(_dict).forEach( ([key, item]) => {\n this.set_item(key, item, { print, overwrite, dryrun, create })\n })\n \n if (dryrun) {\n console.log('dry run completed'.green)\n return\n }\n \n if (print)\n console.log('词典合并完成'.green)\n \n return this\n }\n \n \n \n /** trim?: [true] 是否过滤掉空词条及空翻译 */ \n to_json (trim: boolean = true) {\n if (trim)\n this._dict = Object.fromEntries(\n (Object.entries(this._dict)\n .map( ([key, item]) => ([\n key,\n Object.fromEntries(\n Object.entries(item).filter( ([ language, translation ]) => translation )\n )\n ])\n ).filter( ([ key, item ]: [string, Item]) => Object.keys(item).length )\n )\n )\n \n return JSON.stringify(this._dict, null, 4)\n }\n}\n"]}
@@ -1,9 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Checker = exports.unmarkeds = void 0;
4
- const tslib_1 = require("tslib");
5
- const types = tslib_1.__importStar(require("@babel/types"));
6
- exports.unmarkeds = [];
1
+ import * as types from '@babel/types';
2
+ export let unmarkeds = [];
7
3
  let t = 0;
8
4
  let Trans = 0;
9
5
  function is_t(node) {
@@ -29,7 +25,7 @@ function is_trans(node) {
29
25
  node.openingElement.name.name === "Trans");
30
26
  }
31
27
  const has_unmarked_chinese_characters = (str) => !t && !Trans && /[\u4e00-\u9fa5]/.test(str);
32
- function Checker({ filepath }) {
28
+ export function Checker({ filepath }) {
33
29
  return {
34
30
  CallExpression: {
35
31
  enter({ node }) {
@@ -53,17 +49,16 @@ function Checker({ filepath }) {
53
49
  },
54
50
  JSXText: ({ node }) => {
55
51
  if (has_unmarked_chinese_characters(node.value))
56
- exports.unmarkeds.push({ filepath, loc: node.loc, value: node.value });
52
+ unmarkeds.push({ filepath, loc: node.loc, value: node.value });
57
53
  },
58
54
  StringLiteral: ({ node }) => {
59
55
  if (has_unmarked_chinese_characters(node.value))
60
- exports.unmarkeds.push({ filepath, loc: node.loc, value: node.value });
56
+ unmarkeds.push({ filepath, loc: node.loc, value: node.value });
61
57
  },
62
58
  TemplateElement: ({ node }) => {
63
59
  if (has_unmarked_chinese_characters(node.value.raw))
64
- exports.unmarkeds.push({ filepath, loc: node.loc, value: node.value.cooked });
60
+ unmarkeds.push({ filepath, loc: node.loc, value: node.value.cooked });
65
61
  },
66
62
  };
67
63
  }
68
- exports.Checker = Checker;
69
64
  //# sourceMappingURL=checker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"checker.js","sourceRoot":"","sources":["checker.ts"],"names":[],"mappings":"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","sourcesContent":["import * as types from '@babel/types'\nimport { PluginItem } from '@babel/core'\n\nexport let unmarkeds = []\n\nlet t = 0\nlet Trans = 0\n\nfunction is_t (node: types.Node) {\n if (types.isCallExpression(node)) {\n // t('chtext')\n if (types.isIdentifier(node.callee) && node.callee.name === \"t\")\n return true\n \n // i18n.t('chtext') | i18n.__('chtext')\n if (\n types.isMemberExpression(node.callee) &&\n types.isIdentifier(node.callee.object) &&\n types.isIdentifier(node.callee.property) &&\n node.callee.object.name === \"i18n\" &&\n (node.callee.property.name === \"t\" || node.callee.property.name === '__')\n )\n return true\n }\n return false\n}\n\nfunction is_trans (node: types.Node) {\n // <Trans>\n return (\n types.isJSXElement(node) &&\n types.isJSXOpeningElement(node.openingElement) &&\n types.isJSXIdentifier(node.openingElement.name) &&\n node.openingElement.name.name === \"Trans\"\n )\n}\n\nconst has_unmarked_chinese_characters = (str: string) => \n !t && !Trans && /[\\u4e00-\\u9fa5]/.test(str)\n\n\nexport function Checker ({ filepath }) {\n return {\n CallExpression: {\n enter({ node }) {\n if (is_t(node))\n t++\n },\n exit({ node }) {\n if (is_t(node))\n t--\n },\n },\n JSXElement: {\n enter({ node }) {\n if (is_trans(node))\n Trans++\n },\n exit({ node }) {\n if (is_trans(node))\n Trans--\n },\n },\n JSXText: ({ node }) => {\n if (has_unmarked_chinese_characters(node.value))\n unmarkeds.push({ filepath, loc: node.loc, value: node.value })\n },\n StringLiteral: ({ node }) => {\n if (has_unmarked_chinese_characters(node.value))\n unmarkeds.push({ filepath, loc: node.loc, value: node.value })\n },\n TemplateElement: ({ node }) => {\n if (has_unmarked_chinese_characters(node.value.raw))\n unmarkeds.push({ filepath, loc: node.loc, value: node.value.cooked })\n },\n } as PluginItem\n}\n"]}
1
+ {"version":3,"file":"checker.js","sourceRoot":"","sources":["checker.ts"],"names":[],"mappings":"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","sourcesContent":["import * as types from '@babel/types'\nimport { PluginItem } from '@babel/core'\n\nexport let unmarkeds = []\n\nlet t = 0\nlet Trans = 0\n\nfunction is_t (node: types.Node) {\n if (types.isCallExpression(node)) {\n // t('chtext')\n if (types.isIdentifier(node.callee) && node.callee.name === \"t\")\n return true\n \n // i18n.t('chtext') | i18n.__('chtext')\n if (\n types.isMemberExpression(node.callee) &&\n types.isIdentifier(node.callee.object) &&\n types.isIdentifier(node.callee.property) &&\n node.callee.object.name === \"i18n\" &&\n (node.callee.property.name === \"t\" || node.callee.property.name === '__')\n )\n return true\n }\n return false\n}\n\nfunction is_trans (node: types.Node) {\n // <Trans>\n return (\n types.isJSXElement(node) &&\n types.isJSXOpeningElement(node.openingElement) &&\n types.isJSXIdentifier(node.openingElement.name) &&\n node.openingElement.name.name === \"Trans\"\n )\n}\n\nconst has_unmarked_chinese_characters = (str: string) => \n !t && !Trans && /[\\u4e00-\\u9fa5]/.test(str)\n\n\nexport function Checker ({ filepath }) {\n return {\n CallExpression: {\n enter({ node }) {\n if (is_t(node))\n t++\n },\n exit({ node }) {\n if (is_t(node))\n t--\n },\n },\n JSXElement: {\n enter({ node }) {\n if (is_trans(node))\n Trans++\n },\n exit({ node }) {\n if (is_trans(node))\n Trans--\n },\n },\n JSXText: ({ node }) => {\n if (has_unmarked_chinese_characters(node.value))\n unmarkeds.push({ filepath, loc: node.loc, value: node.value })\n },\n StringLiteral: ({ node }) => {\n if (has_unmarked_chinese_characters(node.value))\n unmarkeds.push({ filepath, loc: node.loc, value: node.value })\n },\n TemplateElement: ({ node }) => {\n if (has_unmarked_chinese_characters(node.value.raw))\n unmarkeds.push({ filepath, loc: node.loc, value: node.value.cooked })\n },\n } as PluginItem\n}\n"]}
@@ -1,23 +1,19 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scanner = void 0;
4
- const tslib_1 = require("tslib");
5
- const i18next_scanner_1 = tslib_1.__importDefault(require("i18next-scanner"));
6
- const upath_1 = tslib_1.__importDefault(require("upath"));
7
- const ejs_1 = tslib_1.__importDefault(require("ejs"));
8
- const vinyl_fs_1 = tslib_1.__importDefault(require("vinyl-fs"));
9
- const gulp_sort_1 = tslib_1.__importDefault(require("gulp-sort"));
10
- const ora_1 = tslib_1.__importDefault(require("ora"));
11
- const cli_truncate_1 = tslib_1.__importDefault(require("cli-truncate"));
12
- const vinyl_1 = tslib_1.__importDefault(require("vinyl"));
13
- const through2_1 = tslib_1.__importDefault(require("through2"));
14
- const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
15
- require("../../prototype.js");
16
- const utils_js_1 = require("../../utils.js");
17
- const index_js_1 = require("../index.js");
18
- const rwdict_js_1 = require("../rwdict.js");
19
- const utils_js_2 = require("../utils.js");
20
- const parser_js_1 = require("./parser.js");
1
+ import i18n_scanner from 'i18next-scanner';
2
+ import path from 'upath';
3
+ import ejs from 'ejs';
4
+ import vfs from 'vinyl-fs';
5
+ import sort from 'gulp-sort';
6
+ import ora from 'ora';
7
+ import cli_truncate from 'cli-truncate';
8
+ import Vinyl from 'vinyl';
9
+ import through2 from 'through2';
10
+ import CliTable from 'cli-table3';
11
+ import '../../prototype.js';
12
+ import { map_stream } from '../../utils.js';
13
+ import { LANGUAGES, } from '../index.js';
14
+ import { RWDict } from '../rwdict.js';
15
+ import { try_load_dict } from '../utils.js';
16
+ import { mix_parse_trans_from_string_by_babel } from './parser.js';
21
17
  /** 默认 i18next 扫描配置 */
22
18
  const DEFAULT_CONFIG = {
23
19
  debug: false,
@@ -103,8 +99,8 @@ const VALID_EXTENTIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.ejs']);
103
99
  - `process.cwd()` rootdir 要扫描根目录
104
100
  - config 配置信息
105
101
  */
106
- async function scanner(rootdir = upath_1.default.normalize(process.cwd()), config = {}) {
107
- const output = upath_1.default.resolve(rootdir, config.output || DEFAULT_CONFIG.output);
102
+ export async function scanner(rootdir = path.normalize(process.cwd()), config = {}) {
103
+ const output = path.resolve(rootdir, config.output || DEFAULT_CONFIG.output);
108
104
  if (!config.input.length)
109
105
  throw new Error('运行 i18n-scan 请指定 --input');
110
106
  const input = [...config.input, ...DEFAULT_CONFIG.input];
@@ -115,32 +111,32 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
115
111
  output,
116
112
  resource: {
117
113
  loadPath: '',
118
- savePath: upath_1.default.resolve(output, 'translation/{{lng}}.js'),
114
+ savePath: path.resolve(output, 'translation/{{lng}}.js'),
119
115
  jsonIndent: 4,
120
116
  lineEnding: '\n'
121
117
  }
122
118
  };
123
- let dict = new rwdict_js_1.RWDict();
119
+ let dict = new RWDict();
124
120
  for (const fp_dict of config.dict)
125
- dict.merge(await (0, utils_js_2.try_load_dict)(upath_1.default.resolve(output, fp_dict)), { print: false, overwrite: true });
121
+ dict.merge(await try_load_dict(path.resolve(output, fp_dict)), { print: false, overwrite: true });
126
122
  let c_files = 0;
127
123
  let c_scanneds = 0;
128
124
  let error_handlers = [];
129
125
  // 所有语言的扫描统计信息
130
126
  let stats = {};
131
- for (const language of index_js_1.LANGUAGES)
127
+ for (const language of LANGUAGES)
132
128
  stats[language] = {
133
129
  translateds: new Set(),
134
130
  untranslateds: new Set()
135
131
  };
136
- let spinner = (0, ora_1.default)({ interval: 66 }).start('Scanning...');
132
+ let spinner = ora({ interval: 66 }).start('Scanning...');
137
133
  function on_scanned(text, { language, key, defaultValue, count, context }) {
138
134
  // console.log(text, { language, key, defaultValue, count, context })
139
135
  text = text || defaultValue;
140
136
  if (!key)
141
137
  key = context ? `${text}_${context}` : text;
142
138
  if (!language) {
143
- for (const language of index_js_1.LANGUAGES)
139
+ for (const language of LANGUAGES)
144
140
  on_scanned(text, { language, key, count, context });
145
141
  return;
146
142
  }
@@ -161,19 +157,19 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
161
157
  on_scanned(text, { language, key: `${key}_plural`, context });
162
158
  }
163
159
  function new_vinyl_file(_path, data) {
164
- return new vinyl_1.default({
160
+ return new Vinyl({
165
161
  cwd: rootdir,
166
162
  base: rootdir,
167
- path: upath_1.default.resolve(config.output, _path),
163
+ path: path.resolve(config.output, _path),
168
164
  contents: Buffer.from(typeof data === 'string' ? data : JSON.stringify(data, null, 4))
169
165
  });
170
166
  }
171
167
  return new Promise((resolve, reject) => {
172
168
  // ------------ scan by file
173
- vinyl_fs_1.default
169
+ vfs
174
170
  .src(config.input, { cwd: rootdir, sync: false })
175
171
  // 每个文件扫描前,统计文件数量
176
- .pipe((0, utils_js_1.map_stream)((file, cb) => {
172
+ .pipe(map_stream((file, cb) => {
177
173
  // 支持 `// @i18n-noscan` 忽略扫描
178
174
  if (/\/\/\s*@i18n-noscan\s/.test(file.contents.toString()))
179
175
  return cb();
@@ -181,11 +177,11 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
181
177
  cb(null, file);
182
178
  }))
183
179
  // 对文件进行排序,保证词条有一定的顺序
184
- .pipe((0, gulp_sort_1.default)())
180
+ .pipe(sort())
185
181
  // 分析代码提取词条
186
- .pipe(i18next_scanner_1.default.createStream(config, function transform(file, encoding, callback) {
182
+ .pipe(i18n_scanner.createStream(config, function transform(file, encoding, callback) {
187
183
  const { parser } = this;
188
- const ext = upath_1.default.extname(file.path);
184
+ const ext = path.extname(file.path);
189
185
  // 只扫描源码文件
190
186
  if (!VALID_EXTENTIONS.has(ext)) {
191
187
  callback();
@@ -194,17 +190,17 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
194
190
  c_scanneds++;
195
191
  const percent = Math.round(100 * c_scanneds / c_files);
196
192
  const text = `Scanning (${percent}%): ${file.path.blue}`;
197
- spinner.text = (0, cli_truncate_1.default)(text, process.stdout.columns - 5, { position: 'middle', });
193
+ spinner.text = cli_truncate(text, process.stdout.columns - 5, { position: 'middle', });
198
194
  let code = file.contents.toString();
199
195
  if (ext === '.ejs')
200
- code = ejs_1.default.compile(code, { filename: file.path, client: true, legacyInclude: true }).toString();
196
+ code = ejs.compile(code, { filename: file.path, client: true, legacyInclude: true }).toString();
201
197
  // --- 添加代码中扫描到的 i18n.t('key') 中的 key 到 parser
202
198
  // parser.parseFuncFromString 使用 esprima 来解析代码,esprima 仍然不支持 optional chaining !!
203
199
  parser.parseFuncFromString(code.replace(/\?\.\[/g, '[').replace(/\?\.\(/g, '(').replace(/\?\./g, '.'), on_scanned);
204
200
  // --- 添加代码中扫描到的 Trans 组件中的 key 到 parser
205
201
  if (ext === '.jsx' || ext === '.tsx') {
206
202
  // parser.parseTransFromString 使用 acorn 解析代码,不支持 TypeScript,添加 parser.parseTransFromStringByBabel
207
- (0, parser_js_1.mix_parse_trans_from_string_by_babel)(parser);
203
+ mix_parse_trans_from_string_by_babel(parser);
208
204
  parser.parseTransFromStringByBabel(code, { filepath: file.path }, on_scanned, (error) => {
209
205
  error_handlers.push(error);
210
206
  });
@@ -212,7 +208,7 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
212
208
  setTimeout(callback, 0);
213
209
  }))
214
210
  // 创建词条文件
215
- .pipe(through2_1.default.obj(
211
+ .pipe(through2.obj(
216
212
  /** i18n-scanner 会把扫描结果以每个语言一个文件的形式提供,这里解析扫描结果
217
213
  * file: 翻译 resource 文件,其中 file.contents 包含翻译的扫描结果
218
214
  */
@@ -222,7 +218,7 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
222
218
  // ------------ stats.json
223
219
  this.push(new_vinyl_file('stats.json', Object.fromEntries(Object.entries(stats).map(([l, { translateds, untranslateds }]) => [l, { translateds: Array.from(translateds), untranslateds: Array.from(untranslateds) }]))));
224
220
  // ------------ 打印 cli 统计表
225
- const table = new cli_table3_1.default({
221
+ const table = new CliTable({
226
222
  head: [
227
223
  '语言',
228
224
  '未翻译'.red,
@@ -303,7 +299,7 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
303
299
  else
304
300
  console.log('\n所有词条都至少含有英文翻译'.green);
305
301
  // ------------ 生成 untranslateds.json (扫描到词条还没有英文翻译)
306
- const fp_untranslateds = upath_1.default.resolve(config.output, 'untranslateds.json');
302
+ const fp_untranslateds = path.resolve(config.output, 'untranslateds.json');
307
303
  let untranslateds = {};
308
304
  for (const key of stats.en.untranslateds) {
309
305
  let item = { ...dict.get(key) };
@@ -314,7 +310,7 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
314
310
  }
315
311
  this.push(new_vinyl_file(fp_untranslateds, untranslateds));
316
312
  // ------------ 写入 dict.json
317
- const fp_dict_new = upath_1.default.resolve(output, 'dict.json');
313
+ const fp_dict_new = path.resolve(output, 'dict.json');
318
314
  this.push(new_vinyl_file(fp_dict_new, dict.to_json(true) + '\n'));
319
315
  console.log(`\n\n${'请手动补全未翻译的词条: '.yellow}${fp_untranslateds.underline.blue}\n` +
320
316
  `${'请检查新生成的词典文件: '.yellow}${fp_dict_new.underline.blue}\n` +
@@ -325,7 +321,7 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
325
321
  cb();
326
322
  }))
327
323
  // 写入词条文件
328
- .pipe(vinyl_fs_1.default.dest(rootdir))
324
+ .pipe(vfs.dest(rootdir))
329
325
  .on('end', () => {
330
326
  if (error_handlers.length) {
331
327
  for (const error_handler of error_handlers)
@@ -336,5 +332,4 @@ async function scanner(rootdir = upath_1.default.normalize(process.cwd()), confi
336
332
  });
337
333
  });
338
334
  }
339
- exports.scanner = scanner;
340
335
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Transform } from 'stream'\n\nimport i18n_scanner from 'i18next-scanner'\nimport path from 'upath'\nimport ejs from 'ejs'\nimport vfs from 'vinyl-fs'\nimport sort from 'gulp-sort'\nimport ora from 'ora'\nimport cli_truncate from 'cli-truncate'\nimport Vinyl from 'vinyl'\nimport through2 from 'through2'\nimport CliTable from 'cli-table3'\n\nimport '../../prototype.js'\nimport { map_stream } from '../../utils.js'\n\nimport {\n LANGUAGES,\n type Language,\n} from '../index.js'\nimport { RWDict } from '../rwdict.js'\nimport type { _Dict } from '../dict.js'\nimport { try_load_dict } from '../utils.js'\n\n\nimport { mix_parse_trans_from_string_by_babel } from './parser.js'\n\n\n\n/** 默认 i18next 扫描配置 */\nconst DEFAULT_CONFIG = {\n debug: false,\n \n input: [\n // 'src/**/*.{js,jsx,ts,tsx}',\n '!i18n/**', // Use ! to filter out files or directories\n '!node_modules/**',\n '!**/*.d.ts',\n ],\n \n // 相对于根目录\n output: 'i18n/',\n \n // 若是相对路径,则以 output 为基准进行解析\n dict: ['dict.json', 'untranslateds.json'],\n \n lngs: ['zh', 'en', 'ja', 'ko'],\n ns: ['translation'],\n defaultLng: 'zh',\n defaultNs: 'translation',\n\n func: {\n list: [ 'i18next.t', 'i18n.t', 'i18n.__', 't', '__' ],\n extensions: [ ], // 避免在 transform 中执行原生的 parseFuncFromString\n },\n \n trans: {\n extensions: [ ], // 避免在 transform 中执行原生的 parseTransFromString\n fallbackKey: true,\n \n babylon: {\n sourceType: 'module',\n \n allowAwaitOutsideFunction: true,\n \n // https://babeljs.io/docs/en/babel-parser\n plugins: [\n // Language extensions\n 'jsx',\n 'typescript',\n \n // ECMAScript proposals\n 'classProperties',\n 'classPrivateProperties',\n 'classPrivateMethods',\n 'classStaticBlock',\n 'decimal',\n ['decorators', { decoratorsBeforeExport: true }],\n 'doExpressions',\n 'exportDefaultFrom',\n 'functionBind',\n 'importAssertions',\n 'moduleBlocks',\n 'moduleStringNames',\n 'partialApplication',\n ['pipelineOperator', { proposal: 'smart' }],\n 'privateIn',\n ['recordAndTuple', { syntaxType: 'bar' }],\n 'throwExpressions',\n 'topLevelAwait',\n ],\n } as import('@babel/parser').ParserOptions,\n \n // 实际并没有用到 acorn, 用了 babel\n acorn: {\n ecmaVersion: 'latest',\n sourceType: 'module', // defaults to 'module'\n // Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options\n }\n },\n \n // 禁用 : 和 . 作为 seperator\n keySeparator: false, // char to separate keys\n nsSeparator: false, // char to split namespace from key\n \n // Context Form\n context: true, // whether to add context form key\n contextFallback: true, // whether to add a fallback key as well as the context form key\n contextSeparator: '_', // char to split context from key\n\n // Plural\n // whether to add plural form key\n plural (language: string, ns: string, key: string, options: any /** Config */) {\n return language === 'en'\n }, \n pluralFallback: true, // whether to add a fallback key as well as the plural form key\n pluralSeparator: '_', // char to split plural from key\n \n // interpolation options\n interpolation: {\n prefix: '{{', // prefix for interpolation\n suffix: '}}' // suffix for interpolation\n }\n}\n\nconst VALID_EXTENTIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.ejs'])\n\nexport type Config = Partial<(typeof DEFAULT_CONFIG) & {\n defaultValue?: string\n resource?: { loadPath?: string, savePath?: string, jsonIndent?: number, lineEnding?: '\\n' }\n}>\n\n\n/** 扫描源码中的词条,以及收集未翻译的词条,将结果保存到 dict.json 和 untranslateds.json\n - `process.cwd()` rootdir 要扫描根目录\n - config 配置信息\n*/\nexport async function scanner (rootdir: string = path.normalize(process.cwd()), config: Config = { }) {\n const output = path.resolve(rootdir, config.output || DEFAULT_CONFIG.output)\n \n if (!config.input.length)\n throw new Error('运行 i18n-scan 请指定 --input')\n \n const input = [...config.input, ...DEFAULT_CONFIG.input]\n \n config = {\n ...DEFAULT_CONFIG,\n ...config,\n input,\n output,\n resource: {\n loadPath: '',\n savePath: path.resolve(output, 'translation/{{lng}}.js'),\n jsonIndent: 4,\n lineEnding: '\\n'\n }\n }\n \n let dict = new RWDict()\n \n for (const fp_dict of config.dict)\n dict.merge(\n await try_load_dict(\n path.resolve(output, fp_dict)\n ), { print: false, overwrite: true }\n )\n \n \n let c_files = 0\n let c_scanneds = 0\n let error_handlers = []\n \n // 所有语言的扫描统计信息\n let stats: Record<Language, { translateds: Set<string>, untranslateds: Set<string> }> = { } as any\n \n for (const language of LANGUAGES)\n stats[language] = {\n translateds: new Set<string>(),\n untranslateds: new Set<string>()\n }\n \n \n let spinner = ora({ interval: 66 }).start('Scanning...')\n \n \n \n function on_scanned (text: string, { language, key, defaultValue, count, context }: { language?: Language, key?: string, defaultValue?: string, count?: number, context?: string }) {\n // console.log(text, { language, key, defaultValue, count, context })\n \n text = text || defaultValue\n \n if (!key)\n key = context ? `${text}_${context}` : text\n \n if (!language) {\n for (const language of LANGUAGES)\n on_scanned(text, { language, key, count, context })\n return\n }\n \n // console.log(text, { language, key, defaultValue, count, context })\n // debugger\n \n const stat = stats[language]\n \n // 获取已有翻译\n const translation = \n dict.get(key, language) || \n language === 'zh' && text || \n ''\n \n if (language === 'zh' && !context)\n return\n \n if (translation)\n stat.translateds.add(key)\n else\n stat.untranslateds.add(key)\n \n if (language === 'en' && count !== undefined)\n on_scanned(text, { language, key: `${key}_plural`, context })\n }\n \n \n function new_vinyl_file (_path: string, data: string | object) {\n return new Vinyl({\n cwd: rootdir,\n base: rootdir,\n path: path.resolve(config.output, _path),\n contents: Buffer.from(typeof data === 'string' ? data : JSON.stringify(data, null, 4))\n })\n }\n \n return new Promise<number>((resolve, reject) => {\n // ------------ scan by file\n vfs\n .src(config.input, { cwd: rootdir, sync: false })\n \n // 每个文件扫描前,统计文件数量\n .pipe(\n map_stream((file: Vinyl, cb: Function) => {\n // 支持 `// @i18n-noscan` 忽略扫描\n if (/\\/\\/\\s*@i18n-noscan\\s/.test(file.contents.toString()))\n return cb()\n c_files++\n cb(null, file)\n })\n )\n \n // 对文件进行排序,保证词条有一定的顺序\n .pipe(\n sort()\n )\n \n // 分析代码提取词条\n .pipe(\n i18n_scanner.createStream( config, function transform (this: { parser: any }, file: Vinyl, encoding: string, callback: Function): void {\n const { parser } = this\n const ext = path.extname(file.path)\n \n // 只扫描源码文件\n if (!VALID_EXTENTIONS.has(ext)) {\n callback()\n return\n }\n \n c_scanneds++\n const percent = Math.round(\n 100 * c_scanneds / c_files\n )\n const text = `Scanning (${percent}%): ${file.path.blue}`\n spinner.text = cli_truncate(text, process.stdout.columns - 5, { position: 'middle', })\n \n let code = file.contents.toString()\n \n if (ext === '.ejs')\n code = ejs.compile( code, { filename: file.path, client: true, legacyInclude: true } as any ).toString()\n \n \n // --- 添加代码中扫描到的 i18n.t('key') 中的 key 到 parser\n // parser.parseFuncFromString 使用 esprima 来解析代码,esprima 仍然不支持 optional chaining !!\n parser.parseFuncFromString(code.replace(/\\?\\.\\[/g, '[').replace(/\\?\\.\\(/g, '(').replace(/\\?\\./g, '.'), on_scanned)\n \n // --- 添加代码中扫描到的 Trans 组件中的 key 到 parser\n if (ext === '.jsx' || ext === '.tsx') {\n // parser.parseTransFromString 使用 acorn 解析代码,不支持 TypeScript,添加 parser.parseTransFromStringByBabel\n mix_parse_trans_from_string_by_babel(parser)\n parser.parseTransFromStringByBabel(\n code,\n { filepath: file.path },\n on_scanned,\n (error: Error) => {\n error_handlers.push(error)\n }\n )\n }\n \n setTimeout(callback, 0)\n })\n )\n \n // 创建词条文件\n .pipe(\n through2.obj(\n /** i18n-scanner 会把扫描结果以每个语言一个文件的形式提供,这里解析扫描结果\n * file: 翻译 resource 文件,其中 file.contents 包含翻译的扫描结果\n */\n function write (this: Transform, file: Vinyl, encoding: string, cb: Function) { cb() },\n \n /** 生成 stats.json, unmarkeds.md; 打印 untranslated / unmarkeds */\n function flush (this: Transform, cb: Function) {\n // ------------ stats.json\n this.push(new_vinyl_file('stats.json', \n Object.fromEntries(\n Object.entries(stats).map( ([l, { translateds, untranslateds }]) => \n [l, { translateds: Array.from(translateds), untranslateds: Array.from(untranslateds) }])\n )\n ))\n \n \n // ------------ 打印 cli 统计表\n const table = new CliTable({\n head: [\n '语言',\n '未翻译'.red,\n '已翻译'.green,\n ],\n colAligns: ['right', 'right', 'right', 'right'],\n style: { head: [] },\n chars: {\n top: '',\n 'top-mid': '',\n 'top-left': '',\n 'top-right': '',\n bottom: '',\n 'bottom-mid': '',\n 'bottom-left': '',\n 'bottom-right': '',\n left: '',\n 'left-mid': '',\n mid: '',\n 'mid-mid': '',\n right: '',\n 'right-mid': '',\n middle: ' ',\n },\n })\n \n Object.entries(stats).forEach( ([lang, stat]) => {\n table.push([\n lang, \n String(stat.untranslateds.size).red, \n String(stat.translateds.size).green\n ] as any)\n })\n \n \n \n spinner.stop()\n console.log(`Scanned ${c_files} files. Occured ${error_handlers.length} errors.`)\n console.log(table.toString())\n \n \n // ------------ 生成 unmarkeds.md 统计\n /*\n const fp_unmarked = path.resolve(config.output, 'unmarkeds.md')\n \n if (fs.existsSync(fp_unmarked))\n rimraf.sync(fp_unmarked)\n \n if (unmarkeds.length) {\n console.log(colors.yellow(`\\n⚠️ 发现未标记的中文字符 ${unmarkeds.length} 处:\\n`))\n unmarkeds.forEach(({ value, filepath, loc: { start } }, index) => {\n if (index >= 5) return\n console.log( ` ${colors.white(`'${value}'`)}\\t${colors.blue.underline(`${path.relative(rootdir, filepath)}:${start.line}:${start.column + 1}`)}` )\n })\n }\n \n this.push( new_vinyl_file( fp_unmarked, \n unmarkeds.map( ({ value, filepath, loc }) =>\n '- [' + value.trim() + '](' + path.relative( config.output, path.resolve(rootdir, filepath || '') ) + '#L' + loc.start.line + ')'\n ).join('\\n') + '\\n'\n ))\n \n \n if (unmarkeds.length > 5) {\n console.log(' ...')\n console.log(colors.yellow(`\\n 完整未标记词条请查看 ${colors.blue.underline(path.relative(rootdir, fp_unmarked))}`))\n }\n */\n \n const en_untranslateds = stats.en.untranslateds\n if (en_untranslateds.size) {\n console.log('\\n缺少英文翻译的词条:'.yellow)\n let i = 0\n for (const untranslated of en_untranslateds) {\n if (i >= 10)\n break\n console.log(untranslated)\n i++\n }\n if (en_untranslateds.size > 10) {\n console.log('...')\n console.log(`--- 共 ${en_untranslateds.size} 个未翻译的英文词条 ---`)\n }\n } else\n console.log('\\n所有词条都至少含有英文翻译'.green)\n \n \n // ------------ 生成 untranslateds.json (扫描到词条还没有英文翻译)\n const fp_untranslateds = path.resolve(config.output, 'untranslateds.json')\n \n let untranslateds: _Dict = { }\n \n for (const key of stats.en.untranslateds) {\n let item = { ...dict.get(key) }\n item.en ||= ''\n item.ja ||= ''\n item.ko ||= ''\n untranslateds[key] = item\n }\n \n this.push(\n new_vinyl_file(fp_untranslateds, untranslateds)\n )\n \n \n // ------------ 写入 dict.json\n const fp_dict_new = path.resolve(output, 'dict.json')\n this.push(\n new_vinyl_file(\n fp_dict_new,\n dict.to_json(true) + '\\n'\n )\n )\n \n console.log(\n `\\n\\n${'请手动补全未翻译的词条: '.yellow}${fp_untranslateds.underline.blue}\\n` +\n `${'请检查新生成的词典文件: '.yellow}${fp_dict_new.underline.blue}\\n` +\n '\\n' +\n '补全 untranslateds.json 后需要重新运行扫描,会根据 untranslateds.json 更新 dict.json\\n'.yellow +\n '最后 dict.json 所包含的词条会被打包进 js, 通过 new I18N(<dict.json>) 或 i18n.init(<dict.json>) 加载\\n\\n'.yellow +\n `${'详细文档请查看: '.yellow}${'https://github.com/ShenHongFei/xshell/tree/master/i18n'.blue.underline}`\n )\n \n cb()\n }\n )\n )\n \n // 写入词条文件\n .pipe(\n vfs.dest(rootdir)\n )\n \n .on('end', () => {\n if (error_handlers.length) {\n for (const error_handler of error_handlers)\n error_handler()\n \n console.log(`以上错误可能是由不规范的词条标记导致,标记规范可见:\\n${'https://www.i18next.com/translation-function/essentials'.blue.underline}`)\n }\n \n resolve(stats.en.untranslateds.size)\n })\n })\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,MAAM,iBAAiB,CAAA;AAC1C,OAAO,IAAI,MAAM,OAAO,CAAA;AACxB,OAAO,GAAG,MAAM,KAAK,CAAA;AACrB,OAAO,GAAG,MAAM,UAAU,CAAA;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,GAAG,MAAM,KAAK,CAAA;AACrB,OAAO,YAAY,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,QAAQ,MAAM,YAAY,CAAA;AAEjC,OAAO,oBAAoB,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EACH,SAAS,GAEZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,OAAO,EAAE,oCAAoC,EAAE,MAAM,aAAa,CAAA;AAIlE,sBAAsB;AACtB,MAAM,cAAc,GAAG;IACnB,KAAK,EAAE,KAAK;IAEZ,KAAK,EAAE;QACH,8BAA8B;QAC9B,UAAU;QACV,kBAAkB;QAClB,YAAY;KACf;IAED,SAAS;IACT,MAAM,EAAE,OAAO;IAEf,2BAA2B;IAC3B,IAAI,EAAE,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAEzC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IAC9B,EAAE,EAAE,CAAC,aAAa,CAAC;IACnB,UAAU,EAAE,IAAI;IAChB,SAAS,EAAE,aAAa;IAExB,IAAI,EAAE;QACF,IAAI,EAAE,CAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAE;QACrD,UAAU,EAAE,EAAG,EAAE,2CAA2C;KAC/D;IAED,KAAK,EAAE;QACH,UAAU,EAAE,EAAG;QACf,WAAW,EAAE,IAAI;QAEjB,OAAO,EAAE;YACL,UAAU,EAAE,QAAQ;YAEpB,yBAAyB,EAAE,IAAI;YAE/B,0CAA0C;YAC1C,OAAO,EAAE;gBACL,sBAAsB;gBACtB,KAAK;gBACL,YAAY;gBAEZ,uBAAuB;gBACvB,iBAAiB;gBACjB,wBAAwB;gBACxB,qBAAqB;gBACrB,kBAAkB;gBAClB,SAAS;gBACT,CAAC,YAAY,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC;gBAChD,eAAe;gBACf,mBAAmB;gBACnB,cAAc;gBACd,kBAAkB;gBAClB,cAAc;gBACd,mBAAmB;gBACnB,oBAAoB;gBACpB,CAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;gBAC3C,WAAW;gBACX,CAAC,gBAAgB,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;gBACzC,kBAAkB;gBAClB,eAAe;aAClB;SACqC;QAE1C,0BAA0B;QAC1B,KAAK,EAAE;YACH,WAAW,EAAE,QAAQ;YACrB,UAAU,EAAE,QAAQ,EAAE,uBAAuB;YAC7C,gGAAgG;SACnG;KACJ;IAED,wBAAwB;IACxB,YAAY,EAAE,KAAK;IACnB,WAAW,EAAE,KAAK;IAElB,eAAe;IACf,OAAO,EAAE,IAAI;IACb,eAAe,EAAE,IAAI;IACrB,gBAAgB,EAAE,GAAG;IAErB,SAAS;IACT,iCAAiC;IACjC,MAAM,CAAE,QAAgB,EAAE,EAAU,EAAE,GAAW,EAAE,OAAY,CAAC,aAAa;QACzE,OAAO,QAAQ,KAAK,IAAI,CAAA;IAC5B,CAAC;IACD,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,GAAG;IAEpB,wBAAwB;IACxB,aAAa,EAAE;QACX,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,IAAI,CAAC,2BAA2B;KAC3C;CACJ,CAAA;AAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAQxE;;;EAGE;AACF,MAAM,CAAC,KAAK,UAAU,OAAO,CAAE,UAAkB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,SAAiB,EAAG;IAChG,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC,CAAA;IAE5E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;QACpB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;IAE/C,MAAM,KAAK,GAAI,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;IAEzD,MAAM,GAAG;QACL,GAAG,cAAc;QACjB,GAAG,MAAM;QACT,KAAK;QACL,MAAM;QACN,QAAQ,EAAE;YACN,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,CAAC;YACxD,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,IAAI;SACnB;KACJ,CAAA;IAED,IAAI,IAAI,GAAG,IAAI,MAAM,EAAE,CAAA;IAEvB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI;QAC7B,IAAI,CAAC,KAAK,CACN,MAAM,aAAa,CACf,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAChC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CACvC,CAAA;IAGL,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,cAAc,GAAG,EAAE,CAAA;IAEvB,cAAc;IACd,IAAI,KAAK,GAA+E,EAAU,CAAA;IAElG,KAAK,MAAM,QAAQ,IAAI,SAAS;QAC5B,KAAK,CAAC,QAAQ,CAAC,GAAG;YACd,WAAW,EAAE,IAAI,GAAG,EAAU;YAC9B,aAAa,EAAE,IAAI,GAAG,EAAU;SACnC,CAAA;IAGL,IAAI,OAAO,GAAG,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;IAIxD,SAAS,UAAU,CAAE,IAAY,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAkG;QAC9K,qEAAqE;QAErE,IAAI,GAAG,IAAI,IAAI,YAAY,CAAA;QAE3B,IAAI,CAAC,GAAG;YACJ,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QAE/C,IAAI,CAAC,QAAQ,EAAE;YACX,KAAK,MAAM,QAAQ,IAAI,SAAS;gBAC5B,UAAU,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;YACvD,OAAM;SACT;QAED,qEAAqE;QACrE,WAAW;QAEX,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;QAE5B,SAAS;QACT,MAAM,WAAW,GACb,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC;YACvB,QAAQ,KAAK,IAAI,IAAI,IAAI;YACzB,EAAE,CAAA;QAEN,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,OAAO;YAC7B,OAAM;QAEV,IAAI,WAAW;YACX,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;;YAEzB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAE/B,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YACxC,UAAU,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;IACrE,CAAC;IAGD,SAAS,cAAc,CAAE,KAAa,EAAE,IAAqB;QACzD,OAAO,IAAI,KAAK,CAAC;YACb,GAAG,EAAE,OAAO;YACZ,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;YACxC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;SACzF,CAAC,CAAA;IACN,CAAC;IAED,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,4BAA4B;QAC5B,GAAG;aACE,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAEjD,iBAAiB;aAChB,IAAI,CACD,UAAU,CAAC,CAAC,IAAW,EAAE,EAAY,EAAE,EAAE;YACrC,4BAA4B;YAC5B,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACtD,OAAO,EAAE,EAAE,CAAA;YACf,OAAO,EAAE,CAAA;YACT,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAClB,CAAC,CAAC,CACL;YAED,qBAAqB;aACpB,IAAI,CACD,IAAI,EAAE,CACT;YAED,WAAW;aACV,IAAI,CACD,YAAY,CAAC,YAAY,CAAE,MAAM,EAAE,SAAS,SAAS,CAAyB,IAAW,EAAE,QAAgB,EAAE,QAAkB;YAC3H,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;YACvB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAEnC,UAAU;YACV,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;gBAC5B,QAAQ,EAAE,CAAA;gBACV,OAAM;aACT;YAED,UAAU,EAAE,CAAA;YACZ,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACtB,GAAG,GAAG,UAAU,GAAG,OAAO,CAC7B,CAAA;YACD,MAAM,IAAI,GAAG,aAAa,OAAO,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;YACxD,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,CAAC,CAAA;YAEtF,IAAI,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAA;YAEnC,IAAI,GAAG,KAAK,MAAM;gBACd,IAAI,GAAG,GAAG,CAAC,OAAO,CAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAS,CAAE,CAAC,QAAQ,EAAE,CAAA;YAG5G,8CAA8C;YAC9C,iFAAiF;YACjF,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,CAAA;YAElH,wCAAwC;YACxC,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,EAAE;gBAClC,iGAAiG;gBACjG,oCAAoC,CAAC,MAAM,CAAC,CAAA;gBAC5C,MAAM,CAAC,2BAA2B,CAC9B,IAAI,EACJ,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,EACvB,UAAU,EACV,CAAC,KAAY,EAAE,EAAE;oBACb,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAC9B,CAAC,CACJ,CAAA;aACJ;YAED,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;QAC3B,CAAC,CAAC,CACL;YAED,SAAS;aACR,IAAI,CACD,QAAQ,CAAC,GAAG;QACR;;UAEE;QACF,SAAS,KAAK,CAAmB,IAAW,EAAE,QAAgB,EAAE,EAAY,IAAI,EAAE,EAAE,CAAA,CAAC,CAAC;QAEtF,+DAA+D;QAC/D,SAAS,KAAK,CAAmB,EAAY;YACzC,0BAA0B;YAC1B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,EACjC,MAAM,CAAC,WAAW,CACd,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAE,CAAC,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,CAC/D,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAC/F,CACJ,CAAC,CAAA;YAGF,0BAA0B;YAC1B,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC;gBACvB,IAAI,EAAE;oBACF,IAAI;oBACJ,KAAK,CAAC,GAAG;oBACT,KAAK,CAAC,KAAK;iBACd;gBACD,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;gBAC/C,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;gBACnB,KAAK,EAAE;oBACH,GAAG,EAAE,EAAE;oBACP,SAAS,EAAE,EAAE;oBACb,UAAU,EAAE,EAAE;oBACd,WAAW,EAAE,EAAE;oBACf,MAAM,EAAE,EAAE;oBACV,YAAY,EAAE,EAAE;oBAChB,aAAa,EAAE,EAAE;oBACjB,cAAc,EAAE,EAAE;oBAClB,IAAI,EAAE,EAAE;oBACR,UAAU,EAAE,EAAE;oBACd,GAAG,EAAE,EAAE;oBACP,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,EAAE;oBACT,WAAW,EAAE,EAAE;oBACf,MAAM,EAAE,GAAG;iBACd;aACJ,CAAC,CAAA;YAEF,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;gBAC5C,KAAK,CAAC,IAAI,CAAC;oBACP,IAAI;oBACJ,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG;oBACnC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK;iBAC/B,CAAC,CAAA;YACb,CAAC,CAAC,CAAA;YAIF,OAAO,CAAC,IAAI,EAAE,CAAA;YACd,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,mBAAmB,cAAc,CAAC,MAAM,UAAU,CAAC,CAAA;YACjF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;YAG7B,kCAAkC;YAClC;;;;;;;;;;;;;;;;;;;;;;;;;cAyBE;YAEF,MAAM,gBAAgB,GAAG,KAAK,CAAC,EAAE,CAAC,aAAa,CAAA;YAC/C,IAAI,gBAAgB,CAAC,IAAI,EAAE;gBACvB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;gBAClC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACT,KAAK,MAAM,YAAY,IAAI,gBAAgB,EAAE;oBACzC,IAAI,CAAC,IAAI,EAAE;wBACP,MAAK;oBACT,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;oBACzB,CAAC,EAAE,CAAA;iBACN;gBACD,IAAI,gBAAgB,CAAC,IAAI,GAAG,EAAE,EAAE;oBAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;oBAClB,OAAO,CAAC,GAAG,CAAC,SAAS,gBAAgB,CAAC,IAAI,gBAAgB,CAAC,CAAA;iBAC9D;aACJ;;gBACG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAA;YAGxC,oDAAoD;YACpD,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;YAE1E,IAAI,aAAa,GAAU,EAAG,CAAA;YAE9B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE;gBACtC,IAAI,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAA;gBAC/B,IAAI,CAAC,EAAE,KAAK,EAAE,CAAA;gBACd,IAAI,CAAC,EAAE,KAAK,EAAE,CAAA;gBACd,IAAI,CAAC,EAAE,KAAK,EAAE,CAAA;gBACd,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;aAC5B;YAED,IAAI,CAAC,IAAI,CACL,cAAc,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAClD,CAAA;YAGD,4BAA4B;YAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;YACrD,IAAI,CAAC,IAAI,CACL,cAAc,CACV,WAAW,EACX,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAC5B,CACJ,CAAA;YAED,OAAO,CAAC,GAAG,CACP,OAAO,eAAe,CAAC,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,IAAI,IAAI;gBACnE,GAAG,eAAe,CAAC,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,IAAI;gBAC1D,IAAI;gBACJ,uEAAuE,CAAC,MAAM;gBAC9E,uFAAuF,CAAC,MAAM;gBAC9F,GAAG,WAAW,CAAC,MAAM,GAAG,wDAAwD,CAAC,IAAI,CAAC,SAAS,EAAE,CACpG,CAAA;YAED,EAAE,EAAE,CAAA;QACR,CAAC,CACJ,CACJ;YAED,SAAS;aACR,IAAI,CACD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CACpB;aAEA,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACZ,IAAI,cAAc,CAAC,MAAM,EAAE;gBACvB,KAAK,MAAM,aAAa,IAAI,cAAc;oBACtC,aAAa,EAAE,CAAA;gBAEnB,OAAO,CAAC,GAAG,CAAC,+BAA+B,yDAAyD,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;aACzH;YAED,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;IACV,CAAC,CAAC,CAAA;AACN,CAAC","sourcesContent":["import type { Transform } from 'stream'\n\nimport i18n_scanner from 'i18next-scanner'\nimport path from 'upath'\nimport ejs from 'ejs'\nimport vfs from 'vinyl-fs'\nimport sort from 'gulp-sort'\nimport ora from 'ora'\nimport cli_truncate from 'cli-truncate'\nimport Vinyl from 'vinyl'\nimport through2 from 'through2'\nimport CliTable from 'cli-table3'\n\nimport '../../prototype.js'\nimport { map_stream } from '../../utils.js'\n\nimport {\n LANGUAGES,\n type Language,\n} from '../index.js'\nimport { RWDict } from '../rwdict.js'\nimport type { _Dict } from '../dict.js'\nimport { try_load_dict } from '../utils.js'\n\n\nimport { mix_parse_trans_from_string_by_babel } from './parser.js'\n\n\n\n/** 默认 i18next 扫描配置 */\nconst DEFAULT_CONFIG = {\n debug: false,\n \n input: [\n // 'src/**/*.{js,jsx,ts,tsx}',\n '!i18n/**', // Use ! to filter out files or directories\n '!node_modules/**',\n '!**/*.d.ts',\n ],\n \n // 相对于根目录\n output: 'i18n/',\n \n // 若是相对路径,则以 output 为基准进行解析\n dict: ['dict.json', 'untranslateds.json'],\n \n lngs: ['zh', 'en', 'ja', 'ko'],\n ns: ['translation'],\n defaultLng: 'zh',\n defaultNs: 'translation',\n\n func: {\n list: [ 'i18next.t', 'i18n.t', 'i18n.__', 't', '__' ],\n extensions: [ ], // 避免在 transform 中执行原生的 parseFuncFromString\n },\n \n trans: {\n extensions: [ ], // 避免在 transform 中执行原生的 parseTransFromString\n fallbackKey: true,\n \n babylon: {\n sourceType: 'module',\n \n allowAwaitOutsideFunction: true,\n \n // https://babeljs.io/docs/en/babel-parser\n plugins: [\n // Language extensions\n 'jsx',\n 'typescript',\n \n // ECMAScript proposals\n 'classProperties',\n 'classPrivateProperties',\n 'classPrivateMethods',\n 'classStaticBlock',\n 'decimal',\n ['decorators', { decoratorsBeforeExport: true }],\n 'doExpressions',\n 'exportDefaultFrom',\n 'functionBind',\n 'importAssertions',\n 'moduleBlocks',\n 'moduleStringNames',\n 'partialApplication',\n ['pipelineOperator', { proposal: 'smart' }],\n 'privateIn',\n ['recordAndTuple', { syntaxType: 'bar' }],\n 'throwExpressions',\n 'topLevelAwait',\n ],\n } as import('@babel/parser').ParserOptions,\n \n // 实际并没有用到 acorn, 用了 babel\n acorn: {\n ecmaVersion: 'latest',\n sourceType: 'module', // defaults to 'module'\n // Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options\n }\n },\n \n // 禁用 : 和 . 作为 seperator\n keySeparator: false, // char to separate keys\n nsSeparator: false, // char to split namespace from key\n \n // Context Form\n context: true, // whether to add context form key\n contextFallback: true, // whether to add a fallback key as well as the context form key\n contextSeparator: '_', // char to split context from key\n\n // Plural\n // whether to add plural form key\n plural (language: string, ns: string, key: string, options: any /** Config */) {\n return language === 'en'\n }, \n pluralFallback: true, // whether to add a fallback key as well as the plural form key\n pluralSeparator: '_', // char to split plural from key\n \n // interpolation options\n interpolation: {\n prefix: '{{', // prefix for interpolation\n suffix: '}}' // suffix for interpolation\n }\n}\n\nconst VALID_EXTENTIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.ejs'])\n\nexport type Config = Partial<(typeof DEFAULT_CONFIG) & {\n defaultValue?: string\n resource?: { loadPath?: string, savePath?: string, jsonIndent?: number, lineEnding?: '\\n' }\n}>\n\n\n/** 扫描源码中的词条,以及收集未翻译的词条,将结果保存到 dict.json 和 untranslateds.json\n - `process.cwd()` rootdir 要扫描根目录\n - config 配置信息\n*/\nexport async function scanner (rootdir: string = path.normalize(process.cwd()), config: Config = { }) {\n const output = path.resolve(rootdir, config.output || DEFAULT_CONFIG.output)\n \n if (!config.input.length)\n throw new Error('运行 i18n-scan 请指定 --input')\n \n const input = [...config.input, ...DEFAULT_CONFIG.input]\n \n config = {\n ...DEFAULT_CONFIG,\n ...config,\n input,\n output,\n resource: {\n loadPath: '',\n savePath: path.resolve(output, 'translation/{{lng}}.js'),\n jsonIndent: 4,\n lineEnding: '\\n'\n }\n }\n \n let dict = new RWDict()\n \n for (const fp_dict of config.dict)\n dict.merge(\n await try_load_dict(\n path.resolve(output, fp_dict)\n ), { print: false, overwrite: true }\n )\n \n \n let c_files = 0\n let c_scanneds = 0\n let error_handlers = []\n \n // 所有语言的扫描统计信息\n let stats: Record<Language, { translateds: Set<string>, untranslateds: Set<string> }> = { } as any\n \n for (const language of LANGUAGES)\n stats[language] = {\n translateds: new Set<string>(),\n untranslateds: new Set<string>()\n }\n \n \n let spinner = ora({ interval: 66 }).start('Scanning...')\n \n \n \n function on_scanned (text: string, { language, key, defaultValue, count, context }: { language?: Language, key?: string, defaultValue?: string, count?: number, context?: string }) {\n // console.log(text, { language, key, defaultValue, count, context })\n \n text = text || defaultValue\n \n if (!key)\n key = context ? `${text}_${context}` : text\n \n if (!language) {\n for (const language of LANGUAGES)\n on_scanned(text, { language, key, count, context })\n return\n }\n \n // console.log(text, { language, key, defaultValue, count, context })\n // debugger\n \n const stat = stats[language]\n \n // 获取已有翻译\n const translation = \n dict.get(key, language) || \n language === 'zh' && text || \n ''\n \n if (language === 'zh' && !context)\n return\n \n if (translation)\n stat.translateds.add(key)\n else\n stat.untranslateds.add(key)\n \n if (language === 'en' && count !== undefined)\n on_scanned(text, { language, key: `${key}_plural`, context })\n }\n \n \n function new_vinyl_file (_path: string, data: string | object) {\n return new Vinyl({\n cwd: rootdir,\n base: rootdir,\n path: path.resolve(config.output, _path),\n contents: Buffer.from(typeof data === 'string' ? data : JSON.stringify(data, null, 4))\n })\n }\n \n return new Promise<number>((resolve, reject) => {\n // ------------ scan by file\n vfs\n .src(config.input, { cwd: rootdir, sync: false })\n \n // 每个文件扫描前,统计文件数量\n .pipe(\n map_stream((file: Vinyl, cb: Function) => {\n // 支持 `// @i18n-noscan` 忽略扫描\n if (/\\/\\/\\s*@i18n-noscan\\s/.test(file.contents.toString()))\n return cb()\n c_files++\n cb(null, file)\n })\n )\n \n // 对文件进行排序,保证词条有一定的顺序\n .pipe(\n sort()\n )\n \n // 分析代码提取词条\n .pipe(\n i18n_scanner.createStream( config, function transform (this: { parser: any }, file: Vinyl, encoding: string, callback: Function): void {\n const { parser } = this\n const ext = path.extname(file.path)\n \n // 只扫描源码文件\n if (!VALID_EXTENTIONS.has(ext)) {\n callback()\n return\n }\n \n c_scanneds++\n const percent = Math.round(\n 100 * c_scanneds / c_files\n )\n const text = `Scanning (${percent}%): ${file.path.blue}`\n spinner.text = cli_truncate(text, process.stdout.columns - 5, { position: 'middle', })\n \n let code = file.contents.toString()\n \n if (ext === '.ejs')\n code = ejs.compile( code, { filename: file.path, client: true, legacyInclude: true } as any ).toString()\n \n \n // --- 添加代码中扫描到的 i18n.t('key') 中的 key 到 parser\n // parser.parseFuncFromString 使用 esprima 来解析代码,esprima 仍然不支持 optional chaining !!\n parser.parseFuncFromString(code.replace(/\\?\\.\\[/g, '[').replace(/\\?\\.\\(/g, '(').replace(/\\?\\./g, '.'), on_scanned)\n \n // --- 添加代码中扫描到的 Trans 组件中的 key 到 parser\n if (ext === '.jsx' || ext === '.tsx') {\n // parser.parseTransFromString 使用 acorn 解析代码,不支持 TypeScript,添加 parser.parseTransFromStringByBabel\n mix_parse_trans_from_string_by_babel(parser)\n parser.parseTransFromStringByBabel(\n code,\n { filepath: file.path },\n on_scanned,\n (error: Error) => {\n error_handlers.push(error)\n }\n )\n }\n \n setTimeout(callback, 0)\n })\n )\n \n // 创建词条文件\n .pipe(\n through2.obj(\n /** i18n-scanner 会把扫描结果以每个语言一个文件的形式提供,这里解析扫描结果\n * file: 翻译 resource 文件,其中 file.contents 包含翻译的扫描结果\n */\n function write (this: Transform, file: Vinyl, encoding: string, cb: Function) { cb() },\n \n /** 生成 stats.json, unmarkeds.md; 打印 untranslated / unmarkeds */\n function flush (this: Transform, cb: Function) {\n // ------------ stats.json\n this.push(new_vinyl_file('stats.json', \n Object.fromEntries(\n Object.entries(stats).map( ([l, { translateds, untranslateds }]) => \n [l, { translateds: Array.from(translateds), untranslateds: Array.from(untranslateds) }])\n )\n ))\n \n \n // ------------ 打印 cli 统计表\n const table = new CliTable({\n head: [\n '语言',\n '未翻译'.red,\n '已翻译'.green,\n ],\n colAligns: ['right', 'right', 'right', 'right'],\n style: { head: [] },\n chars: {\n top: '',\n 'top-mid': '',\n 'top-left': '',\n 'top-right': '',\n bottom: '',\n 'bottom-mid': '',\n 'bottom-left': '',\n 'bottom-right': '',\n left: '',\n 'left-mid': '',\n mid: '',\n 'mid-mid': '',\n right: '',\n 'right-mid': '',\n middle: ' ',\n },\n })\n \n Object.entries(stats).forEach( ([lang, stat]) => {\n table.push([\n lang, \n String(stat.untranslateds.size).red, \n String(stat.translateds.size).green\n ] as any)\n })\n \n \n \n spinner.stop()\n console.log(`Scanned ${c_files} files. Occured ${error_handlers.length} errors.`)\n console.log(table.toString())\n \n \n // ------------ 生成 unmarkeds.md 统计\n /*\n const fp_unmarked = path.resolve(config.output, 'unmarkeds.md')\n \n if (fs.existsSync(fp_unmarked))\n rimraf.sync(fp_unmarked)\n \n if (unmarkeds.length) {\n console.log(colors.yellow(`\\n⚠️ 发现未标记的中文字符 ${unmarkeds.length} 处:\\n`))\n unmarkeds.forEach(({ value, filepath, loc: { start } }, index) => {\n if (index >= 5) return\n console.log( ` ${colors.white(`'${value}'`)}\\t${colors.blue.underline(`${path.relative(rootdir, filepath)}:${start.line}:${start.column + 1}`)}` )\n })\n }\n \n this.push( new_vinyl_file( fp_unmarked, \n unmarkeds.map( ({ value, filepath, loc }) =>\n '- [' + value.trim() + '](' + path.relative( config.output, path.resolve(rootdir, filepath || '') ) + '#L' + loc.start.line + ')'\n ).join('\\n') + '\\n'\n ))\n \n \n if (unmarkeds.length > 5) {\n console.log(' ...')\n console.log(colors.yellow(`\\n 完整未标记词条请查看 ${colors.blue.underline(path.relative(rootdir, fp_unmarked))}`))\n }\n */\n \n const en_untranslateds = stats.en.untranslateds\n if (en_untranslateds.size) {\n console.log('\\n缺少英文翻译的词条:'.yellow)\n let i = 0\n for (const untranslated of en_untranslateds) {\n if (i >= 10)\n break\n console.log(untranslated)\n i++\n }\n if (en_untranslateds.size > 10) {\n console.log('...')\n console.log(`--- 共 ${en_untranslateds.size} 个未翻译的英文词条 ---`)\n }\n } else\n console.log('\\n所有词条都至少含有英文翻译'.green)\n \n \n // ------------ 生成 untranslateds.json (扫描到词条还没有英文翻译)\n const fp_untranslateds = path.resolve(config.output, 'untranslateds.json')\n \n let untranslateds: _Dict = { }\n \n for (const key of stats.en.untranslateds) {\n let item = { ...dict.get(key) }\n item.en ||= ''\n item.ja ||= ''\n item.ko ||= ''\n untranslateds[key] = item\n }\n \n this.push(\n new_vinyl_file(fp_untranslateds, untranslateds)\n )\n \n \n // ------------ 写入 dict.json\n const fp_dict_new = path.resolve(output, 'dict.json')\n this.push(\n new_vinyl_file(\n fp_dict_new,\n dict.to_json(true) + '\\n'\n )\n )\n \n console.log(\n `\\n\\n${'请手动补全未翻译的词条: '.yellow}${fp_untranslateds.underline.blue}\\n` +\n `${'请检查新生成的词典文件: '.yellow}${fp_dict_new.underline.blue}\\n` +\n '\\n' +\n '补全 untranslateds.json 后需要重新运行扫描,会根据 untranslateds.json 更新 dict.json\\n'.yellow +\n '最后 dict.json 所包含的词条会被打包进 js, 通过 new I18N(<dict.json>) 或 i18n.init(<dict.json>) 加载\\n\\n'.yellow +\n `${'详细文档请查看: '.yellow}${'https://github.com/ShenHongFei/xshell/tree/master/i18n'.blue.underline}`\n )\n \n cb()\n }\n )\n )\n \n // 写入词条文件\n .pipe(\n vfs.dest(rootdir)\n )\n \n .on('end', () => {\n if (error_handlers.length) {\n for (const error_handler of error_handlers)\n error_handler()\n \n console.log(`以上错误可能是由不规范的词条标记导致,标记规范可见:\\n${'https://www.i18next.com/translation-function/essentials'.blue.underline}`)\n }\n \n resolve(stats.en.untranslateds.size)\n })\n })\n}\n"]}