xshell 0.0.53 → 0.0.56
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.
- package/chalk.browser.d.ts +1 -1
- package/chalk.browser.js +3 -19
- package/chalk.browser.js.map +1 -1
- package/file.d.ts +3 -3
- package/file.js +74 -97
- package/file.js.map +1 -1
- package/i18n/dict.js +1 -5
- package/i18n/dict.js.map +1 -1
- package/i18n/i18n-scan.js +9 -12
- package/i18n/i18n-scan.js.map +1 -1
- package/i18n/index.js +11 -16
- package/i18n/index.js.map +1 -1
- package/i18n/rwdict.js +3 -7
- package/i18n/rwdict.js.map +1 -1
- package/i18n/scanner/checker.js +6 -11
- package/i18n/scanner/checker.js.map +1 -1
- package/i18n/scanner/index.js +39 -44
- package/i18n/scanner/index.js.map +1 -1
- package/i18n/scanner/parser.js +15 -19
- package/i18n/scanner/parser.js.map +1 -1
- package/i18n/utils.js +3 -7
- package/i18n/utils.js.map +1 -1
- package/index.js +5 -8
- package/index.js.map +1 -1
- package/myfont.woff2 +0 -0
- package/myfontb.woff2 +0 -0
- package/net.browser.js +6 -13
- package/net.browser.js.map +1 -1
- package/net.d.ts +2 -2
- package/net.js +50 -63
- package/net.js.map +1 -1
- package/package.json +14 -14
- package/process.d.ts +3 -3
- package/process.js +18 -23
- package/process.js.map +1 -1
- package/prototype.browser.js +24 -32
- package/prototype.browser.js.map +1 -1
- package/prototype.d.ts +1 -1
- package/prototype.js +37 -46
- package/prototype.js.map +1 -1
- package/repl.js +66 -80
- package/repl.js.map +1 -1
- package/server.d.ts +3 -3
- package/server.js +32 -37
- package/server.js.map +1 -1
- package/toaster.browser.js +6 -9
- package/toaster.browser.js.map +1 -1
- package/toaster.sass +1 -1
- package/tsconfig.json +3 -3
- package/ufs.d.ts +1 -1
- package/ufs.js +5 -9
- package/ufs.js.map +1 -1
- package/utils.browser.js +4 -11
- package/utils.browser.js.map +1 -1
- package/utils.d.ts +3 -3
- package/utils.js +41 -62
- package/utils.js.map +1 -1
- package/xshell.js +2 -4
- package/xshell.js.map +1 -1
package/i18n/i18n-scan.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const commander_1 = require("commander");
|
|
7
|
-
const index_js_1 = require("./scanner/index.js");
|
|
8
|
-
const utils_js_1 = require("./utils.js");
|
|
2
|
+
import path from 'upath';
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { scanner } from './scanner/index.js';
|
|
5
|
+
import { try_load_dict } from './utils.js';
|
|
9
6
|
(async function main() {
|
|
10
|
-
|
|
11
|
-
.option('-r, --rootdir [rootdir]', '根目录:默认为当前工作目录',
|
|
7
|
+
program.name('i18n-scan')
|
|
8
|
+
.option('-r, --rootdir [rootdir]', '根目录:默认为当前工作目录', path.normalize(process.cwd()))
|
|
12
9
|
.option('-i, --input [input]', '扫描 pattern:多个 pattern 用分号分割,采用 glob pattern 匹配,如 `src/**/*.{js,jsx,ts,tsx}`', v => v.split(';'))
|
|
13
10
|
.option('-o, --output [output]', 'i18n 目录:默认为 <rootdir>/i18n/')
|
|
14
11
|
.option('-c, --config [config]', '自定义配置文件,默认为 <rootdir>/i18n/config.js ,可参考默认配置 xshell/i18n/index.ts 以及 https://github.com/i18next/i18next-scanner', 'i18n/config.js')
|
|
15
12
|
.parse(process.argv);
|
|
16
|
-
const { rootdir, config, input, output } =
|
|
17
|
-
|
|
18
|
-
...await
|
|
13
|
+
const { rootdir, config, input, output } = program.opts();
|
|
14
|
+
scanner(rootdir, {
|
|
15
|
+
...await try_load_dict(path.resolve(rootdir, config)),
|
|
19
16
|
...input ? { input } : {},
|
|
20
17
|
...output ? { output } : {},
|
|
21
18
|
});
|
package/i18n/i18n-scan.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n-scan.js","sourceRoot":"","sources":["i18n-scan.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"i18n-scan.js","sourceRoot":"","sources":["i18n-scan.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,OAAO,CAAA;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAEzC;AAAA,CAAC,KAAK,UAAU,IAAI;IACjB,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC;SACpB,MAAM,CAAC,yBAAyB,EAAK,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;SACpF,MAAM,CAAC,qBAAqB,EAAS,6EAA6E,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;SACtI,MAAM,CAAC,uBAAuB,EAAO,6BAA6B,CAAC;SACnE,MAAM,CAAC,uBAAuB,EAAO,kHAAkH,EAAE,gBAAgB,CAAC;SAC1K,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAExB,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IACzD,OAAO,CAAC,OAAO,EAAE;QACb,GAAI,MAAM,aAAa,CACnB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAChC;QACD,GAAI,KAAK,CAAE,CAAC,CAAE,EAAE,KAAK,EAAE,CAAG,CAAC,CAAE,EAAG;QAChC,GAAI,MAAM,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,CAAE,CAAC,CAAE,EAAG;KACnC,CAAC,CAAA;AACN,CAAC,CAAC,EAAE,CAAA","sourcesContent":["#!/usr/bin/env node\n\nimport path from 'upath'\nimport { program } from 'commander'\n\nimport { scanner } from './scanner/index.js'\nimport { try_load_dict } from './utils.js'\n\n;(async function main () {\n program.name('i18n-scan')\n .option('-r, --rootdir [rootdir]' , '根目录:默认为当前工作目录', path.normalize(process.cwd()))\n .option('-i, --input [input]' , '扫描 pattern:多个 pattern 用分号分割,采用 glob pattern 匹配,如 `src/**/*.{js,jsx,ts,tsx}`', v => v.split(';'))\n .option('-o, --output [output]' , 'i18n 目录:默认为 <rootdir>/i18n/')\n .option('-c, --config [config]' , '自定义配置文件,默认为 <rootdir>/i18n/config.js ,可参考默认配置 xshell/i18n/index.ts 以及 https://github.com/i18next/i18next-scanner', 'i18n/config.js')\n .parse(process.argv)\n \n const { rootdir, config, input, output } = program.opts()\n scanner(rootdir, {\n ... await try_load_dict(\n path.resolve(rootdir, config)\n ),\n ... input ? { input } : { },\n ... output ? { output } : { },\n })\n})()\n"]}
|
package/i18n/index.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
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
|
|
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 =
|
|
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 ||
|
|
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 =
|
|
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
|
|
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":"
|
|
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
|
-
|
|
2
|
-
|
|
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
|
|
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
|
package/i18n/rwdict.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rwdict.js","sourceRoot":"","sources":["rwdict.ts"],"names":[],"mappings":"
|
|
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"]}
|
package/i18n/scanner/checker.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"
|
|
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"]}
|
package/i18n/scanner/index.js
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 =
|
|
107
|
-
const 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:
|
|
114
|
+
savePath: path.resolve(output, 'translation/{{lng}}.js'),
|
|
119
115
|
jsonIndent: 4,
|
|
120
116
|
lineEnding: '\n'
|
|
121
117
|
}
|
|
122
118
|
};
|
|
123
|
-
let dict = new
|
|
119
|
+
let dict = new RWDict();
|
|
124
120
|
for (const fp_dict of config.dict)
|
|
125
|
-
dict.merge(await
|
|
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
|
|
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 = (
|
|
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
|
|
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
|
|
160
|
+
return new Vinyl({
|
|
165
161
|
cwd: rootdir,
|
|
166
162
|
base: rootdir,
|
|
167
|
-
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
|
-
|
|
169
|
+
vfs
|
|
174
170
|
.src(config.input, { cwd: rootdir, sync: false })
|
|
175
171
|
// 每个文件扫描前,统计文件数量
|
|
176
|
-
.pipe(
|
|
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((
|
|
180
|
+
.pipe(sort())
|
|
185
181
|
// 分析代码提取词条
|
|
186
|
-
.pipe(
|
|
182
|
+
.pipe(i18n_scanner.createStream(config, function transform(file, encoding, callback) {
|
|
187
183
|
const { parser } = this;
|
|
188
|
-
const ext =
|
|
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 = (
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|