i18n-easy 1.0.0
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/README.md +527 -0
- package/esm/assets/const.js +7 -0
- package/esm/ast_transformer.d.ts +19 -0
- package/esm/ast_transformer.js +54 -0
- package/esm/eslint/react/i18n_easy_plugin.d.ts +3 -0
- package/esm/eslint/react/i18n_easy_plugin.js +111 -0
- package/esm/eslint/react/i18n_utils.d.ts +61 -0
- package/esm/eslint/react/i18n_utils.js +115 -0
- package/esm/eslint/react/report_and_fix.d.ts +67 -0
- package/esm/eslint/react/report_and_fix.js +118 -0
- package/esm/eslint/vue/i18n_easy_plugin.d.ts +5 -0
- package/esm/eslint/vue/i18n_easy_plugin.js +141 -0
- package/esm/eslint/vue/i18n_utils.d.ts +65 -0
- package/esm/eslint/vue/i18n_utils.js +133 -0
- package/esm/eslint/vue/report_and_fix.d.ts +8 -0
- package/esm/eslint/vue/report_and_fix.js +169 -0
- package/esm/file_processor_utils.d.ts +14 -0
- package/esm/file_processor_utils.js +68 -0
- package/esm/i18n_easy.d.ts +77 -0
- package/esm/i18n_easy.js +171 -0
- package/esm/index.d.ts +4 -0
- package/esm/index.js +9 -0
- package/esm/key_map.d.ts +14 -0
- package/esm/key_map.js +51 -0
- package/esm/patch.d.ts +5 -0
- package/esm/progressBus.d.ts +7 -0
- package/esm/progressBus.js +25 -0
- package/esm/translate.d.ts +34 -0
- package/esm/translate.js +107 -0
- package/esm/type.d.ts +94 -0
- package/esm/type.js +0 -0
- package/esm/utils.d.ts +54 -0
- package/esm/utils.js +102 -0
- package/lib/assets/const.js +32 -0
- package/lib/ast_transformer.js +88 -0
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +5 -0
- package/lib/eslint/react/i18n_easy_plugin.js +131 -0
- package/lib/eslint/react/i18n_utils.js +145 -0
- package/lib/eslint/react/report_and_fix.js +156 -0
- package/lib/eslint/vue/i18n_easy_plugin.js +161 -0
- package/lib/eslint/vue/i18n_utils.js +164 -0
- package/lib/eslint/vue/report_and_fix.js +203 -0
- package/lib/file_processor_utils.js +109 -0
- package/lib/i18n_easy.js +205 -0
- package/lib/index.js +35 -0
- package/lib/key_map.js +71 -0
- package/lib/package.json +3 -0
- package/lib/patch.d.ts +5 -0
- package/lib/progressBus.js +50 -0
- package/lib/translate.js +127 -0
- package/lib/type.js +17 -0
- package/lib/utils.js +142 -0
- package/package.json +68 -0
- package/types/assets/const.d.ts +2 -0
- package/types/ast_transformer.d.ts +19 -0
- package/types/eslint/react/i18n_easy_plugin.d.ts +3 -0
- package/types/eslint/react/i18n_utils.d.ts +61 -0
- package/types/eslint/react/report_and_fix.d.ts +67 -0
- package/types/eslint/vue/i18n_easy_plugin.d.ts +5 -0
- package/types/eslint/vue/i18n_utils.d.ts +65 -0
- package/types/eslint/vue/report_and_fix.d.ts +8 -0
- package/types/file_processor_utils.d.ts +14 -0
- package/types/i18n_easy.d.ts +77 -0
- package/types/index.d.ts +4 -0
- package/types/key_map.d.ts +14 -0
- package/types/progressBus.d.ts +6 -0
- package/types/translate.d.ts +34 -0
- package/types/type.d.ts +94 -0
- package/types/utils.d.ts +54 -0
package/esm/key_map.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/key_map.ts
|
|
2
|
+
import { generateKeyByKeyType } from "./utils.js";
|
|
3
|
+
var KeyMap = class _KeyMap {
|
|
4
|
+
// 字符映射表
|
|
5
|
+
charMap = /* @__PURE__ */ new Map();
|
|
6
|
+
// 生成的国际化文件key类型
|
|
7
|
+
keyType = "pinyin";
|
|
8
|
+
static instance;
|
|
9
|
+
constructor() {
|
|
10
|
+
}
|
|
11
|
+
// 获取单例实例
|
|
12
|
+
static getInstance() {
|
|
13
|
+
if (!this.instance) {
|
|
14
|
+
this.instance = new _KeyMap();
|
|
15
|
+
}
|
|
16
|
+
return this.instance;
|
|
17
|
+
}
|
|
18
|
+
// 获取字符映射表
|
|
19
|
+
get charList() {
|
|
20
|
+
return this.charMap;
|
|
21
|
+
}
|
|
22
|
+
// 将中文转换成拼音
|
|
23
|
+
generateKey(str) {
|
|
24
|
+
const trimStr = str.trim();
|
|
25
|
+
const key = generateKeyByKeyType(trimStr, this.keyType);
|
|
26
|
+
this.setCharMap(key, trimStr);
|
|
27
|
+
return key;
|
|
28
|
+
}
|
|
29
|
+
// 清空字符映射表
|
|
30
|
+
clearCharMap() {
|
|
31
|
+
this.charMap.clear();
|
|
32
|
+
}
|
|
33
|
+
// 设置字符映射表
|
|
34
|
+
setCharMap(key, value) {
|
|
35
|
+
if (this.charMap.has(key)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.charMap.set(key, value);
|
|
39
|
+
}
|
|
40
|
+
// 判断字符映射表是否为空
|
|
41
|
+
isEmpty() {
|
|
42
|
+
return this.charMap.size === 0;
|
|
43
|
+
}
|
|
44
|
+
// 设置映射表类型
|
|
45
|
+
setKeyType(type) {
|
|
46
|
+
this.keyType = type;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
KeyMap as default
|
|
51
|
+
};
|
package/esm/patch.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { LogStatus } from './type.js';
|
|
4
|
+
/** 全局单例事件总线 */
|
|
5
|
+
export declare const progressBus: EventEmitter<[never]>;
|
|
6
|
+
/** 内部通用日志函数:既 console.log,也 emit */
|
|
7
|
+
export declare function log(msg: string, status: LogStatus): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/progressBus.ts
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
var colors = {
|
|
4
|
+
reset: "\x1B[0m",
|
|
5
|
+
red: "\x1B[31m",
|
|
6
|
+
green: "\x1B[32m",
|
|
7
|
+
blue: "\x1B[34m"
|
|
8
|
+
};
|
|
9
|
+
var statusColorMap = {
|
|
10
|
+
fail: colors.red,
|
|
11
|
+
info: colors.blue,
|
|
12
|
+
success: colors.green
|
|
13
|
+
};
|
|
14
|
+
var progressBus = new EventEmitter();
|
|
15
|
+
function colorLog(msg, status) {
|
|
16
|
+
console.log(`${statusColorMap[status]}${msg}${colors.reset}`);
|
|
17
|
+
}
|
|
18
|
+
function log(msg, status) {
|
|
19
|
+
colorLog(msg, status);
|
|
20
|
+
progressBus.emit("log", msg, status);
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
log,
|
|
24
|
+
progressBus
|
|
25
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export default class Translate {
|
|
2
|
+
private endpoint;
|
|
3
|
+
private apiKey;
|
|
4
|
+
private static instance;
|
|
5
|
+
private constructor();
|
|
6
|
+
static getInstance(): Translate;
|
|
7
|
+
/**
|
|
8
|
+
* 构建微软翻译API请求配置
|
|
9
|
+
* @param originalTextArr 原始文本数组,每个元素为{ text: string }
|
|
10
|
+
* @param lang 目标语言数组
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
private buildRequestConfig;
|
|
14
|
+
/**
|
|
15
|
+
* 处理微软翻译API响应数据
|
|
16
|
+
* @param responseData 翻译API响应数据,每个元素为{ translations: { text: string }[] }
|
|
17
|
+
* @param originalTextArr 原始文本数组,每个元素为[ key: string, text: string ]
|
|
18
|
+
* @param lang 目标语言数组
|
|
19
|
+
* @returns 翻译结果,格式为{ language: { key: text } }
|
|
20
|
+
*/
|
|
21
|
+
private processResponse;
|
|
22
|
+
/**
|
|
23
|
+
* 翻译中文为其他指定语言
|
|
24
|
+
* @param originalTextArr 原始文本数组,每个元素为[ key: string, text: string ]
|
|
25
|
+
* @param lang 目标语言数组
|
|
26
|
+
* @returns 翻译结果,格式为{ language: { key: text } }
|
|
27
|
+
*/
|
|
28
|
+
translate(originalTextArr: [string, string][], lang: string[]): Promise<Record<string, Record<string, string>>>;
|
|
29
|
+
/**
|
|
30
|
+
* 设置微软翻译API密钥
|
|
31
|
+
* @param apiKey 微软翻译API密钥
|
|
32
|
+
*/
|
|
33
|
+
setApiKey(apiKey: string): void;
|
|
34
|
+
}
|
package/esm/translate.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/translate.ts
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { customTranslation } from "./utils.js";
|
|
4
|
+
var Translate = class _Translate {
|
|
5
|
+
// 微软翻译API
|
|
6
|
+
endpoint = "https://api.cognitive.microsofttranslator.com/translate";
|
|
7
|
+
// 微软翻译API密钥
|
|
8
|
+
apiKey;
|
|
9
|
+
// 单例实例
|
|
10
|
+
static instance;
|
|
11
|
+
constructor() {
|
|
12
|
+
}
|
|
13
|
+
// 获取单例实例
|
|
14
|
+
static getInstance() {
|
|
15
|
+
if (!this.instance) {
|
|
16
|
+
this.instance = new _Translate();
|
|
17
|
+
}
|
|
18
|
+
return this.instance;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 构建微软翻译API请求配置
|
|
22
|
+
* @param originalTextArr 原始文本数组,每个元素为{ text: string }
|
|
23
|
+
* @param lang 目标语言数组
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
buildRequestConfig(originalTextArr, lang) {
|
|
27
|
+
const url = new URL(this.endpoint);
|
|
28
|
+
url.searchParams.append("api-version", "3.0");
|
|
29
|
+
url.searchParams.append("from", "zh");
|
|
30
|
+
lang.forEach((toLang) => url.searchParams.append("to", toLang));
|
|
31
|
+
return {
|
|
32
|
+
url,
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: {
|
|
35
|
+
"Ocp-Apim-Subscription-Key": this.apiKey,
|
|
36
|
+
"Ocp-Apim-Subscription-Region": "global",
|
|
37
|
+
"Content-type": "application/json",
|
|
38
|
+
"X-ClientTraceId": uuidv4().toString()
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify(originalTextArr)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 处理微软翻译API响应数据
|
|
45
|
+
* @param responseData 翻译API响应数据,每个元素为{ translations: { text: string }[] }
|
|
46
|
+
* @param originalTextArr 原始文本数组,每个元素为[ key: string, text: string ]
|
|
47
|
+
* @param lang 目标语言数组
|
|
48
|
+
* @returns 翻译结果,格式为{ language: { key: text } }
|
|
49
|
+
*/
|
|
50
|
+
processResponse(responseData, originalTextArr, lang) {
|
|
51
|
+
const translationResult = {};
|
|
52
|
+
responseData.forEach((arrItem, i) => {
|
|
53
|
+
const key = originalTextArr[i][0];
|
|
54
|
+
arrItem.translations.forEach((item, j) => {
|
|
55
|
+
const language = lang[j];
|
|
56
|
+
translationResult[language] = translationResult[language] || {};
|
|
57
|
+
translationResult[language][key] = item.text;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
return translationResult;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 翻译中文为其他指定语言
|
|
64
|
+
* @param originalTextArr 原始文本数组,每个元素为[ key: string, text: string ]
|
|
65
|
+
* @param lang 目标语言数组
|
|
66
|
+
* @returns 翻译结果,格式为{ language: { key: text } }
|
|
67
|
+
*/
|
|
68
|
+
async translate(originalTextArr, lang) {
|
|
69
|
+
if (!this.apiKey) {
|
|
70
|
+
throw new Error("配置文件缺失配置项:微软翻译apiKey不能为空");
|
|
71
|
+
}
|
|
72
|
+
if (!lang.length) {
|
|
73
|
+
throw new Error("配置文件缺失配置项:目标语言lang不能为空");
|
|
74
|
+
}
|
|
75
|
+
if (!originalTextArr.length) {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
const textArr = originalTextArr.map(([_, text]) => {
|
|
79
|
+
if (!text.includes("{") && !text.includes("}")) {
|
|
80
|
+
return { text };
|
|
81
|
+
}
|
|
82
|
+
const str = text.replace(/({\s*arg[0-9]\s*})/g, (str2) => customTranslation(str2, str2));
|
|
83
|
+
return { text: str };
|
|
84
|
+
});
|
|
85
|
+
const { url, ...config } = this.buildRequestConfig(textArr, lang);
|
|
86
|
+
try {
|
|
87
|
+
const response = await fetch(url, config);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`翻译API请求失败,响应状态码:${response.status}`);
|
|
90
|
+
}
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
return this.processResponse(data, originalTextArr, lang);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw new Error(error instanceof Error ? error.message : String(error));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 设置微软翻译API密钥
|
|
99
|
+
* @param apiKey 微软翻译API密钥
|
|
100
|
+
*/
|
|
101
|
+
setApiKey(apiKey) {
|
|
102
|
+
this.apiKey = apiKey;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
export {
|
|
106
|
+
Translate as default
|
|
107
|
+
};
|
package/esm/type.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { AST } from 'vue-eslint-parser';
|
|
2
|
+
import * as ESTree from 'estree';
|
|
3
|
+
import type { Rule } from 'eslint';
|
|
4
|
+
export declare type Framework = 'vue' | 'react';
|
|
5
|
+
export declare type BaseCodeType = 'vue' | 'js' | 'jsx' | 'ts' | 'tsx' | 'json' | 'template' | 'script' | 'scriptSetup';
|
|
6
|
+
export interface SupportFileMap {
|
|
7
|
+
vue: Extract<BaseCodeType, 'vue' | 'js' | 'jsx' | 'ts' | 'tsx'>;
|
|
8
|
+
react: Extract<BaseCodeType, 'js' | 'jsx' | 'ts' | 'tsx'>;
|
|
9
|
+
}
|
|
10
|
+
export interface I18nInfoType {
|
|
11
|
+
template?: {
|
|
12
|
+
callMethodName: string;
|
|
13
|
+
};
|
|
14
|
+
script?: {
|
|
15
|
+
callMethodName: string;
|
|
16
|
+
injectImport?: string;
|
|
17
|
+
injectCodeToTopLevel?: string;
|
|
18
|
+
};
|
|
19
|
+
scriptSetup?: {
|
|
20
|
+
callMethodName: string;
|
|
21
|
+
injectImport?: string;
|
|
22
|
+
injectCodeToTopLevel?: string;
|
|
23
|
+
injectHooksCall?: string;
|
|
24
|
+
};
|
|
25
|
+
js?: {
|
|
26
|
+
callMethodName: string;
|
|
27
|
+
injectImport?: string;
|
|
28
|
+
injectCodeToTopLevel?: string;
|
|
29
|
+
};
|
|
30
|
+
jsx?: {
|
|
31
|
+
callMethodName: string;
|
|
32
|
+
injectImport?: string;
|
|
33
|
+
injectCodeToTopLevel?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface LangType {
|
|
37
|
+
name: string;
|
|
38
|
+
i18nFilePath: string;
|
|
39
|
+
}
|
|
40
|
+
export interface JSXText extends ESTree.BaseNode, ESTree.BaseExpression {
|
|
41
|
+
type: 'JSXText';
|
|
42
|
+
value: string;
|
|
43
|
+
raw: string | undefined;
|
|
44
|
+
}
|
|
45
|
+
export declare type TextNode = AST.VText | AST.VAttribute | ESTree.TemplateLiteral | (ESTree.Literal & Rule.NodeParentExtension) | JSXText;
|
|
46
|
+
export declare type TextNodeType = 'VText' | 'VAttribute' | 'TemplateLiteral' | 'Literal' | 'JSXText';
|
|
47
|
+
export declare type SetUpNode = ESTree.FunctionExpression | ESTree.ArrowFunctionExpression | ESTree.Program | null;
|
|
48
|
+
export declare type CodeType = Extract<BaseCodeType, 'js' | 'jsx' | 'script' | 'scriptSetup' | 'template'>;
|
|
49
|
+
interface CollectItemType {
|
|
50
|
+
textNode: TextNode;
|
|
51
|
+
codeType: Extract<BaseCodeType, 'js' | 'jsx' | 'script' | 'template'>;
|
|
52
|
+
setUpNode: SetUpNode;
|
|
53
|
+
inSetup: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface CollectListType {
|
|
56
|
+
templateTextNodeList: CollectItemType[];
|
|
57
|
+
scriptTextNodeList: CollectItemType[];
|
|
58
|
+
jsTextNodeList: CollectItemType[];
|
|
59
|
+
jsxTextNodeList: CollectItemType[];
|
|
60
|
+
}
|
|
61
|
+
export interface NodeListener extends Rule.NodeListener {
|
|
62
|
+
VText: (node: AST.VText) => void;
|
|
63
|
+
VAttribute: (node: AST.VAttribute) => void;
|
|
64
|
+
JSXText: (node: JSXText) => void;
|
|
65
|
+
[key: string]: ((...args: any[]) => void) | undefined;
|
|
66
|
+
}
|
|
67
|
+
export interface ReactCollectItemType {
|
|
68
|
+
textNode: ESTree.TemplateLiteral | (ESTree.Literal & Rule.NodeParentExtension) | JSXText;
|
|
69
|
+
componentNode: ESTree.FunctionDeclaration | ESTree.VariableDeclarator | null;
|
|
70
|
+
programNode: ESTree.Program | null;
|
|
71
|
+
inComp: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface ReactCollectListType {
|
|
74
|
+
inComponent: ReactCollectItemType[];
|
|
75
|
+
outComponent: ReactCollectItemType[];
|
|
76
|
+
}
|
|
77
|
+
export declare type ReactInjectCodeType = ESTree.FunctionDeclaration | ESTree.VariableDeclarator | ESTree.Program;
|
|
78
|
+
export interface ReactI18nInfoType {
|
|
79
|
+
js?: {
|
|
80
|
+
callMethodName: string;
|
|
81
|
+
injectImport?: string;
|
|
82
|
+
injectCodeToTopLevel?: string;
|
|
83
|
+
};
|
|
84
|
+
jsx?: {
|
|
85
|
+
callMethodName: string;
|
|
86
|
+
injectImport?: string;
|
|
87
|
+
injectCodeToTopLevel?: string;
|
|
88
|
+
injectHooksCall?: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export declare type ReactCodeType = Extract<BaseCodeType, 'js' | 'jsx'>;
|
|
92
|
+
export declare type LogStatus = 'success' | 'info' | 'fail';
|
|
93
|
+
export declare type KeyType = 'hash' | 'pinyin' | 'pinyinWithNumber';
|
|
94
|
+
export {};
|
package/esm/type.js
ADDED
|
File without changes
|
package/esm/utils.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Framework, SupportFileMap, KeyType, LangType, I18nInfoType, ReactI18nInfoType, TextNode, ReactCollectItemType } from './type.js';
|
|
2
|
+
import type { Rule } from 'eslint';
|
|
3
|
+
export interface Config<T extends Framework> {
|
|
4
|
+
projects: {
|
|
5
|
+
projectPath: string;
|
|
6
|
+
lang: LangType[];
|
|
7
|
+
i18nInfo: T extends 'vue' ? I18nInfoType : ReactI18nInfoType;
|
|
8
|
+
keyType: KeyType;
|
|
9
|
+
type: T;
|
|
10
|
+
customIgnore?: (node: T extends 'vue' ? TextNode : ReactCollectItemType['textNode'], context: Rule.RuleContext) => boolean;
|
|
11
|
+
}[];
|
|
12
|
+
apiKey: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @description 判断字符串是否包含中文
|
|
16
|
+
* @param str 字符串
|
|
17
|
+
* @returns {boolean} 是否包含中文
|
|
18
|
+
*/
|
|
19
|
+
export declare function hasChineseChar(str: string | undefined | null): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* @description 生成对应字符串的 hash 值或拼音值
|
|
22
|
+
* @param char 字符串
|
|
23
|
+
* @param keyType 键类型
|
|
24
|
+
* @returns {string} hash 值或拼音值
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateKeyByKeyType(char: string, keyType: KeyType): string;
|
|
27
|
+
/**
|
|
28
|
+
* @description 判断是否为vue文件的script src导入
|
|
29
|
+
* @param filePath 文件路径
|
|
30
|
+
* @param fileType 文件类型
|
|
31
|
+
* @returns {Promise<boolean>} 是否为vue文件的script src导入
|
|
32
|
+
*/
|
|
33
|
+
export declare function isVueScriptSrcImport(filePath: string, fileType: string): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* @description 判断文件类型是否支持转换
|
|
36
|
+
* @param fileType 文件类型
|
|
37
|
+
* @param framework 框架类型
|
|
38
|
+
* @returns {boolean} 是否支持转换
|
|
39
|
+
*/
|
|
40
|
+
export declare function isSupportFileType<F extends Framework>(fileType: string, framework: F): fileType is SupportFileMap[F];
|
|
41
|
+
/**
|
|
42
|
+
* @description 在指定目录下查重指定名称文件,并加载文件导出内容
|
|
43
|
+
* @param dirPath 目录路径
|
|
44
|
+
* @param searchPlaces 文件名称列表
|
|
45
|
+
* @returns {Promise<any | null>} 文件内容
|
|
46
|
+
*/
|
|
47
|
+
export declare function searchFileContent<T extends Framework>(dirPath: string, searchPlaces: string[]): Promise<Config<T> | null>;
|
|
48
|
+
/**
|
|
49
|
+
* @description 配置辅助函数
|
|
50
|
+
* @param config 配置项
|
|
51
|
+
* @returns 配置项
|
|
52
|
+
*/
|
|
53
|
+
export declare function defineConfig<T extends Framework>(config: Config<T>): Config<T>;
|
|
54
|
+
export declare function customTranslation(source: string, translation: string): string;
|
package/esm/utils.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import { glob } from "glob";
|
|
6
|
+
import { parse } from "@vue/compiler-sfc";
|
|
7
|
+
import { pinyin, addDict } from "pinyin-pro";
|
|
8
|
+
import ModernChineseDict from "@pinyin-pro/data/modern";
|
|
9
|
+
import { cosmiconfig, defaultLoaders } from "cosmiconfig";
|
|
10
|
+
addDict(ModernChineseDict);
|
|
11
|
+
function hasChineseChar(str) {
|
|
12
|
+
if (!str) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return /[\u4e00-\u9fa5]/.test(str);
|
|
16
|
+
}
|
|
17
|
+
function generateHash(char) {
|
|
18
|
+
const hash = createHash("md5");
|
|
19
|
+
hash.update(char);
|
|
20
|
+
return hash.digest("hex").slice();
|
|
21
|
+
}
|
|
22
|
+
function generatePinYin(char, toneType) {
|
|
23
|
+
const filtered = char.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
|
|
24
|
+
return pinyin(filtered, {
|
|
25
|
+
separator: "_",
|
|
26
|
+
// 分隔符
|
|
27
|
+
toneType,
|
|
28
|
+
// 声调
|
|
29
|
+
nonZh: "consecutive",
|
|
30
|
+
// 非汉字在结果中紧凑输出
|
|
31
|
+
nonZhScope: /[a-zA-Z0-9]/
|
|
32
|
+
// 指定nonZh范围的正则
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function generateKeyByKeyType(char, keyType) {
|
|
36
|
+
if (keyType === "hash") {
|
|
37
|
+
return generateHash(char);
|
|
38
|
+
} else if (keyType === "pinyinWithNumber") {
|
|
39
|
+
return generatePinYin(char, "num");
|
|
40
|
+
} else {
|
|
41
|
+
return generatePinYin(char, "none");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function isVueScriptSrcImport(filePath, fileType) {
|
|
45
|
+
if (!["js", "ts"].includes(fileType)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const vueFiles = await glob("*.vue", { cwd: path.dirname(filePath), absolute: true, nodir: true });
|
|
49
|
+
for (const file of vueFiles) {
|
|
50
|
+
const code = await fs.readFile(file, "utf8");
|
|
51
|
+
const { descriptor } = parse(code);
|
|
52
|
+
const { script } = descriptor;
|
|
53
|
+
if ((script == null ? void 0 : script.src) && path.resolve(path.dirname(file), script.src) === filePath) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
function isSupportFileType(fileType, framework) {
|
|
60
|
+
const SUPPORT_FILE_TYPE_MAP = {
|
|
61
|
+
vue: /* @__PURE__ */ new Set(["vue", "js", "jsx", "ts", "tsx"]),
|
|
62
|
+
react: /* @__PURE__ */ new Set(["js", "jsx", "ts", "tsx"])
|
|
63
|
+
};
|
|
64
|
+
return SUPPORT_FILE_TYPE_MAP[framework].has(fileType);
|
|
65
|
+
}
|
|
66
|
+
async function searchFileContent(dirPath, searchPlaces) {
|
|
67
|
+
const explorer = cosmiconfig("", {
|
|
68
|
+
searchStrategy: "none",
|
|
69
|
+
searchPlaces,
|
|
70
|
+
loaders: {
|
|
71
|
+
".json": defaultLoaders[".json"],
|
|
72
|
+
// 保持默认 JSON 加载器
|
|
73
|
+
".js": defaultLoaders[".js"],
|
|
74
|
+
// 保持默认 JS 加载器
|
|
75
|
+
".mjs": defaultLoaders[".mjs"],
|
|
76
|
+
// 保持默认 MJS 加载器
|
|
77
|
+
".cjs": defaultLoaders[".cjs"],
|
|
78
|
+
// 保持默认 CJS 加载器
|
|
79
|
+
".ts": defaultLoaders[".ts"],
|
|
80
|
+
// 保持默认 TS 加载器'
|
|
81
|
+
noExt: defaultLoaders[".json"]
|
|
82
|
+
// 无扩展名文件作为 JSON 处理
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const result = await explorer.search(dirPath);
|
|
86
|
+
return result ? result.config : null;
|
|
87
|
+
}
|
|
88
|
+
function defineConfig(config) {
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
function customTranslation(source, translation) {
|
|
92
|
+
return `<mstrans:dictionary translation=${translation}>${source}</mstrans:dictionary>`;
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
customTranslation,
|
|
96
|
+
defineConfig,
|
|
97
|
+
generateKeyByKeyType,
|
|
98
|
+
hasChineseChar,
|
|
99
|
+
isSupportFileType,
|
|
100
|
+
isVueScriptSrcImport,
|
|
101
|
+
searchFileContent
|
|
102
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/assets/const.ts
|
|
20
|
+
var const_exports = {};
|
|
21
|
+
__export(const_exports, {
|
|
22
|
+
checkReg: () => checkReg,
|
|
23
|
+
configFileNames: () => configFileNames
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(const_exports);
|
|
26
|
+
var configFileNames = ["i18n_easy.mjs", "i18n_easy.cjs", "i18n_easy.js", "i18n_easy.ts", "i18n_easy.json"];
|
|
27
|
+
var checkReg = /(module\.exports\s*=\s*|export\s*default\s*)?({[\s\S]*})/;
|
|
28
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
29
|
+
0 && (module.exports = {
|
|
30
|
+
checkReg,
|
|
31
|
+
configFileNames
|
|
32
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/ast_transformer.ts
|
|
30
|
+
var ast_transformer_exports = {};
|
|
31
|
+
__export(ast_transformer_exports, {
|
|
32
|
+
AstTransformer: () => AstTransformer
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(ast_transformer_exports);
|
|
35
|
+
var import_eslint = require("eslint");
|
|
36
|
+
var import_i18n_easy_plugin = __toESM(require("./eslint/vue/i18n_easy_plugin.js"));
|
|
37
|
+
var import_i18n_easy_plugin2 = __toESM(require("./eslint/react/i18n_easy_plugin.js"));
|
|
38
|
+
var vueParser = __toESM(require("vue-eslint-parser"));
|
|
39
|
+
var espree = __toESM(require("espree"));
|
|
40
|
+
var import_parser = __toESM(require("@typescript-eslint/parser"));
|
|
41
|
+
var AstTransformer = class {
|
|
42
|
+
linter;
|
|
43
|
+
constructor() {
|
|
44
|
+
this.linter = new import_eslint.Linter();
|
|
45
|
+
}
|
|
46
|
+
// 转换文件内容
|
|
47
|
+
convertFileContent(props) {
|
|
48
|
+
const { fileContent, fileType, fileName, i18nInfo, type, customIgnore } = props;
|
|
49
|
+
const settings = { i18nInfo, fileType, customIgnore };
|
|
50
|
+
const parserOptions = {
|
|
51
|
+
ecmaFeatures: { jsx: true },
|
|
52
|
+
parser: { js: espree, jsx: espree, ts: import_parser.default, tsx: import_parser.default }
|
|
53
|
+
};
|
|
54
|
+
const vueConfig = {
|
|
55
|
+
files: ["*.vue", "*.js", "*.jsx", "*.ts", "*.tsx"],
|
|
56
|
+
plugins: { i18nEasyVue: import_i18n_easy_plugin.default },
|
|
57
|
+
languageOptions: {
|
|
58
|
+
parser: vueParser,
|
|
59
|
+
parserOptions
|
|
60
|
+
},
|
|
61
|
+
rules: {
|
|
62
|
+
"i18nEasyVue/i18n-transform": "error"
|
|
63
|
+
},
|
|
64
|
+
settings
|
|
65
|
+
};
|
|
66
|
+
const reactConfig = {
|
|
67
|
+
files: ["*.js", "*.jsx", "*.ts", "*.tsx"],
|
|
68
|
+
plugins: { i18nEasyReact: import_i18n_easy_plugin2.default },
|
|
69
|
+
languageOptions: {
|
|
70
|
+
parser: espree,
|
|
71
|
+
parserOptions
|
|
72
|
+
},
|
|
73
|
+
rules: { "i18nEasyReact/i18n-transform": "error" },
|
|
74
|
+
settings
|
|
75
|
+
};
|
|
76
|
+
const res = this.linter.verifyAndFix(fileContent, type == "vue" ? vueConfig : reactConfig, fileName);
|
|
77
|
+
return {
|
|
78
|
+
newFileContent: res.output,
|
|
79
|
+
// 转换后的文件内容(如果未应用修复,可能与输入内容相同)
|
|
80
|
+
fixed: res.fixed
|
|
81
|
+
// 是否应用了修复
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
86
|
+
0 && (module.exports = {
|
|
87
|
+
AstTransformer
|
|
88
|
+
});
|
package/lib/cli.d.ts
ADDED