flutter-i18n-generator 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evan(埃文)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Flutter I18n Generator
2
+
3
+ 基于 `$t()` 显式标记的 Flutter 国际化自动化工具,将翻译流程整合为**扫描 → 替换 → 生成 ARB** 三步。
4
+
5
+ ## 功能清单
6
+
7
+ | 步骤 | 说明 |
8
+ |------|------|
9
+ | **扫描** | 正则匹配 `$t('...')` 和 `$t("...")` 调用,支持插值参数 |
10
+ | **去重** | 相同模板文本生成同一个 key(`t` + MD5 前 12 位) |
11
+ | **ARB** | 按 `-l` 参数生成多个 `intl_<locale>.arb` 文件 |
12
+ | **替换** | 将 `$t('...')` 替换为 `S.current.xxx(...)` |
13
+ | **Import** | 自动添加 `S` 类 import,移除旧的 `i18n_marker` import |
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ npm install
19
+ npm run build
20
+ ```
21
+
22
+ ## 用法
23
+
24
+ ### 基本命令
25
+
26
+ ```bash
27
+ # 全量扫描(默认 lib/ 目录)
28
+ extractor
29
+
30
+ # 仅预览,不修改任何文件
31
+ extractor --dry-run
32
+
33
+ # 单文件处理
34
+ extractor -f lib/views/home.dart
35
+
36
+ # 指定扫描目录
37
+ extractor -d lib/views/
38
+
39
+ # 排除指定目录/文件
40
+ extractor -e "lib/mock,lib/test"
41
+ ```
42
+
43
+ ### 多语言 ARB 生成
44
+
45
+ 通过 `-l` 参数指定要生成的语言代码,第一个为**主语言**(写入模板原文),其余为**翻译占位**(写入空字符串):
46
+
47
+ ```bash
48
+ # 生成中文(主语言)+ 英文 + 日文 ARB 文件
49
+ extractor -l zh en ja
50
+ ```
51
+
52
+ 输出结果:
53
+
54
+ ```
55
+ lib/l10n/intl_zh.arb → {"t5eb63bbb": "消息设置", "t3f2a1b2": "接收系统通知", ...}
56
+ lib/l10n/intl_en.arb → {"t5eb63bbb": "", "t3f2a1b2": "", ...}
57
+ lib/l10n/intl_ja.arb → {"t5eb63bbb": "", "t3f2a1b2": "", ...}
58
+ ```
59
+
60
+ ### 指定输出目录
61
+
62
+ ```bash
63
+ extractor -o lib/l10n/ -l zh en ja
64
+ ```
65
+
66
+ ### 命令选项
67
+
68
+ | 选项 | 说明 | 默认值 |
69
+ |------|------|--------|
70
+ | `-d, --dir <dir>` | 扫描的目录路径 | `lib/` |
71
+ | `-f, --file <path>` | 仅处理指定的单个 `.dart` 文件 | — |
72
+ | `-o, --output <dir>` | ARB 文件输出目录 | `lib/l10n/` |
73
+ | `-e, --exclude <patterns>` | 排除的文件/目录,逗号分隔 | — |
74
+ | `-l, --locale <value...>` | 生成的语言代码 | `intl_en` |
75
+ | `--dry-run` | 仅预览匹配结果,不实际修改文件 | `false` |
76
+ | `-v, --version` | 显示版本号 | — |
77
+ | `-h, --help` | 显示帮助信息 | — |
78
+
79
+ ## 工作原理
80
+
81
+ ### 1. 标记文本
82
+
83
+ 在 Dart 代码中使用 `$t()` 包裹需要国际化的字符串:
84
+
85
+ ```dart
86
+ // 简单文本
87
+ Text($t('消息设置'))
88
+
89
+ // 带插值参数
90
+ Text($t('共{count}条', {'count': total}))
91
+ ```
92
+
93
+ ### 2. 运行工具
94
+
95
+ ```bash
96
+ extractor -l zh en
97
+ ```
98
+
99
+ ### 3. 自动替换
100
+
101
+ 工具会将源代码中的 `$t()` 调用替换为 Flutter Intl 的标准调用方式:
102
+
103
+ ```dart
104
+ // 替换前
105
+ Text($t('消息设置'))
106
+
107
+ // 替换后
108
+ Text(S.current.t5eb63bbb)
109
+ ```
110
+
111
+ 同时自动管理 import 语句:
112
+ - 移除 `import '...i18n_marker.dart'`
113
+ - 添加 `import 'package:flutter_app_plus/generated/l10n.dart'`
114
+
115
+ ### 4. 生成 ARB 文件
116
+
117
+ 在 IDE 中右键 `lib/l10n/` → **Flutter Intl: Generate** 即可生成对应的 Dart 代码。
118
+
119
+ ### 5. 翻译
120
+
121
+ 翻译人员只需在非主语言的 ARB 文件中填入对应翻译,例如:
122
+
123
+ ```json
124
+ // intl_en.arb
125
+ {
126
+ "t5eb63bbb": "Message Settings",
127
+ "t3f2a1b2": "Receive System Notifications"
128
+ }
129
+ ```
130
+
131
+ ## 合并策略
132
+
133
+ - 已有 ARB 文件中的条目**保留不动**(保护手动修改的翻译)
134
+ - 新增条目**不会覆盖**已有同 key 条目
135
+ - 最终按 key 字母序排序输出,保持文件可读性
136
+
137
+ ## 后续步骤
138
+
139
+ ```
140
+ 1. IDE 中右键 lib/l10n/ → Flutter Intl: Generate(或等待 IDE 自动触发)
141
+ 2. fvm flutter analyze
142
+ ```
143
+
144
+ ## 开发
145
+
146
+ ```bash
147
+ # 开发模式(watch 编译)
148
+ npm run dev
149
+
150
+ # 测试 dry-run
151
+ npm test
152
+
153
+ # 构建
154
+ npm run build
155
+ ```
@@ -0,0 +1,99 @@
1
+ export interface TranslationMatch {
2
+ /** 提取到的模板文本(去掉引号后的内容) */
3
+ template: string;
4
+ /** $t 在源码中的起始位置(用于后续代码替换) */
5
+ start: number;
6
+ /** 字符串闭合引号之后的位置(用于继续解析可选 args Map) */
7
+ end: number;
8
+ }
9
+ export interface I18nParam {
10
+ /** 参数名(对应 ARB 中 {name} 占位符) */
11
+ name: string;
12
+ /** 参数表达式(Dart 代码,原样保留用于后续替换) */
13
+ expr: string;
14
+ }
15
+ /** 根据文本生成唯一 key(t + MD5 前 12 位) */
16
+ export declare function makeKey(text: string): string;
17
+ /**
18
+ * 从指定位置提取平衡括号内的内容
19
+ *
20
+ * 从给定位置的 open 字符开始,逐字符扫描直到找到匹配的 close 字符,
21
+ * 返回括号内的内容字符串。能正确处理嵌套的同类型括号。
22
+ *
23
+ * @param source - 要扫描的源字符串
24
+ * @param open - 开括号字符(如 '{')
25
+ * @param close - 闭括号字符(如 '}')
26
+ * @param start - 起始开括号在源字符串中的索引位置
27
+ * @returns 括号内的字符串内容;如果 start 位置不是 open 或括号未闭合,返回 null
28
+ *
29
+ * @example
30
+ * extractBalanced('{a: 1, b: {c: 2}}', '{', '}', 0) // => "a: 1, b: {c: 2}"
31
+ * extractBalanced('[1, [2, 3]]', '[', ']', 0) // => "1, [2, 3]"
32
+ */
33
+ export declare function extractBalanced(source: string, open: string, close: string, start: number): string | null;
34
+ /**
35
+ * 在文本中定位所有 `$t()` 调用并提取字符串字面量
36
+ *
37
+ * 分两步完成:
38
+ * 1. **正则定位** — `/\$t\(\s*(['"])/g` 搜索 `$t('` 或 `$t("`
39
+ * 2. **逐字符提取** — 从引号后开始扫描,处理转义 `\\`,直到闭合引号
40
+ *
41
+ * ### 为什么逐字符而非正则一次性匹配
42
+ *
43
+ * 模板内容可能包含转义序列(`\n`, `\\`, `\'` 等)和任意嵌套括号,
44
+ * 正则无法可靠区分字符串内的引号与闭合引号。
45
+ *
46
+ * @param source - Dart 文件源码
47
+ * @returns 所有 `$t()` 调用中提取的字符串匹配数组
48
+ *
49
+ * @example
50
+ * extractTranslation("$t('你好') $t(\"world\")")
51
+ * // => [
52
+ * // { template: '你好', start: 0, end: 8 },
53
+ * // { template: 'world', start: 9, end: 20 },
54
+ * // ]
55
+ */
56
+ export declare function extractTranslation(source: string): TranslationMatch[];
57
+ /**
58
+ * 解析 Dart `$t()` 调用中的命名参数
59
+ *
60
+ * 从 `$t('模板', {key1: expr1, key2: expr2})` 中提取参数名和表达式。
61
+ * 支持嵌套括号、方括号(如 `{onTap: () => Navigator.push(...)}`)。
62
+ *
63
+ * ### 解析策略
64
+ *
65
+ * 逐字符扫描,跟踪括号嵌套深度 `nestDepth`:
66
+ * - 遇到 `(` `[` `{` → nestDepth++
67
+ * - 遇到 `)` `]` `}` → nestDepth--
68
+ * - 顶层(nestDepth === 0)遇到 `,` 或 `}` → 当前参数结束
69
+ *
70
+ * @param argsStr - args Map 花括号内的字符串,如 `count: items.length, name: 'foo'`
71
+ * @returns 解析后的参数列表
72
+ *
73
+ * @example
74
+ * parseNamedArguments("count: items.length, name: user.name")
75
+ * // => [{ name: 'count', expr: 'items.length' }, { name: 'name', expr: 'user.name' }]
76
+ *
77
+ * parseNamedArguments("onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => Page()))")
78
+ * // => [{ name: 'onTap', expr: '() => Navigator.push(context, MaterialPageRoute(builder: (_) => Page()))' }]
79
+ */
80
+ export declare function parseNamedArguments(argsStr: string): I18nParam[];
81
+ /**
82
+ * 根据模板条目生成替换后的 Dart 代码
83
+ *
84
+ * ### 生成规则
85
+ *
86
+ * | 原始调用 | 替换结果 |
87
+ * |--------------------------------|--------------------------------|
88
+ * | `$t('你好')` | `S.current.t5eb63bbb` |
89
+ * | `$t('共{count}条', {count: n})`| `S.current.t3f2a1b2(count: n)` |
90
+ *
91
+ * 参数使用 Dart 命名参数语法 `name: value`,表达式原样保留不做转换。
92
+ *
93
+ * @param entry - 包含 key 和可选参数的条目
94
+ * @returns 替换后的 Dart 代码字符串
95
+ */
96
+ export declare function buildReplacement(entry: {
97
+ key: string;
98
+ params?: I18nParam[];
99
+ }): string;
@@ -0,0 +1 @@
1
+ 'use strict';const a0_0x3f9333=a0_0x3ecb;(function(_0x6d4c7d,_0x176ceb){const _0x1056c9=a0_0x3ecb,_0x2e3fcd=_0x6d4c7d();while(!![]){try{const _0x5c52ba=-parseInt(_0x1056c9(0x18e))/0x1*(-parseInt(_0x1056c9(0x192))/0x2)+parseInt(_0x1056c9(0x19d))/0x3*(-parseInt(_0x1056c9(0x193))/0x4)+parseInt(_0x1056c9(0x18c))/0x5*(parseInt(_0x1056c9(0x1a5))/0x6)+parseInt(_0x1056c9(0x1a4))/0x7+parseInt(_0x1056c9(0x187))/0x8+parseInt(_0x1056c9(0x190))/0x9*(parseInt(_0x1056c9(0x1a3))/0xa)+parseInt(_0x1056c9(0x19f))/0xb*(-parseInt(_0x1056c9(0x1a6))/0xc);if(_0x5c52ba===_0x176ceb)break;else _0x2e3fcd['push'](_0x2e3fcd['shift']());}catch(_0x822833){_0x2e3fcd['push'](_0x2e3fcd['shift']());}}}(a0_0x556e,0xb9dd1));var __importDefault=this&&this[a0_0x3f9333(0x1a1)]||function(_0x38c172){return _0x38c172&&_0x38c172['__esModule']?_0x38c172:{'default':_0x38c172};};Object['defineProperty'](exports,'__esModule',{'value':!![]}),exports[a0_0x3f9333(0x198)]=makeKey,exports[a0_0x3f9333(0x1a7)]=extractBalanced,exports['extractTranslation']=extractTranslation,exports[a0_0x3f9333(0x18d)]=parseNamedArguments,exports['buildReplacement']=buildReplacement;const crypto_js_1=__importDefault(require(a0_0x3f9333(0x197)));function makeKey(_0x2c21c1){const _0x35f113=a0_0x3f9333;return't'+crypto_js_1[_0x35f113(0x1a8)][_0x35f113(0x196)](_0x2c21c1)[_0x35f113(0x19b)]()[_0x35f113(0x18f)](0x0,0xc);}function a0_0x556e(){const _0x2c27e9=['5085605MvTcPZ','44040NagWuZ','21588xshvSR','extractBalanced','default','4010040iuIKCM','test','expr','length','match','25RxDXGm','parseNamedArguments','5777Fnyxli','substring','4023ATFJCT','index','262BIWACt','141364xDxlGX','slice','trim','MD5','crypto-js','makeKey','key','S.current.','toString','name','123ShWprI','params','1848CdhoAd','push','__importDefault','map','10990ToAfQX'];a0_0x556e=function(){return _0x2c27e9;};return a0_0x556e();}function extractBalanced(_0x28d0a8,_0x23e08f,_0x41c107,_0x1ad2db){const _0x2af29b=a0_0x3f9333;if(_0x28d0a8[_0x1ad2db]!==_0x23e08f)return null;let _0x38fe85=0x0;for(let _0x3ec3d1=_0x1ad2db;_0x3ec3d1<_0x28d0a8[_0x2af29b(0x18a)];_0x3ec3d1++){const _0x94251=_0x28d0a8[_0x3ec3d1];if(_0x94251===_0x23e08f)_0x38fe85++;else{if(_0x94251===_0x41c107){_0x38fe85--;if(_0x38fe85===0x0)return _0x28d0a8['slice'](_0x1ad2db+0x1,_0x3ec3d1);}}}return null;}function a0_0x3ecb(_0x279861,_0x3846b5){_0x279861=_0x279861-0x187;const _0x556eec=a0_0x556e();let _0x3ecb8a=_0x556eec[_0x279861];return _0x3ecb8a;}function extractTranslation(_0x519941){const _0x814c30=a0_0x3f9333,_0x3aa7fc=[],_0x5cceb6=/\$t\(\s*(['"])/g;let _0x30a9a9;while((_0x30a9a9=_0x5cceb6['exec'](_0x519941))!==null){const _0x54aa1f=_0x30a9a9[0x1],_0x1749eb=_0x30a9a9['index']+_0x30a9a9[0x0][_0x814c30(0x18a)];let _0x21fa1b='',_0x46fdf8=_0x1749eb;while(_0x46fdf8<_0x519941['length']){if(_0x519941[_0x46fdf8]==='\x5c')_0x21fa1b+=_0x519941[_0x46fdf8+0x1],_0x46fdf8+=0x2;else{if(_0x519941[_0x46fdf8]===_0x54aa1f)break;else _0x21fa1b+=_0x519941[_0x46fdf8],_0x46fdf8++;}}if(_0x519941[_0x46fdf8]!==_0x54aa1f)continue;_0x46fdf8++;if(!_0x21fa1b)continue;_0x3aa7fc[_0x814c30(0x1a0)]({'template':_0x21fa1b,'start':_0x30a9a9[_0x814c30(0x191)],'end':_0x46fdf8});}return _0x3aa7fc;}function parseNamedArguments(_0xa4e5a0){const _0x44bedf=a0_0x3f9333,_0x57b570=[];let _0x59c408=0x0;while(_0x59c408<_0xa4e5a0[_0x44bedf(0x18a)]){while(_0x59c408<_0xa4e5a0['length']&&/[\s,]/[_0x44bedf(0x188)](_0xa4e5a0[_0x59c408]))_0x59c408++;if(_0x59c408>=_0xa4e5a0[_0x44bedf(0x18a)])break;const _0x4b750d=_0xa4e5a0[_0x44bedf(0x194)](_0x59c408)[_0x44bedf(0x18b)](/^'(\w+)'\s*:\s*/);if(!_0x4b750d)break;const _0x590509=_0x4b750d[0x1];_0x59c408+=_0x4b750d[0x0][_0x44bedf(0x18a)];let _0x5e176e='',_0x59f794=0x0;while(_0x59c408<_0xa4e5a0['length']){const _0x20263b=_0xa4e5a0[_0x59c408];if(_0x20263b==='('||_0x20263b==='['||_0x20263b==='{')_0x59f794++;else{if(_0x20263b===')'||_0x20263b===']'){if(_0x59f794===0x0)break;_0x59f794--;}else{if(_0x20263b==='}'){if(_0x59f794===0x0)break;_0x59f794--;}else{if(_0x20263b===','&&_0x59f794===0x0)break;}}}_0x5e176e+=_0x20263b,_0x59c408++;}_0x5e176e[_0x44bedf(0x195)]()&&_0x57b570[_0x44bedf(0x1a0)]({'name':_0x590509,'expr':_0x5e176e[_0x44bedf(0x195)]()});}return _0x57b570;}function buildReplacement(_0x5b8e1d){const _0x27730a=a0_0x3f9333;if(!_0x5b8e1d[_0x27730a(0x19e)]||_0x5b8e1d['params'][_0x27730a(0x18a)]===0x0)return _0x27730a(0x19a)+_0x5b8e1d[_0x27730a(0x199)];const _0x2da240=_0x5b8e1d[_0x27730a(0x19e)][_0x27730a(0x1a2)](_0x3cd14c=>_0x3cd14c[_0x27730a(0x19c)]+':\x20'+_0x3cd14c[_0x27730a(0x189)])['join'](',\x20');return'S.current.'+_0x5b8e1d[_0x27730a(0x199)]+'('+_0x2da240+')';}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ## Flutter 国际化提取工具
4
+ *
5
+ * 基于 `$t()` 显式标记的 i18n 自动化方案,将翻译流程整合为三个步骤:
6
+ *
7
+ * ### 功能清单
8
+ *
9
+ * 1. **扫描** — 正则匹配 `$t('...')` 和 `$t("...")` 调用
10
+ * 2. **去重** — 相同模板文本生成同一个 key(t + MD5 前 8 位)
11
+ * 3. **ARB** — 按 `-l` 参数写入多个 `intl_<locale>.arb` 文件
12
+ * 4. **替换** — 将 `$t('...')` 替换为 `S.current.xxx(...)`
13
+ * 5. **Import** — 自动添加 `S` 类 import,移除旧的 marker import
14
+ */
15
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';function a1_0x5650(){const _0x1698cc=['.arb','1126023MrjvzF','buildReplacement','existsSync','46982804shsZGu','option','549wgnWLm','trim','S.current.','\x20\x20[DRY-RUN]\x20将替换:\x20','extractor','-\x20文件:','js-utils-plus','显示帮助信息','\x0a[DRY-RUN]\x20ARB\x20将写入:\x20','length','lib/l10n/','default','keys','parse','dir','exit','makeKey','getOwnPropertyDescriptor','8100102WFSexB','join','-f,\x20--file\x20<path>','includes','错误:\x20文件不存在\x20—\x20','name','mkdirSync','\x0a\x20后续步骤:','create','import\x20\x27package:','扫描\x20Flutter\x20项目中的\x20$t()\x20调用,提取国际化文本并生成\x20ARB\x20文件','599925BJuehe','error','-l,\x20--locale\x20<value...>','getOwnPropertyNames','-o,\x20--output\x20<dir>','Flutter\x20国际化提取工具,基于\x20`$t()`\x20显式标记的\x20i18n\x20自动化方案','intl_','__importStar','test','push','key','25368aVjEAH','slice','./core/utils','16VfvUZX','dryRun','stringify','扫描的目录路径,默认\x20lib/','0.0.1','defineProperty','full','扫描结果输出路径,默认\x20lib/l10n/','log','--dry-run','writeFileSync','\x20\x202.\x20fvm\x20flutter\x20analyze','readFileSync','utf-8','错误:\x20仅支持\x20.dart\x20文件\x20—\x20','output','161510vzKiOf','\x20\x201.\x20IDE\x20中右键\x20lib/l10n/\x20→\x20Flutter\x20Intl:\x20Generate(或等\x20IDE\x20自动触发)','baidu','184rgdSsg','显示版本号','template','2CCoGOx','helpOption','-h,\x20--help','call','get','1348776AVDXvK','extractBalanced','action','version','...','Command','-v,\x20--version','仅处理指定的单个\x20.dart\x20文件,不扫描目录','-d,\x20--dir\x20<dir>','-e,\x20--exclude\x20<patterns>','__esModule','prototype','entries','start','__setModuleDefault','file','sort','params','replace','\x20\x20✅\x20已写入:\x20','\x20\x20[','__importDefault','lib/','\x20处)','使用\x20--help\x20查看用法说明','.dart'];a1_0x5650=function(){return _0x1698cc;};return a1_0x5650();}const a1_0xfafcd1=a1_0x84eb;(function(_0x58a369,_0xa1fbe9){const _0x359909=a1_0x84eb,_0x4a1035=_0x58a369();while(!![]){try{const _0x1992b3=parseInt(_0x359909(0x9d))/0x1*(-parseInt(_0x359909(0x7d))/0x2)+parseInt(_0x359909(0x82))/0x3*(-parseInt(_0x359909(0xcd))/0x4)+-parseInt(_0x359909(0xbf))/0x5+-parseInt(_0x359909(0xb4))/0x6+parseInt(_0x359909(0xca))/0x7*(parseInt(_0x359909(0x7a))/0x8)+parseInt(_0x359909(0xa2))/0x9*(parseInt(_0x359909(0x77))/0xa)+parseInt(_0x359909(0xa0))/0xb;if(_0x1992b3===_0xa1fbe9)break;else _0x4a1035['push'](_0x4a1035['shift']());}catch(_0x361f0c){_0x4a1035['push'](_0x4a1035['shift']());}}}(a1_0x5650,0xe6cb6));var __createBinding=this&&this['__createBinding']||(Object[a1_0xfafcd1(0xbc)]?function(_0x5ef950,_0x257926,_0x11fd58,_0x1b5b31){const _0x4d3f85=a1_0xfafcd1;if(_0x1b5b31===undefined)_0x1b5b31=_0x11fd58;var _0x9e063e=Object[_0x4d3f85(0xb3)](_0x257926,_0x11fd58);(!_0x9e063e||(_0x4d3f85(0x81)in _0x9e063e?!_0x257926[_0x4d3f85(0x8c)]:_0x9e063e['writable']||_0x9e063e['configurable']))&&(_0x9e063e={'enumerable':!![],'get':function(){return _0x257926[_0x11fd58];}}),Object[_0x4d3f85(0xd2)](_0x5ef950,_0x1b5b31,_0x9e063e);}:function(_0x500031,_0x5a34ab,_0x3aec99,_0x5ba312){if(_0x5ba312===undefined)_0x5ba312=_0x3aec99;_0x500031[_0x5ba312]=_0x5a34ab[_0x3aec99];}),__setModuleDefault=this&&this[a1_0xfafcd1(0x90)]||(Object[a1_0xfafcd1(0xbc)]?function(_0x1eb882,_0x1a4d48){const _0x8ca31d=a1_0xfafcd1;Object[_0x8ca31d(0xd2)](_0x1eb882,_0x8ca31d(0xad),{'enumerable':!![],'value':_0x1a4d48});}:function(_0x2f2cf1,_0x240146){const _0x52d588=a1_0xfafcd1;_0x2f2cf1[_0x52d588(0xad)]=_0x240146;}),__importStar=this&&this[a1_0xfafcd1(0xc6)]||(function(){var _0x5958b5=function(_0x4c013f){const _0x2761b0=a1_0x84eb;return _0x5958b5=Object[_0x2761b0(0xc2)]||function(_0x5ca7bb){const _0x442dbf=_0x2761b0;var _0x38358b=[];for(var _0x53ea3d in _0x5ca7bb)if(Object[_0x442dbf(0x8d)]['hasOwnProperty'][_0x442dbf(0x80)](_0x5ca7bb,_0x53ea3d))_0x38358b[_0x38358b[_0x442dbf(0xab)]]=_0x53ea3d;return _0x38358b;},_0x5958b5(_0x4c013f);};return function(_0x1dba48){const _0x1b46ee=a1_0x84eb;if(_0x1dba48&&_0x1dba48[_0x1b46ee(0x8c)])return _0x1dba48;var _0x48392f={};if(_0x1dba48!=null){for(var _0x1d6931=_0x5958b5(_0x1dba48),_0x1428de=0x0;_0x1428de<_0x1d6931[_0x1b46ee(0xab)];_0x1428de++)if(_0x1d6931[_0x1428de]!==_0x1b46ee(0xad))__createBinding(_0x48392f,_0x1dba48,_0x1d6931[_0x1428de]);}return __setModuleDefault(_0x48392f,_0x1dba48),_0x48392f;};}()),__importDefault=this&&this[a1_0xfafcd1(0x97)]||function(_0x130301){const _0x3c73a5=a1_0xfafcd1;return _0x130301&&_0x130301[_0x3c73a5(0x8c)]?_0x130301:{'default':_0x130301};};Object[a1_0xfafcd1(0xd2)](exports,'__esModule',{'value':!![]});const fs=__importStar(require('fs')),commander_1=require('commander'),js_utils_plus_1=require(a1_0xfafcd1(0xa8)),utils_1=require(a1_0xfafcd1(0xcc)),path_1=__importDefault(require('path')),__path=process['cwd'](),S_IMPORT='import\x20\x27package:flutter_app_plus/generated/l10n.dart\x27;';function parseTranslationFile(_0x103e8e){const _0x135c8f=a1_0xfafcd1,_0x193c20=fs[_0x135c8f(0x73)](_0x103e8e,'utf-8'),_0x4ed10d=[];for(const {template:_0x371bae,start:_0x52ea3d,end:_0x5deaa5}of(0x0,utils_1['extractTranslation'])(_0x193c20)){let _0x465768=[],_0x49c99f=_0x5deaa5;while(_0x49c99f<_0x193c20['length']&&/[\s,]/[_0x135c8f(0xc7)](_0x193c20[_0x49c99f]))_0x49c99f++;if(_0x193c20[_0x49c99f]==='{'){const _0x201f5b=(0x0,utils_1[_0x135c8f(0x83)])(_0x193c20,'{','}',_0x49c99f);_0x201f5b!==null&&(_0x465768=(0x0,utils_1['parseNamedArguments'])(_0x201f5b),_0x49c99f+=_0x201f5b[_0x135c8f(0xab)]+0x2);}while(_0x49c99f<_0x193c20['length']&&/\s/[_0x135c8f(0xc7)](_0x193c20[_0x49c99f]))_0x49c99f++;if(_0x193c20[_0x49c99f]===')')_0x49c99f++;_0x4ed10d[_0x135c8f(0xc8)]({'template':_0x371bae,'params':_0x465768,'start':_0x52ea3d,'full':_0x193c20[_0x135c8f(0xcb)](_0x52ea3d,_0x49c99f)});}return _0x4ed10d;}function writeArb(_0x470147,_0x1cfc0d,_0x4a5fc7){const _0x2696a6=a1_0xfafcd1;!fs[_0x2696a6(0x9f)](_0x4a5fc7)&&fs[_0x2696a6(0xba)](_0x4a5fc7,{'recursive':!![]});for(let _0x5e0041=0x0;_0x5e0041<_0x1cfc0d[_0x2696a6(0xab)];_0x5e0041++){const _0x1caf5f=_0x1cfc0d[_0x5e0041],_0x122eff=path_1['default'][_0x2696a6(0xb5)](_0x4a5fc7,'intl_'+_0x1caf5f+_0x2696a6(0x9c));let _0x2f894a={};if(fs[_0x2696a6(0x9f)](_0x122eff))try{_0x2f894a=JSON[_0x2696a6(0xaf)](fs[_0x2696a6(0x73)](_0x122eff,_0x2696a6(0x74)));}catch{}const _0x2697c5={..._0x2f894a};for(const [_0x4d8c1a,_0x281487]of Object[_0x2696a6(0x8e)](_0x470147)){!_0x2697c5[_0x4d8c1a]&&(_0x2697c5[_0x4d8c1a]=_0x5e0041===0x0?_0x281487:'');}const _0x4322a7={};for(const _0x57aa0c of Object[_0x2696a6(0xae)](_0x2697c5)[_0x2696a6(0x92)]()){_0x4322a7[_0x57aa0c]=_0x2697c5[_0x57aa0c];}fs['writeFileSync'](_0x122eff,JSON[_0x2696a6(0xcf)](_0x4322a7,null,0x2)+'\x0a',_0x2696a6(0x74)),console[_0x2696a6(0x6f)](_0x2696a6(0x95)+_0x122eff);}}function replaceInFile(_0x26eac7,_0x26357f,_0x2fd633){const _0x1b1974=a1_0xfafcd1;let _0x30e88f=fs['readFileSync'](_0x26eac7,'utf-8');const _0x5092a3=[..._0x26357f][_0x1b1974(0x92)]((_0x2d9500,_0x40ad8f)=>_0x40ad8f[_0x1b1974(0x8f)]-_0x2d9500[_0x1b1974(0x8f)]);for(const _0x3b4d53 of _0x5092a3){const _0x45db40=(0x0,utils_1[_0x1b1974(0x9e)])(_0x3b4d53);_0x30e88f=_0x30e88f[_0x1b1974(0xcb)](0x0,_0x3b4d53[_0x1b1974(0x8f)])+_0x45db40+_0x30e88f[_0x1b1974(0xcb)](_0x3b4d53[_0x1b1974(0x8f)]+_0x3b4d53[_0x1b1974(0xd3)][_0x1b1974(0xab)]);}_0x30e88f=_0x30e88f[_0x1b1974(0x94)](/^import 'package:flutter_app_plus\/utils\/i18n_marker\.dart';\s*\n?/gm,'');if(_0x30e88f['includes'](_0x1b1974(0xa4))&&!_0x30e88f[_0x1b1974(0xb7)]('generated/l10n.dart')){const _0x3862a6=_0x30e88f['split']('\x0a');let _0x1da073=-0x1;for(let _0x51c488=0x0;_0x51c488<_0x3862a6['length'];_0x51c488++){const _0x4eb8a2=_0x3862a6[_0x51c488][_0x1b1974(0xa3)]();_0x4eb8a2['startsWith'](_0x1b1974(0xbd))&&!_0x4eb8a2[_0x1b1974(0xb7)]('flutter_app_plus/')&&(_0x1da073=_0x51c488);}_0x1da073>=0x0?(_0x3862a6['splice'](_0x1da073+0x1,0x0,S_IMPORT),_0x30e88f=_0x3862a6[_0x1b1974(0xb5)]('\x0a')):_0x30e88f=S_IMPORT+'\x0a'+_0x30e88f;}if(_0x2fd633){console[_0x1b1974(0x6f)](_0x1b1974(0xa5)+_0x26eac7+'\x20('+_0x26357f[_0x1b1974(0xab)]+_0x1b1974(0x99));return;}fs[_0x1b1974(0x71)](_0x26eac7,_0x30e88f,'utf-8'),console[_0x1b1974(0x6f)]('\x20\x20已替换:\x20'+_0x26eac7+'\x20('+_0x26357f[_0x1b1974(0xab)]+'\x20处)');}function a1_0x84eb(_0x23caad,_0x485429){_0x23caad=_0x23caad-0x6e;const _0x565009=a1_0x5650();let _0x84eb07=_0x565009[_0x23caad];return _0x84eb07;}function run(_0x5e43c1){const _0xd9350a=a1_0xfafcd1;var _0x1e517a;const _0x47d1f6=Object['assign']({'dir':_0xd9350a(0x98),'output':_0xd9350a(0xac),'channel':_0xd9350a(0x79),'locale':['en']},_0x5e43c1),_0x575d5a=path_1[_0xd9350a(0xad)][_0xd9350a(0xb5)](__path,_0x47d1f6[_0xd9350a(0xb0)]);console['log'](new Array(0x50)[_0xd9350a(0xb5)]('═')),console['log'](_0xd9350a(0xc4)),console['log'](new Array(0x50)['join']('═'));let _0x48a1b2;if(_0x47d1f6[_0xd9350a(0x91)]){const _0x1c0cc0=path_1[_0xd9350a(0xad)][_0xd9350a(0xb5)](__path,_0x47d1f6[_0xd9350a(0x91)]);!fs[_0xd9350a(0x9f)](_0x1c0cc0)&&(console['error'](_0xd9350a(0xb8)+_0x1c0cc0),process[_0xd9350a(0xb1)](0x1)),!_0x1c0cc0['endsWith']('.dart')&&(console[_0xd9350a(0xc0)](_0xd9350a(0x75)+_0x1c0cc0),process[_0xd9350a(0xb1)](0x1)),_0x48a1b2=[_0x1c0cc0];}else{const _0x4a8e0=(_0x47d1f6['exclude']||[])['map'](_0x450e93=>path_1[_0xd9350a(0xad)]['join'](_0x575d5a,_0x450e93));_0x48a1b2=(0x0,js_utils_plus_1['listFilesRecursively'])(_0x575d5a,_0xd9350a(0x9b),_0x4a8e0);}console['log'](new Array(0x50)[_0xd9350a(0xb5)]('-'));const _0x412f4a={},_0x19e718={};for(const _0x2f989d of _0x48a1b2){const _0x210ae0=parseTranslationFile(_0x2f989d);if(_0x210ae0[_0xd9350a(0xab)]===0x0)continue;for(const _0x45467c of _0x210ae0){const _0x1b2d79=(0x0,utils_1[_0xd9350a(0xb2)])(_0x45467c[_0xd9350a(0x7c)]);_0x412f4a[_0x1b2d79]=_0x45467c[_0xd9350a(0x7c)],((_0x1e517a=_0x19e718[_0x2f989d])!==null&&_0x1e517a!==void 0x0?_0x1e517a:_0x19e718[_0x2f989d]=[])[_0xd9350a(0xc8)]({'key':_0x1b2d79,'template':_0x45467c[_0xd9350a(0x7c)],'params':_0x45467c[_0xd9350a(0x93)],'start':_0x45467c[_0xd9350a(0x8f)],'full':_0x45467c['full']});}}for(const [_0x2e6119,_0x5d61dc]of Object[_0xd9350a(0x8e)](_0x19e718)){console['log'](_0xd9350a(0xa7)+_0x2e6119+'\x20('+_0x5d61dc[_0xd9350a(0xab)]+'\x20处)');if(_0x47d1f6[_0xd9350a(0x91)])for(const _0xf56ad9 of _0x5d61dc){const _0x924068=_0xf56ad9['params'][_0xd9350a(0xab)]>0x0?'\x20[插值]':'',_0xb6774c=_0xf56ad9['template'][_0xd9350a(0xab)]>0x3c?_0xf56ad9[_0xd9350a(0x7c)][_0xd9350a(0xcb)](0x0,0x3c)+_0xd9350a(0x86):_0xf56ad9[_0xd9350a(0x7c)];console[_0xd9350a(0x6f)](_0xd9350a(0x96)+_0xf56ad9[_0xd9350a(0xc9)]+']'+_0x924068+'\x20'+_0xb6774c);}}if(!_0x47d1f6[_0xd9350a(0xce)])for(const [_0x15294b,_0x27e40b]of Object[_0xd9350a(0x8e)](_0x19e718)){replaceInFile(_0x15294b,_0x27e40b,_0x47d1f6[_0xd9350a(0xce)]);}if(!_0x47d1f6[_0xd9350a(0xce)]){const _0x2d2203=path_1[_0xd9350a(0xad)][_0xd9350a(0xb5)](__path,_0x47d1f6['output']),_0x4074e7=_0x47d1f6['locale'];writeArb(_0x412f4a,_0x4074e7,_0x2d2203);}else{const _0x3331f7=_0x47d1f6['locale'],_0x93166b=path_1[_0xd9350a(0xad)][_0xd9350a(0xb5)](__path,_0x47d1f6[_0xd9350a(0x76)]);console[_0xd9350a(0x6f)](_0xd9350a(0xaa)+_0x3331f7['map'](_0x138a2d=>path_1['default'][_0xd9350a(0xb5)](_0x93166b,_0xd9350a(0xc5)+_0x138a2d+'.arb'))[_0xd9350a(0xb5)](',\x20'));}console['log'](_0xd9350a(0xbb)),console['log'](_0xd9350a(0x78)),console[_0xd9350a(0x6f)](_0xd9350a(0x72));}const program=new commander_1[(a1_0xfafcd1(0x87))]();program[a1_0xfafcd1(0xb9)](a1_0xfafcd1(0xa6))['description'](a1_0xfafcd1(0xbe))[a1_0xfafcd1(0x85)](a1_0xfafcd1(0xd1),a1_0xfafcd1(0x88),a1_0xfafcd1(0x7b))[a1_0xfafcd1(0x7e)](a1_0xfafcd1(0x7f),a1_0xfafcd1(0xa9))[a1_0xfafcd1(0xa1)](a1_0xfafcd1(0x8a),a1_0xfafcd1(0xd0))[a1_0xfafcd1(0xa1)](a1_0xfafcd1(0xb6),a1_0xfafcd1(0x89))[a1_0xfafcd1(0xa1)](a1_0xfafcd1(0xc3),a1_0xfafcd1(0x6e))[a1_0xfafcd1(0xa1)](a1_0xfafcd1(0x8b),'排除的文件/目录,逗号分隔')[a1_0xfafcd1(0xa1)](a1_0xfafcd1(0xc1),'生成的语言代码,默认\x20en')[a1_0xfafcd1(0xa1)](a1_0xfafcd1(0x70),'仅预览匹配结果,不实际修改任何文件')['showHelpAfterError'](a1_0xfafcd1(0x9a))[a1_0xfafcd1(0x84)](run),program[a1_0xfafcd1(0xaf)]();
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "flutter-i18n-generator",
3
+ "version": "0.0.1",
4
+ "description": "Flutter国际化i18n编译器,提取项目中的”中文“替换为多语言$t(key)并导出翻译文件。",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "extractor": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsc -w",
11
+ "test": "tsc && node dist/index.js --dry-run --file test/login.dart",
12
+ "test2": "tsc && node dist/index.js --dry-run -d test/",
13
+ "build": "rimraf dist && tsc && javascript-obfuscator dist --output dist"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": ""
18
+ },
19
+ "author": "evan.lin",
20
+ "license": "MIT",
21
+ "private": false,
22
+ "keywords": [
23
+ "flutter-i18n-generator",
24
+ "flutter-i18n"
25
+ ],
26
+ "files": [
27
+ "example/test.vue",
28
+ "dist/*"
29
+ ],
30
+ "dependencies": {
31
+ "axios": "^1.18.1",
32
+ "commander": "^15.0.0",
33
+ "crypto-js": "^4.2.0",
34
+ "js-utils-plus": "^1.5.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/crypto-js": "^4.2.2",
38
+ "@types/node": "^22.0.0",
39
+ "javascript-obfuscator": "^5.4.3",
40
+ "rimraf": "^6.1.3",
41
+ "typescript": "^5.8.0"
42
+ }
43
+ }