scancscode 1.0.57 → 1.0.58

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.
@@ -9,563 +9,427 @@ describe('CSharpStringExtractor', () => {
9
9
  });
10
10
 
11
11
  // 测试从普通函数调用参数中提取字符串表达式信息, 只需要提取参数中的字符串值表达式, 不需要对提取的字符串表达式进行处理
12
-
13
- describe('should handle .text = format', () => {
14
- test('should handle .text = format 1', () => {
15
- const code = `m_text_iwff.text = info ;`;
12
+ test('should extract plain strings', () => {
13
+ const code = 'Console.WriteLine("Hello, world!");';
16
14
  const snippets = extractor.extractStrings(code);
17
- {
18
- let targetSnippet = snippets[0];
19
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`info`));
20
- expect(targetSnippet.originalCode).toBe(`info`);
21
- expect(targetSnippet.convertedCode).toBe(`info.TR()`);
22
- expect(targetSnippet.literals).toEqual([
23
- ]);
24
- expect(targetSnippet.isChanged).toBe(true);
25
- }
26
- })
27
- test('should handle .text = format 2', () => {
28
- const code = `m_text_info.text = info1.member1 + info2.member2;`;
15
+
16
+ expect(snippets.length).toBe(1);
17
+ expect(snippets[0].literals).toEqual(['Hello, world!']);
18
+ });
19
+
20
+ // 已经追加 `.TR()` 后缀的字符串表达式不需要重复追加 `.TR()`
21
+ test('should handle strings with .TR() already appended', () => {
22
+ const code = 'obj.prop = "Hello, world!".TR();';
29
23
  const snippets = extractor.extractStrings(code);
30
- {
31
- let targetSnippet = snippets[0];
32
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`info1.member1 + info2.member2`));
33
- expect(targetSnippet.originalCode).toBe(`info1.member1 + info2.member2`);
34
- expect(targetSnippet.convertedCode).toBe(`info1.member1.TR() + info2.member2.TR()`);
35
- expect(targetSnippet.literals).toEqual([
36
- ]);
37
- expect(targetSnippet.isChanged).toBe(true);
38
- }
39
- })
24
+
25
+ expect(snippets.length).toBe(1);
26
+ expect(snippets[0].originalCode).toBe('"Hello, world!".TR()');
27
+ expect(snippets[0].convertedCode).toBe('"Hello, world!".TR()');
28
+ expect(snippets[0].isChanged).toBe(false);
40
29
  });
41
30
 
42
- describe('should handle .text = with Tr.Format', () => {
43
- test('should handle .text = with Tr.Format 2', () => {
44
- const code = 'm_text_lastAward.text = $"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]";';
31
+ // 测试从普通函数调用参数中提取字符串表达式信息, 只需要提取参数中的字符串值表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
32
+ test('should not add .TR() to function arguments', () => {
33
+ const code = 'CallFunc("Hello, world!");';
45
34
  const snippets = extractor.extractStrings(code);
46
35
 
47
- const snippet = snippets[0];
48
- expect(snippet.originalCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]"');
49
- expect(snippet.convertedCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]".TR()');
50
- expect(snippet.literals).toEqual(['[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]']);
51
- expect(snippet.isChanged).toBe(true);
52
- })
36
+ expect(snippets.length).toBe(1);
37
+ expect(snippets[0].originalCode).toBe('"Hello, world!"');
38
+ expect(snippets[0].convertedCode).toBe('"Hello, world!"');
53
39
  });
54
40
 
55
- describe('should handle bracket', () => {
56
- test('should handle bracket 1', () => {
57
- const code = `m_text_time.text = $"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}";`
41
+ // 测试从普通属性赋值语句中提取字符串表达式信息, 只需要提取赋值表达式中的字符串值表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
42
+ test('should not add .TR() to regular property assignments', () => {
43
+ const code = 'obj.prop = "Hello, world!";';
58
44
  const snippets = extractor.extractStrings(code);
59
45
 
60
- // 验证提取的片段数量
61
- expect(snippets.length).toBeGreaterThan(0);
46
+ expect(snippets.length).toBe(1);
47
+ expect(snippets[0].originalCode).toBe('"Hello, world!"');
48
+ expect(snippets[0].convertedCode).toBe('"Hello, world!"');
49
+ });
62
50
 
63
- let targetSnippet = snippets[0];
64
- expect(targetSnippet.originalCode).toBe(`$"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}"`);
65
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("下一轮神兽出现倒计时: {0}", Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp))`);
66
- expect(targetSnippet.literals).toEqual([
67
- '下一轮神兽出现倒计时: {0}'
68
- ]);
69
- expect(targetSnippet.isChanged).toBe(true);
70
- })
71
- test('should handle bracket 2', () => {
72
- const code = `m_text_time.text = $"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}";`
51
+ // 测试从 $"" 字符串模板(也叫内插字符串)中提取字符串表达式信息, 需要先将字符串模板转换为 `string.Format(...)` 格式, 然后将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
52
+ test('should handle $"" string templates', () => {
53
+ const code = 'var label1 = $"Hello, {name}!";';
73
54
  const snippets = extractor.extractStrings(code);
74
55
 
75
- // 验证提取的片段数量
76
- expect(snippets.length).toBeGreaterThan(0);
56
+ expect(snippets.length).toBe(1);
57
+ expect(snippets[0].originalCode).toBe('$"Hello, {name}!"');
58
+ expect(snippets[0].literals).toContain('Hello, {0}!');
59
+ expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
60
+ });
77
61
 
78
- let targetSnippet = snippets[0];
79
- expect(targetSnippet.originalCode).toBe(`$"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}"`);
80
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("神兽离去时间: {0}", Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp))`);
81
- expect(targetSnippet.literals).toEqual([
82
- '神兽离去时间: {0}'
83
- ]);
84
- expect(targetSnippet.isChanged).toBe(true);
85
- })
86
- test('should handle bracket 3', () => {
87
- const code = `desc += $"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}";`
62
+ // 测试从普通函数调用参数中提取 $"" 字符串模板(也叫内插字符串), 需要先将字符串模板转换为 `string.Format(...)` 格式, 然后将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
63
+ test('should handle $"" string templates in function calls', () => {
64
+ const code = 'CallFunc($"Hello, world: {obj.Func1()}");';
88
65
  const snippets = extractor.extractStrings(code);
89
66
 
90
- // 验证提取的片段数量
91
- expect(snippets.length).toBeGreaterThan(0);
67
+ expect(snippets.length).toBe(1);
68
+ expect(snippets[0].originalCode).toBe('$"Hello, world: {obj.Func1()}"');
69
+ expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, world: {0}", obj.Func1())');
70
+ });
92
71
 
93
- let targetSnippet = snippets[0];
94
- expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}"`);
95
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time))`);
96
- expect(targetSnippet.literals).toEqual([
97
- '剩余{0}'
98
- ]);
99
- expect(targetSnippet.isChanged).toBe(true);
100
- })
101
- test('should handle bracket 4', () => {
102
- const code = `desc += $"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}";`
72
+ // 测试遇到 `string.Format(...)` 形式的字符串表达式, 需要先将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息, 内插字符串概念参考: `https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated`
73
+ test('should convert string.Format to Tr.Format', () => {
74
+ const code = 'string.Format("Hello, ", name, "!");';
103
75
  const snippets = extractor.extractStrings(code);
104
76
 
105
- // 验证提取的片段数量
106
- expect(snippets.length).toBeGreaterThan(0);
77
+ expect(snippets.length).toBe(1);
78
+ // string.Format(...) 形式的字符串表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
79
+ // string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
80
+ expect(snippets[0].originalCode).toBe('string.Format("Hello, ", name, "!")');
81
+ expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, ", name, "!")');
82
+ });
107
83
 
108
- let targetSnippet = snippets[0];
109
- expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}"`);
110
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time())))`);
111
- expect(targetSnippet.literals).toEqual([
112
- '剩余{0}'
113
- ]);
114
- expect(targetSnippet.isChanged).toBe(true);
115
- })
84
+ // 测试遇到 `string.Format(...)` 形式的字符串表达式, 需要先将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
85
+ // 同时测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式
86
+ test('should handle string.Format in .text assignments', () => {
87
+ const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
88
+ const snippets = extractor.extractStrings(code);
89
+
90
+ expect(snippets.length).toBe(1);
91
+ expect(snippets[0].originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
92
+ expect(snippets[0].literals).toContain('pre');
93
+ expect(snippets[0].literals).toContain('sub');
116
94
  });
117
95
 
118
- describe('should handle class member initialization assignment with anonymous function', () => {
119
- test('should handle class member initialization assignment with anonymous function 1', () => {
96
+ // 形如 `m_btn_xxx.title = yyy;` 的赋值语句要和形如 `xxx.text = yyy;` 的赋值语句完全等同处理, 其中 `xxx`和`yyy` 代表某段C#字面量, 可以是对象、字符串、或其他表达式, `m_btn_` 为引用符号名固定前缀; 为了便于理解, 用正则表达式表示, 也就是相当于需要同时匹配 `/^m_btn_\w+\.title\s*=/` 形式的表达式和 `/\w+\.text\s*=/` 形式的表达式, 而其中对于 `xxx` 和 `yyy` 部分的处理逻辑是完全一致的
97
+ test('should handle m_btn_xxx.title = yyy', () => {
120
98
  const code = `
121
- FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
122
- {
123
- Title = "提示",
124
- ConfirmCallback = async () =>
125
- {
126
- //灵纹一键下阵
127
- if (await this.GetModel<CardModel>().DispatchAction(new Rune_Action_CleanRune(_sutraCardData.Card.Id)))
128
- {
129
- Toast.Info("卸载成功");
130
- }
131
- },
132
- CancelText = "取消",
133
- }).Forget();
99
+ buy.title = "lkwjelkfj1";
100
+ m_btn1_buy.title = "lkwjelkfj2";
101
+ am_btn_buy.title = "lkwjelkfj3";
102
+ m_btn_buy.title = "lkwjelkfj4";
103
+ m_btn_wwefHwref.title = $"Hello";
104
+ m_btn_fxx_wf.title = $"Hello, {name}!";
134
105
  `;
135
106
  const snippets = extractor.extractStrings(code);
136
107
  {
137
- let targetSnippet = snippets[0];
138
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"提示"`));
139
- expect(targetSnippet.originalCode).toBe(`"提示"`);
140
- expect(targetSnippet.convertedCode).toBe(`"提示"`);
141
- expect(targetSnippet.literals).toEqual([
142
- '提示'
143
- ]);
144
- expect(targetSnippet.isChanged).toBe(false);
108
+ let snippet = snippets[0];
109
+ expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj1"'));
110
+ expect(snippet.originalCode).toBe('"lkwjelkfj1"');
111
+ // `buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
112
+ expect(snippet.convertedCode).toBe('"lkwjelkfj1"');
113
+ expect(snippet.literals).toContain('lkwjelkfj1');
114
+ expect(snippet.isChanged).toBe(false);
145
115
  }
146
116
  {
147
- let targetSnippet = snippets[1];
148
- // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
149
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
150
- expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
151
- expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
152
- expect(targetSnippet.literals).toEqual([
153
- '卸载成功'
154
- ]);
155
- expect(targetSnippet.isChanged).toBe(false);
117
+ let snippet = snippets[1];
118
+ expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj2"'));
119
+ expect(snippet.originalCode).toBe('"lkwjelkfj2"');
120
+ // `m_btn1_buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
121
+ expect(snippet.convertedCode).toBe('"lkwjelkfj2"');
122
+ expect(snippet.literals).toContain('lkwjelkfj2');
123
+ expect(snippet.isChanged).toBe(false);
156
124
  }
157
125
  {
158
- let targetSnippet = snippets[2];
159
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"取消"`));
160
- expect(targetSnippet.originalCode).toBe(`"取消"`);
161
- expect(targetSnippet.convertedCode).toBe(`"取消"`);
162
- expect(targetSnippet.literals).toEqual([
163
- '取消'
164
- ]);
165
- expect(targetSnippet.isChanged).toBe(false);
126
+ let snippet = snippets[2];
127
+ expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj3"'));
128
+ expect(snippet.originalCode).toBe('"lkwjelkfj3"');
129
+ // `am_btn_buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
130
+ expect(snippet.convertedCode).toBe('"lkwjelkfj3"');
131
+ expect(snippet.literals).toContain('lkwjelkfj3');
132
+ expect(snippet.isChanged).toBe(false);
166
133
  }
167
- })
168
- test('should handle class member initialization assignment with anonymous function 2', () => {
169
- const code = `
170
- FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
171
- {
172
- ConfirmCallback = async () =>
173
- {
174
- Toast.Info("卸载成功");
175
- },
176
- }).Forget();
177
- `;
178
- const snippets = extractor.extractStrings(code);
179
134
  {
180
- let targetSnippet = snippets[0];
181
- // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
182
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
183
- expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
184
- expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
185
- expect(targetSnippet.literals).toEqual([
186
- '卸载成功'
187
- ]);
188
- expect(targetSnippet.isChanged).toBe(false);
135
+ let snippet = snippets[3];
136
+ expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj4"'));
137
+ expect(snippet.originalCode).toBe('"lkwjelkfj4"');
138
+ // `m_btn_buy` 符合 `m_btn_` 开头的条件, 需要强制加 `.TR()`
139
+ expect(snippet.convertedCode).toBe('"lkwjelkfj4".TR()');
140
+ expect(snippet.literals).toContain('lkwjelkfj4');
141
+ expect(snippet.isChanged).toBe(true);
189
142
  }
190
- })
191
- test('should handle class member initialization assignment with anonymous function 3', () => {
192
- const code = `
193
- FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
194
- {
195
- ConfirmCallback = (int qwfewe) =>
196
- {
197
- Toast.Info($"卸载成功: {qwfewe}");
198
- },
199
- }).Forget();
200
- `;
201
- const snippets = extractor.extractStrings(code);
202
143
  {
203
- let targetSnippet = snippets[0];
204
- // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
205
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"卸载成功: {qwfewe}"`));
206
- expect(targetSnippet.originalCode).toBe(`$"卸载成功: {qwfewe}"`);
207
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("卸载成功: {0}", qwfewe)`);
208
- expect(targetSnippet.literals).toEqual([
209
- '卸载成功: {0}'
210
- ]);
211
- expect(targetSnippet.isChanged).toBe(true);
144
+ let snippet = snippets[4];
145
+ expect(snippet.originalIndex).toBe(code.indexOf('$"Hello"'));
146
+ expect(snippet.originalCode).toBe('$"Hello"');
147
+ // `m_btn_wwefHwref` 符合 `m_btn_` 开头的条件, 需要强制加 `.TR()`
148
+ expect(snippet.convertedCode).toBe('$"Hello".TR()');
149
+ expect(snippet.literals).toContain('Hello');
150
+ expect(snippet.isChanged).toBe(true);
212
151
  }
213
- })
214
- test('should handle class member initialization assignment with anonymous function 4', () => {
215
- const code = `
216
- FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
217
- {
218
- ConfirmCallback = async () => Toast.Info("卸载成功"),
219
- }).Forget();
220
- `;
221
- const snippets = extractor.extractStrings(code);
222
152
  {
223
- let targetSnippet = snippets[0];
224
- // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
225
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
226
- expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
227
- expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
228
- expect(targetSnippet.literals).toEqual([
229
- '卸载成功'
230
- ]);
231
- expect(targetSnippet.isChanged).toBe(false);
153
+ let snippet = snippets[5];
154
+ expect(snippet.originalIndex).toBe(code.indexOf('$"Hello, {name}!"'));
155
+ expect(snippet.originalCode).toBe('$"Hello, {name}!"');
156
+ // `m_btn_fxx_wf` 符合 `m_btn_` 开头的条件
157
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
158
+ expect(snippet.literals).toContain('Hello, {0}!');
159
+ expect(snippet.isChanged).toBe(true);
232
160
  }
233
- })
234
- test('should handle class member initialization assignment with anonymous function 5', () => {
235
- const code = `
236
- FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
237
- {
238
- ConfirmCallback = () => Toast.Info("卸载成功"),
239
- }).Forget();
240
- `;
161
+ expect(snippets.length).toBe(6);
162
+ });
163
+
164
+ // 测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式; 并且给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式
165
+ test('should handle string concatenation', () => {
166
+ const code = 'var label2 = "Hello, " + name + "!";';
241
167
  const snippets = extractor.extractStrings(code);
242
- {
243
- let targetSnippet = snippets[0];
244
- // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
245
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
246
- expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
247
- expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
248
- expect(targetSnippet.literals).toEqual([
249
- '卸载成功'
250
- ]);
251
- expect(targetSnippet.isChanged).toBe(false);
252
- }
253
- })
168
+
169
+ expect(snippets.length).toBe(1);
170
+ expect(snippets[0].originalCode).toBe('"Hello, " + name + "!"');
171
+ expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
254
172
  });
255
173
 
256
- describe('should handle comment boundary', () => {
257
- test('should handle comment boundary 1', () => {
258
- const code = `
259
- //上锁
260
- case (int)State.Lock:
261
- Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
262
- break;
263
- `;
174
+ // 测试 `xxx.text = yyy;` 形式的赋值语句, 确保将 `yyy` 中的字符串表达式信息提取出来, 并追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#字面量, 可以是对象、字符串、或其他表达式
175
+ test('should handle .text = assignments with function calls', () => {
176
+ const code = 'label.text = Func();';
264
177
  const snippets = extractor.extractStrings(code);
265
- {
266
- let targetSnippet = snippets[0];
267
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
268
- expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
269
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
270
- expect(targetSnippet.literals).toEqual([
271
- '灵田{0}级解锁'
272
- ]);
273
- expect(targetSnippet.isChanged).toBe(true);
274
- }
275
- })
276
- test('should handle comment boundary 2', () => {
277
- const code = `
278
- // klwjfe
279
- namespace TestNamespace{
280
- // lkwjfe
281
- public class FK{
282
- // klwf
283
- public void TestM(){
284
- // 模拟状态变量
285
- var _state = (int)State.Lock;
286
- // 模拟土地ID变量
287
- switch (_state){
288
- //上锁
289
- case (int)State.Lock:
290
- // lkwjefl
291
- Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
292
- // klwejf
293
- break;
294
- // lkwjf
295
- }
296
- // klwjfel
297
- }
298
- }
299
- }
300
- `;
178
+
179
+ expect(snippets.length).toBe(1);
180
+ expect(snippets[0].originalCode).toBe('Func()');
181
+ expect(snippets[0].convertedCode).toBe('Func().TR()');
182
+ });
183
+
184
+ // 测试 `xxx.text = xxx + xxx;` 形式的赋值语句, 确保将 `xxx.text` 中的 `xxx` 字符串表达式提取出来, 并追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#表达式, 可以是对象、字符串、或其他表达式
185
+ test('should handle .text = assignments with multiple function calls', () => {
186
+ const code = 'label.text = Func() + Func();';
301
187
  const snippets = extractor.extractStrings(code);
302
- {
303
- let targetSnippet = snippets[0];
304
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
305
- expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
306
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
307
- expect(targetSnippet.literals).toEqual([
308
- '灵田{0}级解锁'
309
- ]);
310
- expect(targetSnippet.isChanged).toBe(true);
311
- }
312
- })
313
- test('should handle comment boundary 3', () => {
314
- const code = `
315
- // klwjfe
316
- namespace TestNamespace{
317
- // lkwjfe
318
- public class FK{
319
- // wlkjelj
320
- private int Llkwe = 23;
321
- // lkjlkj
322
- private int Dwekl { get; set; } = 23;
323
- // jklwef
324
- private static int Dwekl { get; set; } = 23;
325
- // lkwjeflk
326
- public static void TestM(){}
327
- // wesd
328
- public static void TestM()
329
- {
330
- }
331
- // wesd
332
- public static void TestM(){
333
- }
334
- // klwf
335
- public void TestM(){
336
- // 模拟状态变量
337
- var _state = (int)State.Lock;
338
- // 模拟土地ID变量
339
- switch (_state){
340
- //上锁
341
- case (int)State.Lock:
342
- // lkwjefl
343
- Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
344
- // klwejf
345
- break;
346
- // lkwjf
347
- }
348
- // klwjfel
349
- }
350
- }
351
- }
352
- `;
188
+
189
+ expect(snippets.length).toBe(1);
190
+ expect(snippets[0].originalCode).toBe('Func() + Func()');
191
+ expect(snippets[0].convertedCode).toBe('Func().TR() + Func().TR()');
192
+ });
193
+
194
+ // 测试 `xxx.text = yyy;` 形式的赋值语句, 确保将 `yyy` 中的 `xxx` 内插字符串提取出来, 并转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#表达式, 可以是对象、字符串、或其他表达式
195
+ test('should handle .text = assignments with string templates', () => {
196
+ const code = 'label.text = $"pre{Func()}sub";';
353
197
  const snippets = extractor.extractStrings(code);
354
- {
355
- let targetSnippet = snippets[0];
356
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
357
- expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
358
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
359
- expect(targetSnippet.literals).toEqual([
360
- '灵田{0}级解锁'
361
- ]);
362
- expect(targetSnippet.isChanged).toBe(true);
363
- }
364
- })
198
+
199
+ expect(snippets.length).toBe(1);
200
+ expect(snippets[0].originalCode).toBe('$"pre{Func()}sub"');
201
+ expect(snippets[0].literals).toContain('pre{0}sub');
202
+ expect(snippets[0].convertedCode).toBe('Tr.Format("pre{0}sub", Func())');
365
203
  });
366
204
 
367
- describe('should handle complex string', () => {
368
- test('should handle complex string 1', () => {
369
- const code = `
370
- infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";
371
- infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
372
- `;
205
+ // 测试从内插字符串中提取字符串表达式信息时, 需要注意转换和捕获内插字符串格式参数, 内插字符串和对应格式参数参考: `https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated`
206
+ test('should handle .text = assignments with string.Format', () => {
207
+ const code = 'label.text = string.Format("pre{0:F2}{1}", Func(), "sub") + other;';
373
208
  const snippets = extractor.extractStrings(code);
374
- {
375
- let targetSnippet = snippets[0];
376
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`));
377
- expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
378
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
379
- expect(targetSnippet.literals).toEqual([
380
- '伤害类型: [color={0}][b]{1}[/b][/color]\n'
381
- ]);
382
- expect(targetSnippet.isChanged).toBe(true);
383
- }
384
- })
385
- test('should handle complex string 2', () => {
386
- const code = `
387
- infoStr += Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType);
388
- infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
389
- `;
209
+
210
+ expect(snippets.length).toBe(1);
211
+ expect(snippets[0].originalCode).toBe('string.Format("pre{0:F2}{1}", Func(), "sub") + other');
212
+ expect(snippets[0].literals).toContain('pre{0:F2}{1}');
213
+ expect(snippets[0].literals).toContain('sub');
214
+ });
215
+
216
+ // 测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式; 并且给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
217
+ test('should handle complex concatenation with existing .TR() calls', () => {
218
+ const code = 'var hello3 = "Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!";';
390
219
  const snippets = extractor.extractStrings(code);
391
- {
392
- let targetSnippet = snippets[0];
393
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`));
394
- expect(targetSnippet.originalCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
395
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
396
- expect(targetSnippet.literals).toEqual([
397
- '伤害类型: [color={0}][b]{1}[/b][/color]\n'
398
- ]);
399
- expect(targetSnippet.isChanged).toBe(false);
400
- }
401
- })
402
- test('should handle complex string 4', () => {
403
- const code = `
404
- private void OnBtnIaa(EventContext context)
405
- {
406
- var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
407
- if (iaaConfig == null)
408
- {
409
- return;
410
- }
411
220
 
412
- string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
413
- this.GetModel<IAAModel>().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
414
- }
415
- `;
221
+ expect(snippets.length).toBe(1);
222
+ expect(snippets[0].originalCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!"');
223
+ expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop.TR() + "!".TR()');
224
+ });
225
+
226
+ // 测试处理包含转义引号的字符串表达式, 确保能正确提取和转换转义引号, 而不会影响字符串边界的正常解析
227
+ test('should handle escaped quotes in strings', () => {
228
+ const code = 'Debug.Log("aaa\\\"bbb\\\"c\\\"d\\\\\\"cc");';
416
229
  const snippets = extractor.extractStrings(code);
417
- {
418
- let targetSnippet = snippets[0];
419
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`));
420
- expect(targetSnippet.originalCode).toBe(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`);
421
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("观看视频后\n等待时间减少{0}分钟", iaaConfig.EffectValue / 60)`);
422
- expect(targetSnippet.literals).toEqual([
423
- '观看视频后\n等待时间减少{0}分钟'
424
- ]);
425
- expect(targetSnippet.isChanged).toBe(true);
426
- }
427
- })
230
+
231
+ expect(snippets.length).toBe(1);
232
+ expect(snippets[0].originalCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
233
+ expect(snippets[0].convertedCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
234
+ // 由于JavaScript字符串转义的复杂性,我们只检查是否提取了字符串
235
+ expect(snippets[0].literals).toEqual(['aaa\\\"bbb\\\"c\\\"d\\\\\\"cc']);
428
236
  });
429
237
 
430
- describe('should handle complex string with optional value', () => {
431
- test('should handle complex string with optional value 3', () => {
432
- const code = `
433
- else if (battleModel.CurGameType == BattleModel.GameType.Teamwork)
434
- {
435
- //合作模式显示队友名称
436
- m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";
437
- }
438
- `;
238
+ // 测试处理包含 `+` 连接的字符串表达式, 需要给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
239
+ test('should capture statement boundaries correctly', () => {
240
+ const code = 'obj.text = "Test property" + "Test property2";';
439
241
  const snippets = extractor.extractStrings(code);
440
- {
441
- let targetSnippet = snippets[0];
442
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
443
- expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
444
- expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
445
- expect(targetSnippet.literals).toEqual([
446
- ''
447
- ]);
448
- expect(targetSnippet.isChanged).toBe(true);
449
- }
450
- })
242
+
243
+ expect(snippets.length).toBe(1);
244
+ expect(snippets[0].originalCode).toBe('"Test property" + "Test property2"');
245
+ expect(snippets[0].convertedCode).toBe('"Test property".TR() + "Test property2".TR()');
246
+ });
247
+
248
+ // 测试处理包含多个语句的C#代码, 确保能从各个语句中正确提取和转换每个语句中的字符串表达式, 而不会影响其他语句的正常解析
249
+ test('should handle multiple statements', () => {
250
+ const code = 'obj1.text = "Hello"; obj2.text = "World";';
251
+ const snippets = extractor.extractStrings(code);
252
+
253
+ expect(snippets.length).toBe(2);
254
+ expect(snippets[0].originalCode).toBe('"Hello"');
255
+ expect(snippets[0].convertedCode).toBe('"Hello".TR()');
256
+ expect(snippets[1].originalCode).toBe('"World"');
257
+ expect(snippets[1].convertedCode).toBe('"World".TR()');
451
258
  });
452
259
 
453
- describe('should handle duplicate tr handle', () => {
454
- test('should handle duplicate tr handle 1', () => {
455
- const code = `infoStr = $"+={colorCode}";`
260
+ // 测试处理作为函数参数的内插字符串表达式, 确保能从内插字符串中提取字符串表达式信息, 并转换为 `Tr.Format(...)` 形式
261
+ test('should extract and convert string templates', () => {
262
+ const code = 'Ljk.Ilk($"Hello, {name}!");';
456
263
  const snippets = extractor.extractStrings(code);
457
264
 
458
- // 验证提取的片段数量
459
265
  expect(snippets.length).toBeGreaterThan(0);
266
+ const snippet = snippets[0];
267
+ expect(snippet.originalCode).toBe('$"Hello, {name}!"');
268
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
269
+ expect(snippet.literals).toEqual(['Hello, {0}!']);
270
+ expect(snippet.isChanged).toBe(true);
271
+ });
460
272
 
461
- let targetSnippet = snippets[0];
462
- expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
463
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
464
- expect(targetSnippet.literals).toEqual([
465
- '+={0}'
466
- ]);
467
- expect(targetSnippet.isChanged).toBe(true);
468
- })
469
- test('should handle duplicate tr handle 2', () => {
470
- const code = `infoStr += $"={colorCode}";`
273
+ // 测试处理 `string.Format` 调用, 确保能将其转换为 `Tr.Format(...)` 形式, 并正确提取字符串表达式信息
274
+ test('should convert string.Format to Tr.Format', () => {
275
+ const code = 'string.Format("Hello, {0}!", name);';
471
276
  const snippets = extractor.extractStrings(code);
472
277
 
473
- // 验证提取的片段数量
474
278
  expect(snippets.length).toBeGreaterThan(0);
279
+ const snippet = snippets[0];
280
+ expect(snippet.originalCode).toBe('string.Format("Hello, {0}!", name)');
281
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
282
+ expect(snippet.literals).toEqual(['Hello, {0}!']);
283
+ expect(snippet.isChanged).toBe(true);
284
+ });
475
285
 
476
- let targetSnippet = snippets[0];
477
- expect(targetSnippet.originalCode).toBe(`$"={colorCode}"`);
478
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("={0}", colorCode)`);
479
- expect(targetSnippet.literals).toEqual([
480
- '={0}'
481
- ]);
482
- expect(targetSnippet.isChanged).toBe(true);
483
- })
484
- test('should handle duplicate tr handle 3', () => {
485
- const code = `infoStr += $"+={colorCode}";`
286
+ // 测试处理 `xxx.text = yyy;` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式, 需要从 `yyy` 中正确提取字符串表达式信息, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
287
+ test('should handle .text = assignments', () => {
288
+ const code = 'label.text = "Test";';
486
289
  const snippets = extractor.extractStrings(code);
487
290
 
488
- // 验证提取的片段数量
489
291
  expect(snippets.length).toBeGreaterThan(0);
292
+ const snippet = snippets[0];
293
+ expect(snippet.originalCode).toBe('"Test"');
294
+ expect(snippet.convertedCode).toBe('"Test".TR()');
295
+ expect(snippet.literals).toEqual(['Test']);
296
+ expect(snippet.isChanged).toBe(true);
297
+ });
490
298
 
491
- let targetSnippet = snippets[0];
492
- expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
493
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
494
- expect(targetSnippet.literals).toEqual([
495
- '+={0}'
496
- ]);
497
- expect(targetSnippet.isChanged).toBe(true);
498
- })
499
- test('should handle duplicate tr handle 4', () => {
500
- const code = `infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";`
299
+ // 测试处理包含 `+` 连接的字符串表达式, 需要给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
300
+ test('should handle string concatenations', () => {
301
+ const code = 'var wkleee = "Hello, " + name + "!";';
501
302
  const snippets = extractor.extractStrings(code);
502
303
 
503
- // 验证提取的片段数量
504
304
  expect(snippets.length).toBeGreaterThan(0);
305
+ const snippet = snippets[0];
306
+ expect(snippet.originalCode).toBe('"Hello, " + name + "!"');
307
+ expect(snippet.convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
308
+ expect(snippet.literals).toEqual(['Hello, ', '!']);
309
+ expect(snippet.isChanged).toBe(true);
310
+ });
505
311
 
506
- let targetSnippet = snippets[0];
507
- expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
508
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
509
- expect(targetSnippet.literals).toEqual([
510
- '伤害类型: [color={0}][b]{1}[/b][/color]\n'
511
- ]);
512
- expect(targetSnippet.isChanged).toBe(true);
513
- })
312
+ // 测试处理 `+` 连接的字符串表达式时, 确保不会给已经包含 `.TR()` 的字符串表达式再次追加 `.TR()`
313
+ test('should not add .TR() to strings already having it', () => {
314
+ const code = 'var wkle = "Hello".TR();';
315
+ const snippets = extractor.extractStrings(code);
316
+
317
+ const snippet = snippets[0];
318
+ expect(snippet.originalCode).toBe('"Hello".TR()');
319
+ expect(snippet.convertedCode).toBe('"Hello".TR()');
320
+ expect(snippet.literals).toEqual(['Hello']);
321
+ expect(snippet.isChanged).toBe(false);
514
322
  });
515
323
 
516
- describe('should handle lost', () => {
517
- test('should handle lost 1', () => {
518
- const code = readFileSync('./test/KeeperDialog.cs', 'utf8');
324
+ // 测试处理复杂的 `.text =` 赋值语句, 确保能正确处理包含 `string.Format` 调用和 `+` 连接的字符串表达式, 并转换为 `Tr.Format(...)` 形式或进一步递归处理
325
+ test('should handle complex text assignments', () => {
326
+ const code = 'label.text = string.Format("pre{0}sub", Func()) + other;';
519
327
  const snippets = extractor.extractStrings(code);
520
328
 
521
- let targetSnippet = snippets[3];
522
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"已购买";
523
- m_btn_buy.enabled = false;
329
+ const snippet = snippets[0];
330
+ expect(snippet.originalCode).toBe('string.Format("pre{0}sub", Func()) + other');
331
+ expect(snippet.convertedCode).toBe('Tr.Format("pre{0}sub", Func()) + other.TR()');
332
+ expect(snippet.literals).toEqual(['pre{0}sub']);
333
+ expect(snippet.isChanged).toBe(true);
334
+ });
524
335
 
525
- //购买后自动启用`));
526
- expect(targetSnippet.originalCode).toBe(`"已购买"`);
527
- expect(targetSnippet.convertedCode).toBe(`"已购买".TR()`);
528
- expect(targetSnippet.literals).toEqual([
529
- "已购买"
530
- ]);
531
- expect(targetSnippet.isChanged).toBe(true);
336
+ // 测试处理转义字符串, 确保能正确处理包含转义字符的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
337
+ test('should handle escaped strings', () => {
338
+ const code = 'a.text = "aaa\\"bbb\\"c\\"d\\\\\\"cc";';
339
+ const snippets = extractor.extractStrings(code);
532
340
 
533
- // 验证提取的片段数量
534
- expect(snippets.length).toBeGreaterThanOrEqual(5)
535
- })
536
- test('should handle lost 2', () => {
537
- const code = `m_text_runeSlot.text = Tr.Format(")", _sutraCardData.Card.RuneOpenCount);`;
341
+ const snippet = snippets[0];
342
+ expect(snippet.originalCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc"');
343
+ expect(snippet.convertedCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc".TR()');
344
+ expect(snippet.literals).toEqual(['aaa\\"bbb\\"c\\"d\\\\\\"cc']);
345
+ expect(snippet.isChanged).toBe(true);
346
+ });
347
+
348
+ // 测试处理 `@$""` 和 `$@""` 格式的字符串表达式, 确保能将其转换为 `Tr.Format(...)` 形式, 并正确提取字符串表达式信息
349
+ test('should handle @$"" and $@"" formats', () => {
350
+ const code = 'Fcx.Kjl(@$"Hello, {name}!");';
538
351
  const snippets = extractor.extractStrings(code);
539
352
 
540
- let targetSnippet = snippets[0];
541
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`));
542
- expect(targetSnippet.originalCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
543
- expect(targetSnippet.convertedCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
544
- // 字符串中包含 `)` 符号时, 也需要捕获该字符串
545
- expect(targetSnippet.literals).toEqual([
546
- ')'
547
- ]);
548
- expect(targetSnippet.isChanged).toBe(false);
353
+ const snippet = snippets[0];
354
+ expect(snippet.originalCode).toBe('@$"Hello, {name}!"');
355
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
356
+ expect(snippet.literals).toEqual(['Hello, {0}!']);
357
+ expect(snippet.isChanged).toBe(true);
358
+ });
549
359
 
550
- })
551
- test('should handle lost 3', () => {
552
- const code = `m_text_runeSlot.text = ")";`;
360
+ // 测试处理函数调用时, 确保能正确提取参数中的字符串表达式, 不需要给字符串表达式追加 `.TR()` 或 转换为 `Tr.Format(...)` 形式, 除非遇到由 `+` 连接的字符串表达式
361
+ test('should handle function calls with string arguments', () => {
362
+ const code = 'CallFunc("Hello", "World");';
553
363
  const snippets = extractor.extractStrings(code);
364
+ {
365
+ const snippet = snippets[0];
366
+ expect(snippet.originalCode).toBe('"Hello"');
367
+ expect(snippet.convertedCode).toBe('"Hello"');
368
+ expect(snippet.literals).toEqual(['Hello']);
369
+ expect(snippet.isChanged).toBe(false);
370
+ }
371
+ {
554
372
 
555
- let targetSnippet = snippets[0];
556
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`")"`));
557
- expect(targetSnippet.originalCode).toBe(`")"`);
558
- expect(targetSnippet.convertedCode).toBe(`")".TR()`);
559
- expect(targetSnippet.literals).toEqual([
560
- ')'
561
- ]);
562
- expect(targetSnippet.isChanged).toBe(true);
373
+ const snippet = snippets[1];
374
+ expect(snippet.originalCode).toBe('"World"');
375
+ expect(snippet.convertedCode).toBe('"World"');
376
+ expect(snippet.literals).toEqual(['World']);
377
+ expect(snippet.isChanged).toBe(false);
378
+ }
379
+ });
563
380
 
564
- })
381
+ // 测试处理 `.text =` 赋值语句时, 确保能正确处理包含 `Tr.Format(...)` 调用的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
382
+ test('should handle .text = with Tr.Format', () => {
383
+ const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
384
+ const snippets = extractor.extractStrings(code);
385
+
386
+ const snippet = snippets[0];
387
+ expect(snippet.originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
388
+ expect(snippet.convertedCode).toBe('Tr.Format("pre", Func(), "sub") + other.TR()');
389
+ expect(snippet.literals).toEqual(['pre', 'sub']);
390
+ expect(snippet.isChanged).toBe(true);
391
+ });
392
+
393
+ // 测试处理 `.text =` 赋值语句时, 确保能正确处理包含 `Tr.Format(...)` 调用的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式, 除非遇到由 `+` 连接的字符串表达式
394
+ test('should handle .text = with Tr.Format 2', () => {
395
+ const code = 'm_text_lastAward.text = $"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]";';
396
+ const snippets = extractor.extractStrings(code);
397
+
398
+ const snippet = snippets[0];
399
+ expect(snippet.originalCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]"');
400
+ expect(snippet.convertedCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]".TR()');
401
+ expect(snippet.literals).toEqual(['[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]']);
402
+ expect(snippet.isChanged).toBe(true);
403
+ });
404
+
405
+ // 处理C#多行语句
406
+ test('should handle non string assignment 1', () => {
407
+ const code = 'var d1 = 12;var d2 = 13;var d3 = d1 + d2;var aa = "aaa";';
408
+ const snippets = extractor.extractStrings(code);
409
+ {
410
+ const snippet = snippets[0];
411
+ expect(snippet.originalCode).toBe('"aaa"');
412
+ expect(snippet.convertedCode).toBe('"aaa"');
413
+ expect(snippet.literals).toEqual(['aaa']);
414
+ expect(snippet.isChanged).toBe(false);
415
+ }
416
+ });
417
+
418
+ // 处理C#多行语句
419
+ test('should handle non string assignment 2', () => {
420
+ const code = 'var d3 = d1 + d2;var aa = "aaa";';
421
+ const snippets = extractor.extractStrings(code);
422
+ {
423
+ const snippet = snippets[0];
424
+ expect(snippet.originalCode).toBe('"aaa"');
425
+ expect(snippet.convertedCode).toBe('"aaa"');
426
+ expect(snippet.literals).toEqual(['aaa']);
427
+ expect(snippet.isChanged).toBe(false);
428
+ }
565
429
  });
566
430
 
567
- describe('should handle multilne content', () => {
568
- test('should handle multilne content 1', () => {
431
+ // 处理C#多行语句
432
+ test('should handle multilne content 1', () => {
569
433
  const code = 'var aa = "aaa";\nvar bb = "bbb";\nvar cc = "ccc";';
570
434
  const snippets = extractor.extractStrings(code);
571
435
  {
@@ -589,8 +453,10 @@ namespace TestNamespace{
589
453
  expect(snippet.literals).toEqual(['ccc']);
590
454
  expect(snippet.isChanged).toBe(false);
591
455
  }
592
- })
593
- test('should handle multilne content 2', () => {
456
+ });
457
+
458
+ // 处理C#多行语句
459
+ test('should handle multilne content 2', () => {
594
460
  const code = 'var d1 = 12;var d2 = 13;var d3 = d1 + d2;var aa = "aaa";\nvar bb = "bbb";\nvar dd = aa + bb;\nvar hh = aa + bb + "hhh";\ncc.text = "ccc" + aa + bb;var ii = "iii";jj.text = "jjj";CallFunc("jjj");CallFunc2("jjj", "kkk");jj.text = "jjj" + ll;';
595
461
  const snippets = extractor.extractStrings(code);
596
462
  {
@@ -663,8 +529,10 @@ namespace TestNamespace{
663
529
  expect(snippet.literals).toEqual(['jjj']);
664
530
  expect(snippet.isChanged).toBe(true);
665
531
  }
666
- })
667
- test('should handle multilne content 3', () => {
532
+ });
533
+
534
+ // 处理C#多行语句
535
+ test('should handle multilne content 3', () => {
668
536
  const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;\nlabel2.text = Tr.Format("pre2", Func(), "sub2") + other2;';
669
537
  const snippets = extractor.extractStrings(code);
670
538
  {
@@ -681,8 +549,10 @@ namespace TestNamespace{
681
549
  expect(snippet.literals).toEqual(['pre2', 'sub2']);
682
550
  expect(snippet.isChanged).toBe(true);
683
551
  }
684
- })
685
- test('should handle multilne content 4', () => {
552
+ });
553
+
554
+ // 处理C#多行语句
555
+ test('should handle multilne content 4', () => {
686
556
  const code = 'label.text =\n Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other;';
687
557
  const snippets = extractor.extractStrings(code);
688
558
  {
@@ -692,8 +562,10 @@ namespace TestNamespace{
692
562
  expect(snippet.literals).toEqual(['pre', 'sub']);
693
563
  expect(snippet.isChanged).toBe(true);
694
564
  }
695
- })
696
- test('should handle multilne content 5', () => {
565
+ });
566
+
567
+ // 处理C#多行语句
568
+ test('should handle multilne content 5', () => {
697
569
  const code = `
698
570
  // 注释中无字符串表达式时, 不需要包含在捕获部分里
699
571
  m_text_name.text = cardTable.Name;
@@ -706,8 +578,10 @@ namespace TestNamespace{
706
578
  expect(snippet.literals).toEqual([]);
707
579
  expect(snippet.isChanged).toBe(true);
708
580
  }
709
- })
710
- test('should handle multilne content 6', () => {
581
+ });
582
+
583
+ // 处理C#多行语句
584
+ test('should handle multilne content 6', () => {
711
585
  const code = `
712
586
  if (condition)
713
587
  {
@@ -723,8 +597,10 @@ namespace TestNamespace{
723
597
  expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
724
598
  expect(snippet.isChanged).toBe(false);
725
599
  }
726
- })
727
- test('should handle multilne content 7', () => {
600
+ });
601
+
602
+ // 处理C#多行语句
603
+ test('should handle multilne content 7', () => {
728
604
  const code = `
729
605
  while (condition)
730
606
  {
@@ -740,8 +616,10 @@ namespace TestNamespace{
740
616
  expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
741
617
  expect(snippet.isChanged).toBe(false);
742
618
  }
743
- })
744
- test('should handle multilne content 8', () => {
619
+ });
620
+
621
+ // 处理C#多行语句
622
+ test('should handle multilne content 8', () => {
745
623
  const code = `
746
624
  for (scope;condition;continuex)
747
625
  {
@@ -756,8 +634,10 @@ namespace TestNamespace{
756
634
  expect(snippet.literals).toEqual(['卸载成功']);
757
635
  expect(snippet.isChanged).toBe(false);
758
636
  }
759
- })
760
- test('should handle multilne content 9', () => {
637
+ });
638
+
639
+ // 处理C#多行语句
640
+ test('should handle multilne content 9', () => {
761
641
  const code = `
762
642
  {
763
643
  KK3.Ca3("卸载成功");
@@ -771,8 +651,10 @@ namespace TestNamespace{
771
651
  expect(snippet.literals).toEqual(['卸载成功']);
772
652
  expect(snippet.isChanged).toBe(false);
773
653
  }
774
- })
775
- test('should handle multilne content 10', () => {
654
+ });
655
+
656
+ // 处理C#多行语句
657
+ test('should handle multilne content 10', () => {
776
658
  const code = `
777
659
  var lambda1 = () => {
778
660
  LJN4.Fn4("卸载成功");
@@ -786,8 +668,10 @@ namespace TestNamespace{
786
668
  expect(snippet.literals).toEqual(['卸载成功']);
787
669
  expect(snippet.isChanged).toBe(false);
788
670
  }
789
- })
790
- test('should handle multilne content 11', () => {
671
+ });
672
+
673
+ // 处理C#多行语句
674
+ test('should handle multilne content 11', () => {
791
675
  const code = `
792
676
  void lambda1() {
793
677
  KK5.Call5("卸载成功");
@@ -801,8 +685,10 @@ namespace TestNamespace{
801
685
  expect(snippet.literals).toEqual(['卸载成功']);
802
686
  expect(snippet.isChanged).toBe(false);
803
687
  }
804
- })
805
- test('should handle multilne content 12', () => {
688
+ });
689
+
690
+ // 处理C#多行语句
691
+ test('should handle multilne content 12', () => {
806
692
  const code = `
807
693
  //星级
808
694
  m_list_star.RemoveChildrenToPool();
@@ -827,8 +713,10 @@ namespace TestNamespace{
827
713
  expect(snippet.literals).toEqual(['{0}阶']);
828
714
  expect(snippet.isChanged).toBe(false);
829
715
  }
830
- })
831
- test('should handle multilne content 13', () => {
716
+ });
717
+
718
+ // 处理C#多行语句
719
+ test('should handle multilne content 13', () => {
832
720
  const code = `
833
721
  Toast.Info("卸载成功");
834
722
  Toast.Info("卸载成功");
@@ -850,8 +738,10 @@ namespace TestNamespace{
850
738
  expect(snippet.literals).toEqual(['卸载成功']);
851
739
  expect(snippet.isChanged).toBe(false);
852
740
  }
853
- })
854
- test('should handle multilne content 14', () => {
741
+ });
742
+
743
+ // 处理C#多行语句
744
+ test('should handle multilne content 14', () => {
855
745
  const code = readFileSync('./test/MainSutraDetailDialog.cs', 'utf8');
856
746
  const snippets = extractor.extractStrings(code);
857
747
 
@@ -1030,8 +920,10 @@ namespace TestNamespace{
1030
920
  expect(snippets[21].convertedCode).toBe('"卸载成功"');
1031
921
  expect(snippets[21].literals).toEqual(['卸载成功']);
1032
922
  expect(snippets[21].isChanged).toBe(false);
1033
- })
1034
- test('should handle multilne content 15', () => {
923
+ });
924
+
925
+ // 处理C#多行语句
926
+ test('should handle multilne content 15', () => {
1035
927
  const code = readFileSync('./test/GuildDonateDialog.cs', 'utf8');
1036
928
  const snippets = extractor.extractStrings(code);
1037
929
 
@@ -1052,8 +944,41 @@ namespace TestNamespace{
1052
944
  expect(targetSnippet.isChanged).toBe(true);
1053
945
  }
1054
946
 
1055
- })
1056
- test('should handle multilne content 17', () => {
947
+ });
948
+
949
+ // 处理C#普通字符串包含 `//` 的情形
950
+ test('should handle string with `//`', () => {
951
+ const code = `m_btn_paid.text = "//";`
952
+ const snippets = extractor.extractStrings(code);
953
+
954
+ // 验证提取的片段数量
955
+ expect(snippets.length).toBeGreaterThan(0);
956
+
957
+ let targetSnippet = snippets[0];
958
+ expect(targetSnippet.originalCode).toBe(`"//"`);
959
+ expect(targetSnippet.convertedCode).toBe(`"//".TR()`);
960
+ expect(targetSnippet.literals).toEqual(['//']);
961
+ expect(targetSnippet.isChanged).toBe(true);
962
+
963
+ });
964
+
965
+ // 处理C# `+=` 形式赋值语句
966
+ test('should handle string template with format specifier', () => {
967
+ const code = `infoStr += $"最终抗性: {resistReduce:F}\\n";`
968
+ const snippets = extractor.extractStrings(code);
969
+
970
+ // 验证提取的片段数量
971
+ expect(snippets.length).toBeGreaterThan(0);
972
+
973
+ let targetSnippet = snippets[0];
974
+ expect(targetSnippet.originalCode).toBe(`$"最终抗性: {resistReduce:F}\\n"`);
975
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("最终抗性: {0:F}\\n", resistReduce)`);
976
+ expect(targetSnippet.literals).toEqual(['最终抗性: {0:F}\\n']);
977
+ expect(targetSnippet.isChanged).toBe(true);
978
+ });
979
+
980
+ // 处理C#多行语句, 特别是包含 `\n` 的字符串
981
+ test('should handle multilne content 17', () => {
1057
982
  const code = `
1058
983
  infoStr += $"aaa: {aa}\n" +
1059
984
  $"bbb = {bb}";
@@ -1073,8 +998,10 @@ namespace TestNamespace{
1073
998
  'bbb = {0}'
1074
999
  ]);
1075
1000
  expect(targetSnippet.isChanged).toBe(true);
1076
- })
1077
- test('should handle multilne content 19', () => {
1001
+ });
1002
+
1003
+ // 处理C#多行语句, 特别是包含 `\\n` 的内插字符串
1004
+ test('should handle multilne content 19', () => {
1078
1005
  const code = `
1079
1006
  infoStr += $"aaa: {aa}\\n" +
1080
1007
  $"bbb = {bb}";
@@ -1094,8 +1021,10 @@ namespace TestNamespace{
1094
1021
  'bbb = {0}'
1095
1022
  ]);
1096
1023
  expect(targetSnippet.isChanged).toBe(true);
1097
- })
1098
- test('should handle multilne content 18', () => {
1024
+ });
1025
+
1026
+ // 处理C#中多行字符串拼接成的字符串表达式
1027
+ test('should handle multilne content 18', () => {
1099
1028
  const code = `
1100
1029
  infoStr += "伤害公式: \\n" +
1101
1030
  $"最终伤害[color={colorCode}][{(float)damageValue:F}][/color] = \\n" +
@@ -1139,92 +1068,324 @@ namespace TestNamespace{
1139
1068
  '抗性减免[b][{7:F}]'
1140
1069
  ]);
1141
1070
  expect(targetSnippet.isChanged).toBe(true);
1142
- })
1143
1071
  });
1144
1072
 
1145
- describe('should handle non string assignment', () => {
1146
- test('should handle non string assignment 1', () => {
1147
- const code = 'var d1 = 12;var d2 = 13;var d3 = d1 + d2;var aa = "aaa";';
1073
+ // 处理C#中包含括号的内插字符串
1074
+ test('should handle bracket 1', () => {
1075
+ const code = `m_text_time.text = $"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}";`
1148
1076
  const snippets = extractor.extractStrings(code);
1149
- {
1150
- const snippet = snippets[0];
1151
- expect(snippet.originalCode).toBe('"aaa"');
1152
- expect(snippet.convertedCode).toBe('"aaa"');
1153
- expect(snippet.literals).toEqual(['aaa']);
1154
- expect(snippet.isChanged).toBe(false);
1155
- }
1156
- })
1157
- test('should handle non string assignment 2', () => {
1158
- const code = 'var d3 = d1 + d2;var aa = "aaa";';
1077
+
1078
+ // 验证提取的片段数量
1079
+ expect(snippets.length).toBeGreaterThan(0);
1080
+
1081
+ let targetSnippet = snippets[0];
1082
+ expect(targetSnippet.originalCode).toBe(`$"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}"`);
1083
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("下一轮神兽出现倒计时: {0}", Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp))`);
1084
+ expect(targetSnippet.literals).toEqual([
1085
+ '下一轮神兽出现倒计时: {0}'
1086
+ ]);
1087
+ expect(targetSnippet.isChanged).toBe(true);
1088
+ });
1089
+
1090
+ // 处理C#中包含括号的内插字符串
1091
+ test('should handle bracket 2', () => {
1092
+ const code = `m_text_time.text = $"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}";`
1159
1093
  const snippets = extractor.extractStrings(code);
1160
- {
1161
- const snippet = snippets[0];
1162
- expect(snippet.originalCode).toBe('"aaa"');
1163
- expect(snippet.convertedCode).toBe('"aaa"');
1164
- expect(snippet.literals).toEqual(['aaa']);
1165
- expect(snippet.isChanged).toBe(false);
1166
- }
1167
- })
1094
+
1095
+ // 验证提取的片段数量
1096
+ expect(snippets.length).toBeGreaterThan(0);
1097
+
1098
+ let targetSnippet = snippets[0];
1099
+ expect(targetSnippet.originalCode).toBe(`$"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}"`);
1100
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("神兽离去时间: {0}", Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp))`);
1101
+ expect(targetSnippet.literals).toEqual([
1102
+ '神兽离去时间: {0}'
1103
+ ]);
1104
+ expect(targetSnippet.isChanged).toBe(true);
1168
1105
  });
1169
1106
 
1170
- describe('should handle null', () => {
1171
- test('should handle null 1', () => {
1172
- const code = `m_text_notice.text = null;`;
1107
+ // 处理C#中包含括号的内插字符串
1108
+ test('should handle bracket 3', () => {
1109
+ const code = `desc += $"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}";`
1173
1110
  const snippets = extractor.extractStrings(code);
1174
- {
1175
- let targetSnippet = snippets[0];
1176
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`null`));
1177
- expect(targetSnippet.originalCode).toBe(`null`);
1178
- expect(targetSnippet.convertedCode).toBe(`null`);
1179
- expect(targetSnippet.literals).toEqual([
1180
- ]);
1181
- expect(targetSnippet.isChanged).toBe(false);
1182
- }
1183
- expect(snippets.length).toBe(1);
1184
- })
1185
- test('should handle null 2', () => {
1186
- const code = `m_text_notice.wfefe = null;`;
1111
+
1112
+ // 验证提取的片段数量
1113
+ expect(snippets.length).toBeGreaterThan(0);
1114
+
1115
+ let targetSnippet = snippets[0];
1116
+ expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}"`);
1117
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time))`);
1118
+ expect(targetSnippet.literals).toEqual([
1119
+ '剩余{0}'
1120
+ ]);
1121
+ expect(targetSnippet.isChanged).toBe(true);
1122
+ });
1123
+
1124
+ // 处理C#中包含括号的内插字符串
1125
+ test('should handle bracket 4', () => {
1126
+ const code = `desc += $"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}";`
1187
1127
  const snippets = extractor.extractStrings(code);
1188
- expect(snippets.length).toBe(0);
1189
- })
1128
+
1129
+ // 验证提取的片段数量
1130
+ expect(snippets.length).toBeGreaterThan(0);
1131
+
1132
+ let targetSnippet = snippets[0];
1133
+ expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}"`);
1134
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time())))`);
1135
+ expect(targetSnippet.literals).toEqual([
1136
+ '剩余{0}'
1137
+ ]);
1138
+ expect(targetSnippet.isChanged).toBe(true);
1139
+ });
1140
+
1141
+ // 处理C#中包含`+=`符号的内插字符串
1142
+ test('should handle duplicate tr handle 1', () => {
1143
+ const code = `infoStr = $"+={colorCode}";`
1144
+ const snippets = extractor.extractStrings(code);
1145
+
1146
+ // 验证提取的片段数量
1147
+ expect(snippets.length).toBeGreaterThan(0);
1148
+
1149
+ let targetSnippet = snippets[0];
1150
+ expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
1151
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
1152
+ expect(targetSnippet.literals).toEqual([
1153
+ '+={0}'
1154
+ ]);
1155
+ expect(targetSnippet.isChanged).toBe(true);
1156
+ });
1157
+
1158
+ // 处理C# `+=` 形式的赋值语句后接 包含 `=` 符号的内插字符串
1159
+ test('should handle duplicate tr handle 2', () => {
1160
+ const code = `infoStr += $"={colorCode}";`
1161
+ const snippets = extractor.extractStrings(code);
1162
+
1163
+ // 验证提取的片段数量
1164
+ expect(snippets.length).toBeGreaterThan(0);
1165
+
1166
+ let targetSnippet = snippets[0];
1167
+ expect(targetSnippet.originalCode).toBe(`$"={colorCode}"`);
1168
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("={0}", colorCode)`);
1169
+ expect(targetSnippet.literals).toEqual([
1170
+ '={0}'
1171
+ ]);
1172
+ expect(targetSnippet.isChanged).toBe(true);
1173
+ });
1174
+
1175
+ // 处理C# `+=` 形式的赋值语句后接 包含 `+=` 符号的内插字符串
1176
+ test('should handle duplicate tr handle 3', () => {
1177
+ const code = `infoStr += $"+={colorCode}";`
1178
+ const snippets = extractor.extractStrings(code);
1179
+
1180
+ // 验证提取的片段数量
1181
+ expect(snippets.length).toBeGreaterThan(0);
1182
+
1183
+ let targetSnippet = snippets[0];
1184
+ expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
1185
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
1186
+ expect(targetSnippet.literals).toEqual([
1187
+ '+={0}'
1188
+ ]);
1189
+ expect(targetSnippet.isChanged).toBe(true);
1190
+ });
1191
+
1192
+ // 处理C# `+=` 形式的赋值语句后接包含 `=`、`[`、`]`、`{`、`}`、`=` 等特殊符号的内插字符串
1193
+ test('should handle duplicate tr handle 4', () => {
1194
+ const code = `infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";`
1195
+ const snippets = extractor.extractStrings(code);
1196
+
1197
+ // 验证提取的片段数量
1198
+ expect(snippets.length).toBeGreaterThan(0);
1199
+
1200
+ let targetSnippet = snippets[0];
1201
+ expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
1202
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1203
+ expect(targetSnippet.literals).toEqual([
1204
+ '伤害类型: [color={0}][b]{1}[/b][/color]\n'
1205
+ ]);
1206
+ expect(targetSnippet.isChanged).toBe(true);
1207
+ });
1208
+
1209
+ // 处理C#中函数调用参数中包含普通字符串的情况, 函数参数需要拆分, 逐个参数捕获
1210
+ test('should handle para 1', () => {
1211
+ const code = `SetData("等级", level + 1);`
1212
+ const snippets = extractor.extractStrings(code);
1213
+
1214
+ // 验证提取的片段数量
1215
+ expect(snippets.length).toBeGreaterThan(0);
1216
+
1217
+ let targetSnippet = snippets[0];
1218
+ // 函数参数需要拆分, 逐个参数捕获
1219
+ expect(targetSnippet.originalCode).toBe(`"等级"`);
1220
+ expect(targetSnippet.convertedCode).toBe(`"等级"`);
1221
+ expect(targetSnippet.literals).toEqual([
1222
+ "等级"
1223
+ ]);
1224
+ expect(targetSnippet.isChanged).toBe(false);
1225
+ });
1226
+
1227
+ // 处理C#中函数调用参数中包含普通字符串的情况
1228
+ test('should handle para 2', () => {
1229
+ const code = `bbb.Func2("等级", level, level + 1, false, isMax);`
1230
+ const snippets = extractor.extractStrings(code);
1231
+
1232
+ // 验证提取的片段数量
1233
+ expect(snippets.length).toBeGreaterThan(0);
1234
+
1235
+ let targetSnippet = snippets[0];
1236
+ expect(targetSnippet.originalCode).toBe(`"等级"`);
1237
+ expect(targetSnippet.convertedCode).toBe(`"等级"`);
1238
+ expect(targetSnippet.literals).toEqual([
1239
+ "等级"
1240
+ ]);
1241
+ expect(targetSnippet.isChanged).toBe(false);
1242
+ });
1243
+
1244
+ // 处理C#中switch语句中包含普通字符串的情况
1245
+ test('should handle switch 1', () => {
1246
+ const code = `
1247
+ switch(condition){
1248
+ default:
1249
+ Log.Warning("实时更新自:" + rankData.Type);
1250
+ break;
1251
+ }`
1252
+ const snippets = extractor.extractStrings(code);
1253
+
1254
+ // 验证提取的片段数量
1255
+ expect(snippets.length).toBeGreaterThan(0);
1256
+
1257
+ let targetSnippet = snippets[0];
1258
+ expect(targetSnippet.originalCode).toBe(`"实时更新自:" + rankData.Type`);
1259
+ // 普通函数参数不需要追加 `.TR()`, 除非包含由 `+` 符号连接字符串等的情况
1260
+ expect(targetSnippet.convertedCode).toBe(`"实时更新自:" + rankData.Type`);
1261
+ expect(targetSnippet.literals).toEqual([
1262
+ "实时更新自:"
1263
+ ]);
1264
+ expect(targetSnippet.isChanged).toBe(false);
1265
+ });
1266
+
1267
+ // 处理C#中switch语句中包含普通字符串的情况
1268
+ test('should handle switch 2', () => {
1269
+ const code = `
1270
+ switch(kwjkwlje){
1271
+ case (int)RANK_TYPE.天梯段位:
1272
+ Debug.Log1("jkwhfehwfjkh:" + rank2Data.T4zype);
1273
+ break;
1274
+ }`
1275
+ const snippets = extractor.extractStrings(code);
1276
+
1277
+ // 验证提取的片段数量, switch 语句中包含普通字符串的情况
1278
+ expect(snippets.length).toBeGreaterThan(0);
1279
+
1280
+ let targetSnippet = snippets[0];
1281
+ expect(targetSnippet.originalCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
1282
+ expect(targetSnippet.convertedCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
1283
+ expect(targetSnippet.literals).toEqual([
1284
+ "jkwhfehwfjkh:"
1285
+ ]);
1286
+ expect(targetSnippet.isChanged).toBe(false);
1287
+ });
1288
+
1289
+ // 处理C#中switch语句中包含普通字符串的情况
1290
+ test('should handle switch 3', () => {
1291
+ const code = `
1292
+ switch(lkw){
1293
+ case V434:
1294
+ {
1295
+ Action2("wegwfw:" + varnws);
1296
+ break;
1297
+ }
1298
+ }`
1299
+ const snippets = extractor.extractStrings(code);
1300
+
1301
+ // 验证提取的片段数量
1302
+ expect(snippets.length).toBeGreaterThan(0);
1303
+
1304
+ let targetSnippet = snippets[0];
1305
+ expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
1306
+ expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
1307
+ expect(targetSnippet.literals).toEqual([
1308
+ "wegwfw:"
1309
+ ]);
1310
+ expect(targetSnippet.isChanged).toBe(false);
1190
1311
  });
1191
1312
 
1192
- describe('should handle para', () => {
1193
- test('should handle para 1', () => {
1194
- const code = `SetData("等级", level + 1);`
1313
+ // 处理C#中switch语句中包含普通字符串的情况
1314
+ test('should handle switch 4', () => {
1315
+ const code = `
1316
+ case V434:
1317
+ Action2("wegwfw:" + varnws);
1318
+ break;`
1195
1319
  const snippets = extractor.extractStrings(code);
1196
1320
 
1197
1321
  // 验证提取的片段数量
1198
1322
  expect(snippets.length).toBeGreaterThan(0);
1199
1323
 
1200
1324
  let targetSnippet = snippets[0];
1201
- // 函数参数需要拆分, 逐个参数捕获
1202
- expect(targetSnippet.originalCode).toBe(`"等级"`);
1203
- expect(targetSnippet.convertedCode).toBe(`"等级"`);
1325
+ expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
1326
+ expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
1204
1327
  expect(targetSnippet.literals).toEqual([
1205
- "等级"
1328
+ "wegwfw:"
1206
1329
  ]);
1207
1330
  expect(targetSnippet.isChanged).toBe(false);
1208
- })
1209
- test('should handle para 2', () => {
1210
- const code = `bbb.Func2("等级", level, level + 1, false, isMax);`
1331
+ });
1332
+
1333
+ // 处理 C#内容中存在多个相同的包含字符串的语句时, 要每个都提取出来
1334
+ test('should handle lost 1', () => {
1335
+ const code = readFileSync('./test/KeeperDialog.cs', 'utf8');
1211
1336
  const snippets = extractor.extractStrings(code);
1212
1337
 
1338
+ let targetSnippet = snippets[3];
1339
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"已购买";
1340
+ m_btn_buy.enabled = false;
1341
+
1342
+ //购买后自动启用`));
1343
+ expect(targetSnippet.originalCode).toBe(`"已购买"`);
1344
+ expect(targetSnippet.convertedCode).toBe(`"已购买".TR()`);
1345
+ expect(targetSnippet.literals).toEqual([
1346
+ "已购买"
1347
+ ]);
1348
+ expect(targetSnippet.isChanged).toBe(true);
1349
+
1213
1350
  // 验证提取的片段数量
1214
- expect(snippets.length).toBeGreaterThan(0);
1351
+ expect(snippets.length).toBeGreaterThanOrEqual(5)
1352
+ });
1353
+
1354
+ // 字符串中包含 `)` 符号时, 也需要捕获该字符串
1355
+ test('should handle lost 2', () => {
1356
+ const code = `m_text_runeSlot.text = Tr.Format(")", _sutraCardData.Card.RuneOpenCount);`;
1357
+ const snippets = extractor.extractStrings(code);
1215
1358
 
1216
1359
  let targetSnippet = snippets[0];
1217
- expect(targetSnippet.originalCode).toBe(`"等级"`);
1218
- expect(targetSnippet.convertedCode).toBe(`"等级"`);
1360
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`));
1361
+ expect(targetSnippet.originalCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
1362
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
1363
+ // 字符串中包含 `)` 符号时, 也需要捕获该字符串
1219
1364
  expect(targetSnippet.literals).toEqual([
1220
- "等级"
1365
+ ')'
1221
1366
  ]);
1222
1367
  expect(targetSnippet.isChanged).toBe(false);
1223
- })
1368
+
1369
+ });
1370
+
1371
+ // 处理 C#内容时, 需要匹配出包含 `(` 符号的字符串表达式
1372
+ test('should handle lost 3', () => {
1373
+ const code = `m_text_runeSlot.text = ")";`;
1374
+ const snippets = extractor.extractStrings(code);
1375
+
1376
+ let targetSnippet = snippets[0];
1377
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`")"`));
1378
+ expect(targetSnippet.originalCode).toBe(`")"`);
1379
+ expect(targetSnippet.convertedCode).toBe(`")".TR()`);
1380
+ expect(targetSnippet.literals).toEqual([
1381
+ ')'
1382
+ ]);
1383
+ expect(targetSnippet.isChanged).toBe(true);
1384
+
1224
1385
  });
1225
1386
 
1226
- describe('should handle return statement', () => {
1227
- test('should handle return statement 1', () => {
1387
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1388
+ test('should handle return statement 1', () => {
1228
1389
  const code = `return "参数错误";`;
1229
1390
  const snippets = extractor.extractStrings(code);
1230
1391
 
@@ -1237,8 +1398,10 @@ namespace TestNamespace{
1237
1398
  ]);
1238
1399
  expect(targetSnippet.isChanged).toBe(false);
1239
1400
 
1240
- })
1241
- test('should handle return statement 2', () => {
1401
+ });
1402
+
1403
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1404
+ test('should handle return statement 2', () => {
1242
1405
  const code = `return "";`;
1243
1406
  const snippets = extractor.extractStrings(code);
1244
1407
 
@@ -1251,8 +1414,10 @@ namespace TestNamespace{
1251
1414
  ]);
1252
1415
  expect(targetSnippet.isChanged).toBe(false);
1253
1416
 
1254
- })
1255
- test('should handle return statement 3', () => {
1417
+ });
1418
+
1419
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1420
+ test('should handle return statement 3', () => {
1256
1421
  const code = `return $"";`;
1257
1422
  const snippets = extractor.extractStrings(code);
1258
1423
 
@@ -1265,8 +1430,10 @@ namespace TestNamespace{
1265
1430
  ]);
1266
1431
  expect(targetSnippet.isChanged).toBe(false);
1267
1432
 
1268
- })
1269
- test('should handle return statement 4', () => {
1433
+ });
1434
+
1435
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1436
+ test('should handle return statement 4', () => {
1270
1437
  const code = `return $"fwefwe";`;
1271
1438
  const snippets = extractor.extractStrings(code);
1272
1439
 
@@ -1279,481 +1446,256 @@ namespace TestNamespace{
1279
1446
  ]);
1280
1447
  expect(targetSnippet.isChanged).toBe(false);
1281
1448
 
1282
- })
1283
- test('should handle return statement 5', () => {
1284
- const code = `return $"wefwf{fwef}";`;
1285
- const snippets = extractor.extractStrings(code);
1286
-
1287
- let targetSnippet = snippets[0];
1288
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"wefwf{fwef}"`));
1289
- expect(targetSnippet.originalCode).toBe(`$"wefwf{fwef}"`);
1290
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("wefwf{0}", fwef)`);
1291
- expect(targetSnippet.literals).toEqual([
1292
- 'wefwf{0}'
1293
- ]);
1294
- expect(targetSnippet.isChanged).toBe(true);
1295
-
1296
- })
1297
- });
1298
-
1299
- describe('should handle special characters in string', () => {
1300
- test('should handle special characters in string 8', () => {
1301
- const code = `
1302
- m_btn_paid.text = $"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}";
1303
- `;
1304
- const snippets = extractor.extractStrings(code);
1305
- {
1306
- let targetSnippet = snippets[0];
1307
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}"`));
1308
- expect(targetSnippet.originalCode).toBe(`$"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}"`);
1309
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}", paidConfig.Quantity)`);
1310
- expect(targetSnippet.literals).toEqual([
1311
- `<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}`
1312
- ]);
1313
- expect(targetSnippet.isChanged).toBe(true);
1314
- }
1315
- expect(snippets.length).toBe(1);
1316
- })
1317
- test('should handle special characters in string 9', () => {
1318
- const code = `
1319
- (GetChild($"m_textAnime{GetNextNumber()}") as TurtleTextAnimeComp)?.SetText(word);
1320
- `;
1321
- const snippets = extractor.extractStrings(code);
1322
- {
1323
- let targetSnippet = snippets[0];
1324
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_textAnime{GetNextNumber()}"`));
1325
- expect(targetSnippet.originalCode).toBe(`$"m_textAnime{GetNextNumber()}"`);
1326
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_textAnime{0}", GetNextNumber())`);
1327
- expect(targetSnippet.literals).toEqual([
1328
- `m_textAnime{0}`
1329
- ]);
1330
- expect(targetSnippet.isChanged).toBe(true);
1331
- }
1332
- expect(snippets.length).toBe(1);
1333
- })
1334
- test('should handle special characters in string 10', () => {
1335
- const code = `
1336
- (GetChild($"m_textAnime{GetNextNumber()}") as TurtleTextAnimeComp)?.SetText(word);
1337
- `;
1338
- const snippets = extractor.extractStrings(code);
1339
- {
1340
- let targetSnippet = snippets[0];
1341
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_textAnime{GetNextNumber()}"`));
1342
- expect(targetSnippet.originalCode).toBe(`$"m_textAnime{GetNextNumber()}"`);
1343
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_textAnime{0}", GetNextNumber())`);
1344
- expect(targetSnippet.literals).toEqual([
1345
- `m_textAnime{0}`
1346
- ]);
1347
- expect(targetSnippet.isChanged).toBe(true);
1348
- }
1349
- expect(snippets.length).toBe(1);
1350
- })
1351
1449
  });
1352
1450
 
1353
- describe('should handle special characters in string expression', () => {
1354
- test('should handle special characters in string expression 1', () => {
1355
- const code = `return "abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\";`;
1451
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1452
+ test('should handle return statement 5', () => {
1453
+ const code = `return $"wefwf{fwef}";`;
1356
1454
  const snippets = extractor.extractStrings(code);
1357
1455
 
1358
- let targetSnippet = snippets[0];
1359
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
1360
- expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1361
- expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1362
- expect(targetSnippet.literals).toEqual([
1363
- 'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
1364
- ]);
1365
- expect(targetSnippet.isChanged).toBe(false);
1366
- })
1367
- test('should handle special characters in string expression 3', () => {
1368
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1369
- const snippets = extractor.extractStrings(code);
1370
- {
1371
- let targetSnippet = snippets[3];
1372
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1373
- expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1374
- expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1375
- expect(targetSnippet.literals).toEqual([
1376
- '1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1377
- ]);
1378
- expect(targetSnippet.isChanged).toBe(false);
1379
- }
1380
- {
1381
- let targetSnippet = snippets[4];
1382
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1383
- expect(targetSnippet.originalCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1384
- expect(targetSnippet.convertedCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1385
- expect(targetSnippet.literals).toEqual([
1386
- '2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1387
- ]);
1388
- expect(targetSnippet.isChanged).toBe(false);
1389
- }
1390
- {
1391
- let targetSnippet = snippets[6];
1392
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1393
- expect(targetSnippet.originalCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1394
- expect(targetSnippet.convertedCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1395
- expect(targetSnippet.literals).toEqual([
1396
- '4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1397
- ]);
1398
- expect(targetSnippet.isChanged).toBe(false);
1399
- }
1400
- })
1401
- test('should handle special characters in string expression 3.1', () => {
1402
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1403
- const snippets = extractor.extractStrings(code);
1404
- {
1405
- let targetSnippet = snippets[3];
1406
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1407
- expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1408
- expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1409
- expect(targetSnippet.literals).toEqual([
1410
- '1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1411
- ]);
1412
- expect(targetSnippet.isChanged).toBe(false);
1413
- }
1414
- })
1415
- test('should handle special characters in string expression 3.2', () => {
1416
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1417
- const snippets = extractor.extractStrings(code);
1418
- {
1419
- let targetSnippet = snippets[8];
1420
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1421
- expect(targetSnippet.originalCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1422
- expect(targetSnippet.convertedCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1423
- expect(targetSnippet.literals).toEqual([
1424
- '6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1425
- ]);
1426
- expect(targetSnippet.isChanged).toBe(false);
1427
- }
1428
- {
1429
- let targetSnippet = snippets[9];
1430
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1431
- expect(targetSnippet.originalCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1432
- expect(targetSnippet.convertedCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1433
- expect(targetSnippet.literals).toEqual([
1434
- '7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1435
- ]);
1436
- expect(targetSnippet.isChanged).toBe(false);
1437
- }
1438
- })
1439
- test('should handle special characters in string expression 4', () => {
1440
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1441
- const snippets = extractor.extractStrings(code);
1442
- {
1443
- let targetSnippet = snippets[5];
1444
- // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1445
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1446
- expect(targetSnippet.originalCode).toBe(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1447
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wef)`);
1448
- expect(targetSnippet.literals).toEqual([
1449
- '3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1450
- ]);
1451
- expect(targetSnippet.isChanged).toBe(true);
1452
- }
1453
- })
1454
- test('should handle special characters in string expression 5', () => {
1455
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1456
- const snippets = extractor.extractStrings(code);
1457
- {
1458
- let targetSnippet = snippets[10];
1459
- // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1460
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1461
- expect(targetSnippet.originalCode).toBe(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1462
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1463
- expect(targetSnippet.literals).toEqual([
1464
- '8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1465
- ]);
1466
- expect(targetSnippet.isChanged).toBe(true);
1467
- }
1468
- })
1469
- test('should handle special characters in string expression 6', () => {
1470
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1471
- const snippets = extractor.extractStrings(code);
1472
- {
1473
- let targetSnippet = snippets[11];
1474
- // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1475
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1476
- expect(targetSnippet.originalCode).toBe(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1477
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1478
- expect(targetSnippet.literals).toEqual([
1479
- '9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1480
- ]);
1481
- expect(targetSnippet.isChanged).toBe(true);
1482
- }
1483
- })
1484
- test('should handle special characters in string expression 7', () => {
1485
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
1456
+ let targetSnippet = snippets[0];
1457
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"wefwf{fwef}"`));
1458
+ expect(targetSnippet.originalCode).toBe(`$"wefwf{fwef}"`);
1459
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("wefwf{0}", fwef)`);
1460
+ expect(targetSnippet.literals).toEqual([
1461
+ 'wefwf{0}'
1462
+ ]);
1463
+ expect(targetSnippet.isChanged).toBe(true);
1464
+
1465
+ });
1466
+
1467
+ // 测试处理C#字符串表达式中包含各种特殊字符的情况
1468
+ test('should handle special characters in string expression 1', () => {
1469
+ const code = `return "abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\";`;
1486
1470
  const snippets = extractor.extractStrings(code);
1487
- {
1488
- let targetSnippet = snippets[12];
1489
- // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1490
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1491
- expect(targetSnippet.originalCode).toBe(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1492
- // 需要统一匹配 `return` 语句中的字符串表达式
1493
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1494
- expect(targetSnippet.literals).toEqual([
1495
- '10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1496
- ]);
1497
- expect(targetSnippet.isChanged).toBe(true);
1498
- }
1499
- })
1471
+
1472
+ let targetSnippet = snippets[0];
1473
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
1474
+ expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1475
+ expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1476
+ expect(targetSnippet.literals).toEqual([
1477
+ 'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
1478
+ ]);
1479
+ expect(targetSnippet.isChanged).toBe(false);
1500
1480
  });
1501
1481
 
1502
- describe('should handle string expression capture range', () => {
1503
- test('should handle string expression capture range 1', () => {
1504
- const code = `
1505
- (GetChild($"vwvwe{index + 1}") as LargeLotterySpellCardComp)?.InitData(spellCardId, true);
1506
- `;
1482
+ // 测试处理在C#类成员初始赋值语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1483
+ test('should handle member initial assignment', () => {
1484
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1507
1485
  const snippets = extractor.extractStrings(code);
1508
1486
  {
1509
1487
  let targetSnippet = snippets[0];
1510
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"vwvwe{index + 1}"`));
1511
- expect(targetSnippet.originalCode).toBe(`$"vwvwe{index + 1}"`);
1512
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("vwvwe{0}", index + 1)`);
1488
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\\\"`));
1489
+ expect(targetSnippet.originalCode).toBe(`"\\\\"`);
1490
+ expect(targetSnippet.convertedCode).toBe(`"\\\\"`);
1513
1491
  expect(targetSnippet.literals).toEqual([
1514
- `vwvwe{0}`
1492
+ '\\\\'
1515
1493
  ]);
1516
- expect(targetSnippet.isChanged).toBe(true);
1494
+ expect(targetSnippet.isChanged).toBe(false);
1517
1495
  }
1518
- expect(snippets.length).toBe(1);
1519
- })
1520
- test('should handle string expression capture range 2', () => {
1521
- const code = `
1522
- (GetChild($"brrr_{index + 1}") as LargeLotterySpellCardComp)?.InitData(spellCardId, true);
1523
- `;
1524
- const snippets = extractor.extractStrings(code);
1525
1496
  {
1526
- let targetSnippet = snippets[0];
1527
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"brrr_{index + 1}"`));
1528
- expect(targetSnippet.originalCode).toBe(`$"brrr_{index + 1}"`);
1529
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("brrr_{0}", index + 1)`);
1497
+ let targetSnippet = snippets[1];
1498
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\n"`));
1499
+ expect(targetSnippet.originalCode).toBe(`"\\n"`);
1500
+ expect(targetSnippet.convertedCode).toBe(`"\\n"`);
1530
1501
  expect(targetSnippet.literals).toEqual([
1531
- `brrr_{0}`
1502
+ '\\n'
1532
1503
  ]);
1533
- expect(targetSnippet.isChanged).toBe(true);
1504
+ expect(targetSnippet.isChanged).toBe(false);
1534
1505
  }
1535
- expect(snippets.length).toBe(1);
1536
- })
1537
- test('should handle string expression capture range 3', () => {
1538
- const code = `
1539
- (GetChild($"m_card_{index}") as LargeLotterySpellCardComp)?.PlayEff();
1540
- `;
1541
- const snippets = extractor.extractStrings(code);
1542
1506
  {
1543
- let targetSnippet = snippets[0];
1544
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_card_{index}"`));
1545
- expect(targetSnippet.originalCode).toBe(`$"m_card_{index}"`);
1546
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_card_{0}", index)`);
1507
+ let targetSnippet = snippets[2];
1508
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"\\t"`));
1509
+ expect(targetSnippet.originalCode).toBe(`$"\\t"`);
1510
+ expect(targetSnippet.convertedCode).toBe(`$"\\t"`);
1547
1511
  expect(targetSnippet.literals).toEqual([
1548
- `m_card_{0}`
1512
+ '\\t'
1549
1513
  ]);
1550
- expect(targetSnippet.isChanged).toBe(true);
1514
+ expect(targetSnippet.isChanged).toBe(false);
1551
1515
  }
1552
- expect(snippets.length).toBe(1);
1553
- })
1554
1516
  });
1555
1517
 
1556
- describe('should handle string expression in method call parameter', () => {
1557
- test('should handle string expression in method call parameter 1', () => {
1558
- const code = `
1559
- namespace FaBao.UI.Comp
1560
- {
1561
- public partial class ItemCommonComp
1562
- {
1563
- public void BindData(bool needCompare = false)
1564
- {
1565
- m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
1566
- }
1567
- }
1568
- }
1569
- `;
1518
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1519
+ test('should handle special characters in string expression 3', () => {
1520
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1570
1521
  const snippets = extractor.extractStrings(code);
1571
1522
  {
1572
- let targetSnippet = snippets[0];
1573
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1574
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}", _itemConfig.PillLevel)`));
1575
- expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}", _itemConfig.PillLevel)`);
1576
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
1523
+ let targetSnippet = snippets[3];
1524
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1525
+ expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1526
+ expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1577
1527
  expect(targetSnippet.literals).toEqual([
1578
- '{0}'
1528
+ '1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1579
1529
  ]);
1580
1530
  expect(targetSnippet.isChanged).toBe(false);
1581
1531
  }
1582
- expect(snippets.length).toBe(1);
1583
- })
1584
- test('should handle string expression in method call parameter 2', () => {
1585
- const code = `
1586
- namespace FaBao.UI.Comp
1587
- {
1588
- public partial class ItemCommonComp
1589
- {
1590
- public void BindData(bool needCompare = false, long needCompare2 = 23, float needCompare2 = 324.0)
1591
- {
1592
- m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
1593
- }
1594
- }
1595
- }
1596
- `;
1597
- const snippets = extractor.extractStrings(code);
1598
1532
  {
1599
- let targetSnippet = snippets[0];
1600
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1601
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}", _itemConfig.PillLevel)`));
1602
- expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}", _itemConfig.PillLevel)`);
1603
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
1533
+ let targetSnippet = snippets[4];
1534
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1535
+ expect(targetSnippet.originalCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1536
+ expect(targetSnippet.convertedCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1604
1537
  expect(targetSnippet.literals).toEqual([
1605
- '{0}'
1538
+ '2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1606
1539
  ]);
1607
1540
  expect(targetSnippet.isChanged).toBe(false);
1608
1541
  }
1609
- expect(snippets.length).toBe(1);
1610
- })
1611
- test('should handle string expression in method call parameter 3', () => {
1612
- const code = `
1613
- namespace FaBao.UI.Comp
1614
- {
1615
- public partial class ItemCommonComp
1616
- {
1617
- public void BindData(string wkle, bool needCompare = false, int needCompare2 = false)
1618
- {
1619
- m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
1620
- }
1621
- }
1622
- }
1623
- `;
1624
- const snippets = extractor.extractStrings(code);
1625
1542
  {
1626
- let targetSnippet = snippets[0];
1627
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1628
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}", _itemConfig.PillLevel)`));
1629
- expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}", _itemConfig.PillLevel)`);
1630
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
1543
+ let targetSnippet = snippets[6];
1544
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1545
+ expect(targetSnippet.originalCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1546
+ expect(targetSnippet.convertedCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1631
1547
  expect(targetSnippet.literals).toEqual([
1632
- '{0}'
1548
+ '4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1633
1549
  ]);
1634
1550
  expect(targetSnippet.isChanged).toBe(false);
1635
1551
  }
1636
- expect(snippets.length).toBe(1);
1637
- })
1638
- test('should handle string expression in method call parameter 4', () => {
1639
- const code = `
1640
- namespace FaBao.UI.Comp
1641
- {
1642
- public partial class ItemCommonComp
1643
- {
1644
- public void BindData(string wkle = "11111", bool needCompare = false, int needCompare2 = false)
1645
- {
1646
- }
1647
-
1648
- public void BindData(string wkle = "22", string wkle = "werwer", bool needCompare = false, string wkle = "wefwewef31f", int needCompare2 = false, string wkle = "df223")
1649
- {
1650
- }
1552
+ });
1651
1553
 
1652
- public void BindData(string wkle = "(*&%\\\\*(@Fwfe))&(")
1554
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1555
+ test('should handle special characters in string expression 3.1', () => {
1556
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1557
+ const snippets = extractor.extractStrings(code);
1653
1558
  {
1559
+ let targetSnippet = snippets[3];
1560
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1561
+ expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1562
+ expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1563
+ expect(targetSnippet.literals).toEqual([
1564
+ '1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1565
+ ]);
1566
+ expect(targetSnippet.isChanged).toBe(false);
1654
1567
  }
1568
+ });
1655
1569
 
1656
- public void BindData(string we, string wkle = "aaaaa", bool needCompare = false, int needCompare2 = false)
1657
- {
1658
- m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
1659
- }
1660
- }
1661
- }
1662
- `;
1570
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1571
+ test('should handle special characters in string expression of return sentence', () => {
1572
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1663
1573
  const snippets = extractor.extractStrings(code);
1664
1574
  {
1665
- let targetSnippet = snippets[0];
1666
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1667
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"11111"`));
1668
- expect(targetSnippet.originalCode).toBe(`"11111"`);
1669
- expect(targetSnippet.convertedCode).toBe(`"11111"`);
1575
+ let targetSnippet = snippets[7];
1576
+ // 需要统一匹配 `return` 语句中的字符串表达式
1577
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1578
+ expect(targetSnippet.originalCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1579
+ expect(targetSnippet.convertedCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1670
1580
  expect(targetSnippet.literals).toEqual([
1671
- '11111'
1581
+ '5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1672
1582
  ]);
1673
1583
  expect(targetSnippet.isChanged).toBe(false);
1674
1584
  }
1585
+ });
1586
+
1587
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1588
+ test('should handle special characters in string expression 3.2', () => {
1589
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1590
+ const snippets = extractor.extractStrings(code);
1675
1591
  {
1676
- let targetSnippet = snippets[1];
1677
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1678
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"22"`));
1679
- expect(targetSnippet.originalCode).toBe(`"22"`);
1680
- expect(targetSnippet.convertedCode).toBe(`"22"`);
1592
+ let targetSnippet = snippets[8];
1593
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1594
+ expect(targetSnippet.originalCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1595
+ expect(targetSnippet.convertedCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1681
1596
  expect(targetSnippet.literals).toEqual([
1682
- '22'
1597
+ '6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1683
1598
  ]);
1684
1599
  expect(targetSnippet.isChanged).toBe(false);
1685
1600
  }
1686
1601
  {
1687
- let targetSnippet = snippets[2];
1688
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1689
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"werwer"`));
1690
- expect(targetSnippet.originalCode).toBe(`"werwer"`);
1691
- expect(targetSnippet.convertedCode).toBe(`"werwer"`);
1602
+ let targetSnippet = snippets[9];
1603
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1604
+ expect(targetSnippet.originalCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1605
+ expect(targetSnippet.convertedCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1692
1606
  expect(targetSnippet.literals).toEqual([
1693
- 'werwer'
1607
+ '7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1694
1608
  ]);
1695
1609
  expect(targetSnippet.isChanged).toBe(false);
1696
1610
  }
1611
+ });
1612
+
1613
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1614
+ test('should handle special characters in string expression 4', () => {
1615
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1616
+ const snippets = extractor.extractStrings(code);
1697
1617
  {
1698
- let targetSnippet = snippets[3];
1699
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1700
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"wefwewef31f"`));
1701
- expect(targetSnippet.originalCode).toBe(`"wefwewef31f"`);
1702
- expect(targetSnippet.convertedCode).toBe(`"wefwewef31f"`);
1618
+ let targetSnippet = snippets[5];
1619
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1620
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1621
+ expect(targetSnippet.originalCode).toBe(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1622
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wef)`);
1703
1623
  expect(targetSnippet.literals).toEqual([
1704
- 'wefwewef31f'
1624
+ '3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1705
1625
  ]);
1706
- expect(targetSnippet.isChanged).toBe(false);
1626
+ expect(targetSnippet.isChanged).toBe(true);
1707
1627
  }
1628
+ });
1629
+
1630
+ // 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
1631
+ test('should handle special characters in string expression 5', () => {
1632
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1633
+ const snippets = extractor.extractStrings(code);
1708
1634
  {
1709
- let targetSnippet = snippets[4];
1710
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1711
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"df223"`));
1712
- expect(targetSnippet.originalCode).toBe(`"df223"`);
1713
- expect(targetSnippet.convertedCode).toBe(`"df223"`);
1635
+ let targetSnippet = snippets[10];
1636
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1637
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1638
+ expect(targetSnippet.originalCode).toBe(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1639
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1714
1640
  expect(targetSnippet.literals).toEqual([
1715
- 'df223'
1641
+ '8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1716
1642
  ]);
1717
- expect(targetSnippet.isChanged).toBe(false);
1643
+ expect(targetSnippet.isChanged).toBe(true);
1718
1644
  }
1645
+ });
1646
+
1647
+ // 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
1648
+ test('should handle special characters in string expression 6', () => {
1649
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1650
+ const snippets = extractor.extractStrings(code);
1719
1651
  {
1720
- let targetSnippet = snippets[5];
1721
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1722
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"(*&%\\\\*(@Fwfe))&("`));
1723
- expect(targetSnippet.originalCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
1724
- expect(targetSnippet.convertedCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
1652
+ let targetSnippet = snippets[11];
1653
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1654
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1655
+ expect(targetSnippet.originalCode).toBe(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1656
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1725
1657
  expect(targetSnippet.literals).toEqual([
1726
- '(*&%\\\\*(@Fwfe))&('
1658
+ '9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1727
1659
  ]);
1728
- expect(targetSnippet.isChanged).toBe(false);
1660
+ expect(targetSnippet.isChanged).toBe(true);
1729
1661
  }
1662
+ });
1663
+
1664
+ // 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
1665
+ test('should handle special characters in string expression 7', () => {
1666
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1667
+ const snippets = extractor.extractStrings(code);
1730
1668
  {
1731
- let targetSnippet = snippets[6];
1732
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1733
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"aaaaa"`));
1734
- expect(targetSnippet.originalCode).toBe(`"aaaaa"`);
1735
- expect(targetSnippet.convertedCode).toBe(`"aaaaa"`);
1669
+ let targetSnippet = snippets[12];
1670
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1671
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1672
+ expect(targetSnippet.originalCode).toBe(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1673
+ // 需要统一匹配 `return` 语句中的字符串表达式
1674
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1736
1675
  expect(targetSnippet.literals).toEqual([
1737
- 'aaaaa'
1676
+ '10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1738
1677
  ]);
1739
- expect(targetSnippet.isChanged).toBe(false);
1678
+ expect(targetSnippet.isChanged).toBe(true);
1740
1679
  }
1680
+ });
1681
+
1682
+ test('should handle optional value assignment', () => {
1683
+ const code = `m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";`;
1684
+ const snippets = extractor.extractStrings(code);
1741
1685
  {
1742
- let targetSnippet = snippets[7];
1743
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
1744
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
1745
- expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
1746
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
1686
+ let targetSnippet = snippets[0];
1687
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
1688
+ expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
1689
+ expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
1747
1690
  expect(targetSnippet.literals).toEqual([
1748
- '{0}阶'
1691
+ ''
1749
1692
  ]);
1750
- expect(targetSnippet.isChanged).toBe(false);
1693
+ expect(targetSnippet.isChanged).toBe(true);
1751
1694
  }
1752
- })
1753
1695
  });
1754
1696
 
1755
- describe('should handle string in comment', () => {
1756
- test('should handle string in comment 1', () => {
1697
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1698
+ test('should handle string in comment 1', () => {
1757
1699
  const code = `
1758
1700
  protected override void OnShow(object param)
1759
1701
  {
@@ -1771,8 +1713,10 @@ namespace FaBao.UI.Comp
1771
1713
  ]);
1772
1714
  expect(targetSnippet.isChanged).toBe(false);
1773
1715
  }
1774
- })
1775
- test('should handle string in comment 2', () => {
1716
+ });
1717
+
1718
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1719
+ test('should handle string in comment 2', () => {
1776
1720
  const code = `
1777
1721
  protected override void OnShow(object param)
1778
1722
  {
@@ -1790,8 +1734,10 @@ namespace FaBao.UI.Comp
1790
1734
  ]);
1791
1735
  expect(targetSnippet.isChanged).toBe(false);
1792
1736
  }
1793
- })
1794
- test('should handle string in comment 3', () => {
1737
+ });
1738
+
1739
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1740
+ test('should handle string in comment 3', () => {
1795
1741
  const code = `
1796
1742
  protected override void OnShow(object param)
1797
1743
  {
@@ -1809,8 +1755,10 @@ namespace FaBao.UI.Comp
1809
1755
  ]);
1810
1756
  expect(targetSnippet.isChanged).toBe(true);
1811
1757
  }
1812
- })
1813
- test('should handle string in comment 4', () => {
1758
+ });
1759
+
1760
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1761
+ test('should handle string in comment 4', () => {
1814
1762
  const code = `
1815
1763
  protected override void OnShow(object param)
1816
1764
  {
@@ -1828,8 +1776,10 @@ namespace FaBao.UI.Comp
1828
1776
  ]);
1829
1777
  expect(targetSnippet.isChanged).toBe(false);
1830
1778
  }
1831
- })
1832
- test('should handle string in comment 5', () => {
1779
+ });
1780
+
1781
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1782
+ test('should handle string in comment 5', () => {
1833
1783
  const code = `
1834
1784
  protected override void OnShow(object param)
1835
1785
  {
@@ -1847,799 +1797,955 @@ namespace FaBao.UI.Comp
1847
1797
  ]);
1848
1798
  expect(targetSnippet.isChanged).toBe(true);
1849
1799
  }
1850
- })
1851
- test('should handle string in comment 5', () => {
1800
+ });
1801
+
1802
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1803
+ test('should handle complex string 1', () => {
1852
1804
  const code = `
1853
- m_text_percent.text = $"{curGoodsConfig.RebateRate}%";
1854
- m_name_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Name);
1855
- m_thumbnail_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Thumbnail);
1856
- m_btn_buy.text = $"¥ {curGoodsConfig.Price}";
1857
- m_text_timeleft.text = curShop.TimeLimitData.RemainTime().TR() + "后消失".TR();
1805
+ infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";
1806
+ infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
1858
1807
  `;
1859
1808
  const snippets = extractor.extractStrings(code);
1860
1809
  {
1861
- let targetSnippet = snippets[2];
1862
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`));
1863
- expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
1864
- expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
1810
+ let targetSnippet = snippets[0];
1811
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`));
1812
+ expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
1813
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1865
1814
  expect(targetSnippet.literals).toEqual([
1866
- '后消失'
1815
+ '伤害类型: [color={0}][b]{1}[/b][/color]\n'
1867
1816
  ]);
1868
- expect(targetSnippet.isChanged).toBe(false);
1817
+ expect(targetSnippet.isChanged).toBe(true);
1869
1818
  }
1870
- })
1871
1819
  });
1872
1820
 
1873
- describe('should handle switch', () => {
1874
- test('should handle switch 1', () => {
1821
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1822
+ test('should handle complex string 2', () => {
1875
1823
  const code = `
1876
- switch(condition){
1877
- default:
1878
- Log.Warning("实时更新自:" + rankData.Type);
1879
- break;
1880
- }`
1824
+ infoStr += Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType);
1825
+ infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
1826
+ `;
1881
1827
  const snippets = extractor.extractStrings(code);
1828
+ {
1829
+ let targetSnippet = snippets[0];
1830
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`));
1831
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1832
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1833
+ expect(targetSnippet.literals).toEqual([
1834
+ '伤害类型: [color={0}][b]{1}[/b][/color]\n'
1835
+ ]);
1836
+ expect(targetSnippet.isChanged).toBe(false);
1837
+ }
1838
+ });
1882
1839
 
1883
- // 验证提取的片段数量
1884
- expect(snippets.length).toBeGreaterThan(0);
1840
+ // 正确处理C# 字符串表达式中递归内嵌字符串表达式的情况: 需要正确转换内插字符串格式
1841
+ test('should handle fix interpolated string nested literals', () => {
1842
+ const code = `infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";`;
1843
+ const snippets = extractor.extractStrings(code);
1844
+ {
1845
+ let targetSnippet = snippets[0];
1846
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`));
1847
+ expect(targetSnippet.originalCode).toBe(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`);
1848
+ // 需要正确转换内插字符串格式
1849
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("是否暴击: [b]{0}[/b]\n", (_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否"))`);
1850
+ expect(targetSnippet.literals).toEqual([
1851
+ '是否暴击: [b]{0}[/b]\n'
1852
+ ]);
1853
+ expect(targetSnippet.isChanged).toBe(true);
1854
+ }
1855
+ });
1885
1856
 
1886
- let targetSnippet = snippets[0];
1887
- expect(targetSnippet.originalCode).toBe(`"实时更新自:" + rankData.Type`);
1888
- // 普通函数参数不需要追加 `.TR()`, 除非包含由 `+` 符号连接字符串等的情况
1889
- expect(targetSnippet.convertedCode).toBe(`"实时更新自:" + rankData.Type`);
1890
- expect(targetSnippet.literals).toEqual([
1891
- "实时更新自:"
1892
- ]);
1893
- expect(targetSnippet.isChanged).toBe(false);
1894
- })
1895
- test('should handle switch 2', () => {
1857
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1858
+ test('should handle complex string with optional value 3', () => {
1896
1859
  const code = `
1897
- switch(kwjkwlje){
1898
- case (int)RANK_TYPE.天梯段位:
1899
- Debug.Log1("jkwhfehwfjkh:" + rank2Data.T4zype);
1900
- break;
1901
- }`
1860
+ else if (battleModel.CurGameType == BattleModel.GameType.Teamwork)
1861
+ {
1862
+ //合作模式显示队友名称
1863
+ m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";
1864
+ }
1865
+ `;
1902
1866
  const snippets = extractor.extractStrings(code);
1867
+ {
1868
+ let targetSnippet = snippets[0];
1869
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
1870
+ expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
1871
+ expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
1872
+ expect(targetSnippet.literals).toEqual([
1873
+ ''
1874
+ ]);
1875
+ expect(targetSnippet.isChanged).toBe(true);
1876
+ }
1877
+ });
1903
1878
 
1904
- // 验证提取的片段数量, switch 语句中包含普通字符串的情况
1905
- expect(snippets.length).toBeGreaterThan(0);
1906
-
1907
- let targetSnippet = snippets[0];
1908
- expect(targetSnippet.originalCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
1909
- expect(targetSnippet.convertedCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
1910
- expect(targetSnippet.literals).toEqual([
1911
- "jkwhfehwfjkh:"
1912
- ]);
1913
- expect(targetSnippet.isChanged).toBe(false);
1914
- })
1915
- test('should handle switch 3', () => {
1879
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1880
+ test('should handle complex string 4', () => {
1916
1881
  const code = `
1917
- switch(lkw){
1918
- case V434:
1882
+ private void OnBtnIaa(EventContext context)
1883
+ {
1884
+ var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
1885
+ if (iaaConfig == null)
1919
1886
  {
1920
- Action2("wegwfw:" + varnws);
1921
- break;
1887
+ return;
1922
1888
  }
1923
- }`
1924
- const snippets = extractor.extractStrings(code);
1925
-
1926
- // 验证提取的片段数量
1927
- expect(snippets.length).toBeGreaterThan(0);
1928
1889
 
1929
- let targetSnippet = snippets[0];
1930
- expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
1931
- expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
1932
- expect(targetSnippet.literals).toEqual([
1933
- "wegwfw:"
1934
- ]);
1935
- expect(targetSnippet.isChanged).toBe(false);
1936
- })
1937
- test('should handle switch 4', () => {
1938
- const code = `
1939
- case V434:
1940
- Action2("wegwfw:" + varnws);
1941
- break;`
1890
+ string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
1891
+ this.GetModel<IAAModel>().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
1892
+ }
1893
+ `;
1942
1894
  const snippets = extractor.extractStrings(code);
1895
+ {
1896
+ let targetSnippet = snippets[0];
1897
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`));
1898
+ expect(targetSnippet.originalCode).toBe(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`);
1899
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("观看视频后\n等待时间减少{0}分钟", iaaConfig.EffectValue / 60)`);
1900
+ expect(targetSnippet.literals).toEqual([
1901
+ '观看视频后\n等待时间减少{0}分钟'
1902
+ ]);
1903
+ expect(targetSnippet.isChanged).toBe(true);
1904
+ }
1905
+ });
1943
1906
 
1944
- // 验证提取的片段数量
1945
- expect(snippets.length).toBeGreaterThan(0);
1907
+ // 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
1908
+ test('should handle strings in function call arguments', () => {
1909
+ const code = `
1910
+ private void OnBtnIaa(EventContext context)
1911
+ {
1912
+ var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
1913
+ if (iaaConfig == null)
1914
+ {
1915
+ return;
1916
+ }
1946
1917
 
1947
- let targetSnippet = snippets[0];
1948
- expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
1949
- expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
1950
- expect(targetSnippet.literals).toEqual([
1951
- "wegwfw:"
1952
- ]);
1953
- expect(targetSnippet.isChanged).toBe(false);
1954
- })
1918
+ string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
1919
+ this.GetModel<IAAModel>().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
1920
+ }
1921
+ `;
1922
+ const snippets = extractor.extractStrings(code);
1923
+ {
1924
+ let targetSnippet = snippets[1];
1925
+ // 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
1926
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"加速时间"`));
1927
+ expect(targetSnippet.originalCode).toBe(`"加速时间"`);
1928
+ expect(targetSnippet.convertedCode).toBe(`"加速时间"`);
1929
+ expect(targetSnippet.literals).toEqual([
1930
+ '加速时间'
1931
+ ]);
1932
+ expect(targetSnippet.isChanged).toBe(false);
1933
+ }
1955
1934
  });
1956
1935
 
1957
- describe('should handle thriple expression with string expression', () => {
1958
- test('should handle thriple expression with string expression 1', () => {
1959
- const code = `m_btn_confirm.title = isUsed ? "使用中" : "使用";`;
1936
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1937
+ test('should handle string in comment 5', () => {
1938
+ const code = `
1939
+ m_text_percent.text = $"{curGoodsConfig.RebateRate}%";
1940
+ m_name_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Name);
1941
+ m_thumbnail_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Thumbnail);
1942
+ m_btn_buy.text = $"¥ {curGoodsConfig.Price}";
1943
+ m_text_timeleft.text = curShop.TimeLimitData.RemainTime().TR() + "后消失".TR();
1944
+ `;
1960
1945
  const snippets = extractor.extractStrings(code);
1961
1946
  {
1962
- let snippet = snippets[0];
1963
- expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
1964
- expect(snippet.convertedCode).toBe('isUsed ? "使用中".TR() : "使用".TR()');
1965
- expect(snippet.literals).toEqual([
1966
- '使用中',
1967
- '使用'
1947
+ let targetSnippet = snippets[2];
1948
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`));
1949
+ expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
1950
+ expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
1951
+ expect(targetSnippet.literals).toEqual([
1952
+ '后消失'
1968
1953
  ]);
1954
+ expect(targetSnippet.isChanged).toBe(false);
1969
1955
  }
1970
- })
1971
- test('should handle thriple expression with string expression 2', () => {
1972
- const code = `wefe.text = isUsed ? "使用中" : "使用";`;
1956
+ });
1957
+
1958
+ // 正确处理C# `switch` 表达式形式中的字符串表达式
1959
+ test('should handle switch expression string', () => {
1960
+ const code = `
1961
+ case (int)State.Lock:
1962
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
1963
+ break;
1964
+ `;
1973
1965
  const snippets = extractor.extractStrings(code);
1974
1966
  {
1975
- let snippet = snippets[0];
1976
- expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
1977
- expect(snippet.convertedCode).toBe('isUsed ? "使用中".TR() : "使用".TR()');
1978
- expect(snippet.literals).toEqual([
1979
- '使用中',
1980
- '使用'
1967
+ let targetSnippet = snippets[0];
1968
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
1969
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
1970
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
1971
+ expect(targetSnippet.literals).toEqual([
1972
+ '灵田{0}级解锁'
1981
1973
  ]);
1974
+ expect(targetSnippet.isChanged).toBe(true);
1982
1975
  }
1983
- })
1984
- test('should handle thriple expression with string expression 3', () => {
1985
- const code = `wefetext = isUsed ? "使用中" : "使用";`;
1976
+ });
1977
+
1978
+ // 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
1979
+ test('should handle comment boundary 1', () => {
1980
+ const code = `
1981
+ //上锁
1982
+ case (int)State.Lock:
1983
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
1984
+ break;
1985
+ `;
1986
1986
  const snippets = extractor.extractStrings(code);
1987
1987
  {
1988
- let snippet = snippets[0];
1989
- expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
1990
- expect(snippet.convertedCode).toBe('isUsed ? "使用中" : "使用"');
1991
- expect(snippet.literals).toEqual([
1992
- '使用中',
1993
- '使用'
1988
+ let targetSnippet = snippets[0];
1989
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
1990
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
1991
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
1992
+ expect(targetSnippet.literals).toEqual([
1993
+ '灵田{0}级解锁'
1994
1994
  ]);
1995
+ expect(targetSnippet.isChanged).toBe(true);
1995
1996
  }
1996
- })
1997
- test('should handle thriple expression with string expression 4', () => {
1998
- const code = `wefetext(isUsed ? "使用中" : "使用");`;
1997
+ });
1998
+
1999
+ // 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
2000
+ test('should handle comment boundary 2', () => {
2001
+ const code = `
2002
+ // klwjfe
2003
+ namespace TestNamespace{
2004
+ // lkwjfe
2005
+ public class FK{
2006
+ // klwf
2007
+ public void TestM(){
2008
+ // 模拟状态变量
2009
+ var _state = (int)State.Lock;
2010
+ // 模拟土地ID变量
2011
+ switch (_state){
2012
+ //上锁
2013
+ case (int)State.Lock:
2014
+ // lkwjefl
2015
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
2016
+ // klwejf
2017
+ break;
2018
+ // lkwjf
2019
+ }
2020
+ // klwjfel
2021
+ }
2022
+ }
2023
+ }
2024
+ `;
1999
2025
  const snippets = extractor.extractStrings(code);
2000
2026
  {
2001
- let snippet = snippets[0];
2002
- expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
2003
- expect(snippet.convertedCode).toBe('isUsed ? "使用中" : "使用"');
2004
- expect(snippet.literals).toEqual([
2005
- '使用中',
2006
- '使用'
2027
+ let targetSnippet = snippets[0];
2028
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
2029
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
2030
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
2031
+ expect(targetSnippet.literals).toEqual([
2032
+ '灵田{0}级解锁'
2007
2033
  ]);
2034
+ expect(targetSnippet.isChanged).toBe(true);
2008
2035
  }
2009
- })
2010
- });
2011
-
2012
- describe('基本字符串提取', () => {
2013
- test('should extract plain strings', () => {
2014
- const code = 'Console.WriteLine("Hello, world!");';
2015
- const snippets = extractor.extractStrings(code);
2016
-
2017
- expect(snippets.length).toBe(1);
2018
- expect(snippets[0].literals).toEqual(['Hello, world!']);
2019
- })
2020
2036
  });
2021
-
2022
- describe('已带.TR()后缀的字符串处理', () => {
2023
- test('should handle strings with .TR() already appended', () => {
2024
- const code = 'obj.prop = "Hello, world!".TR();';
2025
- const snippets = extractor.extractStrings(code);
2026
-
2027
- expect(snippets.length).toBe(1);
2028
- expect(snippets[0].originalCode).toBe('"Hello, world!".TR()');
2029
- expect(snippets[0].convertedCode).toBe('"Hello, world!".TR()');
2030
- expect(snippets[0].isChanged).toBe(false);
2031
- })
2032
- test('should not add .TR() to function arguments', () => {
2033
- const code = 'CallFunc("Hello, world!");';
2034
- const snippets = extractor.extractStrings(code);
2035
-
2036
- expect(snippets.length).toBe(1);
2037
- expect(snippets[0].originalCode).toBe('"Hello, world!"');
2038
- expect(snippets[0].convertedCode).toBe('"Hello, world!"');
2039
- })
2040
- test('should not add .TR() to regular property assignments', () => {
2041
- const code = 'obj.prop = "Hello, world!";';
2042
- const snippets = extractor.extractStrings(code);
2043
-
2044
- expect(snippets.length).toBe(1);
2045
- expect(snippets[0].originalCode).toBe('"Hello, world!"');
2046
- expect(snippets[0].convertedCode).toBe('"Hello, world!"');
2047
- })
2048
- test('should handle complex concatenation with existing .TR() calls', () => {
2049
- const code = 'var hello3 = "Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!";';
2050
- const snippets = extractor.extractStrings(code);
2051
-
2052
- expect(snippets.length).toBe(1);
2053
- expect(snippets[0].originalCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!"');
2054
- expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop.TR() + "!".TR()');
2055
- })
2056
- test('should not add .TR() to strings already having it', () => {
2057
- const code = 'var wkle = "Hello".TR();';
2037
+ // 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
2038
+ test('should handle comment boundary 3', () => {
2039
+ const code = `
2040
+ // klwjfe
2041
+ namespace TestNamespace{
2042
+ // lkwjfe
2043
+ public class FK{
2044
+ // wlkjelj
2045
+ private int Llkwe = 23;
2046
+ // lkjlkj
2047
+ private int Dwekl { get; set; } = 23;
2048
+ // jklwef
2049
+ private static int Dwekl { get; set; } = 23;
2050
+ // lkwjeflk
2051
+ public static void TestM(){}
2052
+ // wesd
2053
+ public static void TestM()
2054
+ {
2055
+ }
2056
+ // wesd
2057
+ public static void TestM(){
2058
+ }
2059
+ // klwf
2060
+ public void TestM(){
2061
+ // 模拟状态变量
2062
+ var _state = (int)State.Lock;
2063
+ // 模拟土地ID变量
2064
+ switch (_state){
2065
+ //上锁
2066
+ case (int)State.Lock:
2067
+ // lkwjefl
2068
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
2069
+ // klwejf
2070
+ break;
2071
+ // lkwjf
2072
+ }
2073
+ // klwjfel
2074
+ }
2075
+ }
2076
+ }
2077
+ `;
2058
2078
  const snippets = extractor.extractStrings(code);
2059
-
2060
- const snippet = snippets[0];
2061
- expect(snippet.originalCode).toBe('"Hello".TR()');
2062
- expect(snippet.convertedCode).toBe('"Hello".TR()');
2063
- expect(snippet.literals).toEqual(['Hello']);
2064
- expect(snippet.isChanged).toBe(false);
2065
- })
2079
+ {
2080
+ let targetSnippet = snippets[0];
2081
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
2082
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
2083
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
2084
+ expect(targetSnippet.literals).toEqual([
2085
+ '灵田{0}级解锁'
2086
+ ]);
2087
+ expect(targetSnippet.isChanged).toBe(true);
2088
+ }
2066
2089
  });
2067
2090
 
2068
- describe('字符串模板/内插字符串处理', () => {
2069
- test('should handle $"" string templates', () => {
2070
- const code = 'var label1 = $"Hello, {name}!";';
2071
- const snippets = extractor.extractStrings(code);
2072
-
2073
- expect(snippets.length).toBe(1);
2074
- expect(snippets[0].originalCode).toBe('$"Hello, {name}!"');
2075
- expect(snippets[0].literals).toContain('Hello, {0}!');
2076
- expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
2077
- })
2078
- test('should handle $"" string templates in function calls', () => {
2079
- const code = 'CallFunc($"Hello, world: {obj.Func1()}");';
2080
- const snippets = extractor.extractStrings(code);
2081
-
2082
- expect(snippets.length).toBe(1);
2083
- expect(snippets[0].originalCode).toBe('$"Hello, world: {obj.Func1()}"');
2084
- expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, world: {0}", obj.Func1())');
2085
- })
2086
- test('should handle .text = assignments with string templates', () => {
2087
- const code = 'label.text = $"pre{Func()}sub";';
2088
- const snippets = extractor.extractStrings(code);
2089
-
2090
- expect(snippets.length).toBe(1);
2091
- expect(snippets[0].originalCode).toBe('$"pre{Func()}sub"');
2092
- expect(snippets[0].literals).toContain('pre{0}sub');
2093
- expect(snippets[0].convertedCode).toBe('Tr.Format("pre{0}sub", Func())');
2094
- })
2095
- test('should extract and convert string templates', () => {
2096
- const code = 'Ljk.Ilk($"Hello, {name}!");';
2097
- const snippets = extractor.extractStrings(code);
2098
-
2099
- expect(snippets.length).toBeGreaterThan(0);
2100
- const snippet = snippets[0];
2101
- expect(snippet.originalCode).toBe('$"Hello, {name}!"');
2102
- expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
2103
- expect(snippet.literals).toEqual(['Hello, {0}!']);
2104
- expect(snippet.isChanged).toBe(true);
2105
- })
2106
- test('should handle @$"" and $@"" formats', () => {
2107
- const code = 'Fcx.Kjl(@$"Hello, {name}!");';
2108
- const snippets = extractor.extractStrings(code);
2109
-
2110
- const snippet = snippets[0];
2111
- expect(snippet.originalCode).toBe('@$"Hello, {name}!"');
2112
- expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
2113
- expect(snippet.literals).toEqual(['Hello, {0}!']);
2114
- expect(snippet.isChanged).toBe(true);
2115
- })
2116
- test('should handle string template with format specifier', () => {
2117
- const code = `infoStr += $"最终抗性: {resistReduce:F}\\n";`
2118
- const snippets = extractor.extractStrings(code);
2091
+ // 正确识别C#文档注释边界, 类、方法、类成员的文档注释需要直接忽略捕获, 不参与字符串表达式提取; 文档注释通常位于类、方法、类成员的定义上方, 文档注释的格式形如: ` /// <xxx> yyy\n /// </xxx>` 或 ` /// <xxx yyy/>`, 其中xxx为标签, yyy为注释内容, `<xxx>`和`</xxx>` 不一定在同一行或不同行, ` /// <xxx> yyy\n` 不一定只有一行; 文档注释通常以`<xxx>`标签开始, 以`</xxx>`标签结束, 或者以 `<xxx ` 开始, 以 `/>` 结束。
2092
+ test('should handle doc comment boundary for class/method/members', () => {
2093
+ const code = `
2094
+ /// <summary>
2095
+ /// "fwef 类注释1"
2096
+ /// $"fwef 类注释2"
2097
+ /// $"fwef 类注释2 {wef}"
2098
+ /// wef $"fwef 类注释2 {wef}" wef
2099
+ /// </summary>
2100
+ /// <sumary "lkwjfelj"/>
2101
+ public partial class TimeLimitedChargeDialog
2102
+ {
2103
+ /// <summary>
2104
+ /// $" Property成员注释 dsvdf {wlpoe}"
2105
+ /// </summary>
2106
+ private int Ljfw { get; set; } = 342;
2119
2107
 
2120
- // 验证提取的片段数量
2121
- expect(snippets.length).toBeGreaterThan(0);
2108
+ /// <summary>
2109
+ /// $" Field成员注释 wfewe {vvwe}"
2110
+ /// $" Field成员注释 wfewe {vvwe}" wfe
2111
+ /// </summary>
2112
+ private int Ljfw = 342;
2122
2113
 
2123
- let targetSnippet = snippets[0];
2124
- expect(targetSnippet.originalCode).toBe(`$"最终抗性: {resistReduce:F}\\n"`);
2125
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("最终抗性: {0:F}\\n", resistReduce)`);
2126
- expect(targetSnippet.literals).toEqual(['最终抗性: {0:F}\\n']);
2127
- expect(targetSnippet.isChanged).toBe(true);
2128
- })
2129
- test('should handle fix interpolated string nested literals', () => {
2130
- const code = `infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";`;
2114
+ /// <summary>
2115
+ ///</summary>
2116
+ private int Ljfw = 342;
2117
+
2118
+ /// <summary>
2119
+ /// " wef方法注释 wef"
2120
+ /// $" wef方法注释 wef"
2121
+ /// $" wef方法注释 wef{wef}"
2122
+ /// wfe $" wef方法注释 wef{wef}"
2123
+ /// wfe $" wef方法注释 wef{wef}" wef
2124
+ ///wfe $" wef方法注释 wef{wef}" wef
2125
+ /// wefw $" wef方法注释 wef{wef}" we
2126
+ /// </summary>
2127
+ /// <sumary wf $"klwf"/>
2128
+ /// <param name="force">是否重新创建页签列表</param>
2129
+ private void RefreshGoodsInfo()
2130
+ {
2131
+ m_text_timeleft.text = curShop.TimeLimitData.RemainTime() + "后消失";
2132
+ }
2133
+ }
2134
+ `;
2131
2135
  const snippets = extractor.extractStrings(code);
2132
2136
  {
2133
2137
  let targetSnippet = snippets[0];
2134
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`));
2135
- expect(targetSnippet.originalCode).toBe(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`);
2136
- // 需要正确转换内插字符串格式
2137
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("是否暴击: [b]{0}[/b]\n", (_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否"))`);
2138
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2139
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime() + "后消失"`));
2140
+ expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime() + "后消失"`);
2141
+ expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
2138
2142
  expect(targetSnippet.literals).toEqual([
2139
- '是否暴击: [b]{0}[/b]\n'
2143
+ '后消失'
2140
2144
  ]);
2141
2145
  expect(targetSnippet.isChanged).toBe(true);
2142
2146
  }
2143
- })
2144
2147
  });
2145
2148
 
2146
- describe('string.Format和Tr.Format处理', () => {
2147
- test('should convert string.Format to Tr.Format', () => {
2148
- const code = 'string.Format("Hello, ", name, "!");';
2149
+ // 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
2150
+ test('should handle string expression in method call parameter 1', () => {
2151
+ const code = `
2152
+ namespace FaBao.UI.Comp
2153
+ {
2154
+ public partial class ItemCommonComp
2155
+ {
2156
+ public void BindData(bool needCompare = false)
2157
+ {
2158
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2159
+ }
2160
+ }
2161
+ }
2162
+ `;
2149
2163
  const snippets = extractor.extractStrings(code);
2150
-
2164
+ {
2165
+ let targetSnippet = snippets[0];
2166
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2167
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2168
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2169
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2170
+ expect(targetSnippet.literals).toEqual([
2171
+ '{0}阶'
2172
+ ]);
2173
+ expect(targetSnippet.isChanged).toBe(false);
2174
+ }
2151
2175
  expect(snippets.length).toBe(1);
2152
- // string.Format(...) 形式的字符串表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
2153
- // string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
2154
- expect(snippets[0].originalCode).toBe('string.Format("Hello, ", name, "!")');
2155
- expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, ", name, "!")');
2156
- })
2157
- test('should convert string.Format to Tr.Format', () => {
2158
- const code = 'string.Format("Hello, {0}!", name);';
2159
- const snippets = extractor.extractStrings(code);
2160
-
2161
- expect(snippets.length).toBeGreaterThan(0);
2162
- const snippet = snippets[0];
2163
- expect(snippet.originalCode).toBe('string.Format("Hello, {0}!", name)');
2164
- expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
2165
- expect(snippet.literals).toEqual(['Hello, {0}!']);
2166
- expect(snippet.isChanged).toBe(true);
2167
- })
2168
2176
  });
2169
2177
 
2170
- describe('.text赋值语句处理', () => {
2171
- test('should handle string.Format in .text assignments', () => {
2172
- const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
2178
+ // 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
2179
+ test('should handle string expression in method call parameter 2', () => {
2180
+ const code = `
2181
+ namespace FaBao.UI.Comp
2182
+ {
2183
+ public partial class ItemCommonComp
2184
+ {
2185
+ public void BindData(bool needCompare = false, long needCompare2 = 23, float needCompare2 = 324.0)
2186
+ {
2187
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2188
+ }
2189
+ }
2190
+ }
2191
+ `;
2173
2192
  const snippets = extractor.extractStrings(code);
2174
-
2193
+ {
2194
+ let targetSnippet = snippets[0];
2195
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2196
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2197
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2198
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2199
+ expect(targetSnippet.literals).toEqual([
2200
+ '{0}阶'
2201
+ ]);
2202
+ expect(targetSnippet.isChanged).toBe(false);
2203
+ }
2175
2204
  expect(snippets.length).toBe(1);
2176
- expect(snippets[0].originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
2177
- expect(snippets[0].literals).toContain('pre');
2178
- expect(snippets[0].literals).toContain('sub');
2179
- })
2180
- test('should handle .text = assignments with function calls', () => {
2181
- const code = 'label.text = Func();';
2182
- const snippets = extractor.extractStrings(code);
2205
+ });
2183
2206
 
2184
- expect(snippets.length).toBe(1);
2185
- expect(snippets[0].originalCode).toBe('Func()');
2186
- expect(snippets[0].convertedCode).toBe('Func().TR()');
2187
- })
2188
- test('should handle .text = assignments with multiple function calls', () => {
2189
- const code = 'label.text = Func() + Func();';
2207
+ // 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
2208
+ test('should handle string expression in method call parameter 3', () => {
2209
+ const code = `
2210
+ namespace FaBao.UI.Comp
2211
+ {
2212
+ public partial class ItemCommonComp
2213
+ {
2214
+ public void BindData(string wkle, bool needCompare = false, int needCompare2 = false)
2215
+ {
2216
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2217
+ }
2218
+ }
2219
+ }
2220
+ `;
2190
2221
  const snippets = extractor.extractStrings(code);
2191
-
2222
+ {
2223
+ let targetSnippet = snippets[0];
2224
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2225
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2226
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2227
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2228
+ expect(targetSnippet.literals).toEqual([
2229
+ '{0}阶'
2230
+ ]);
2231
+ expect(targetSnippet.isChanged).toBe(false);
2232
+ }
2192
2233
  expect(snippets.length).toBe(1);
2193
- expect(snippets[0].originalCode).toBe('Func() + Func()');
2194
- expect(snippets[0].convertedCode).toBe('Func().TR() + Func().TR()');
2195
- })
2196
- test('should handle .text = assignments with string.Format', () => {
2197
- const code = 'label.text = string.Format("pre{0:F2}{1}", Func(), "sub") + other;';
2198
- const snippets = extractor.extractStrings(code);
2234
+ });
2199
2235
 
2200
- expect(snippets.length).toBe(1);
2201
- expect(snippets[0].originalCode).toBe('string.Format("pre{0:F2}{1}", Func(), "sub") + other');
2202
- expect(snippets[0].literals).toContain('pre{0:F2}{1}');
2203
- expect(snippets[0].literals).toContain('sub');
2204
- })
2205
- test('should handle .text = assignments', () => {
2206
- const code = 'label.text = "Test";';
2207
- const snippets = extractor.extractStrings(code);
2236
+ // 正确提取C#方法参数中的字符串表达式信息, 支持1到多个形参有默认值的情形
2237
+ test('should handle string expression in method call parameter 4', () => {
2238
+ const code = `
2239
+ namespace FaBao.UI.Comp
2240
+ {
2241
+ public partial class ItemCommonComp
2242
+ {
2243
+ public void BindData(string wkle = "11111", bool needCompare = false, int needCompare2 = false)
2244
+ {
2245
+ }
2208
2246
 
2209
- expect(snippets.length).toBeGreaterThan(0);
2210
- const snippet = snippets[0];
2211
- expect(snippet.originalCode).toBe('"Test"');
2212
- expect(snippet.convertedCode).toBe('"Test".TR()');
2213
- expect(snippet.literals).toEqual(['Test']);
2214
- expect(snippet.isChanged).toBe(true);
2215
- })
2216
- test('should handle .text = with Tr.Format', () => {
2217
- const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
2218
- const snippets = extractor.extractStrings(code);
2247
+ public void BindData(string wkle = "22", string wkle = "werwer", bool needCompare = false, string wkle = "wefwewef31f", int needCompare2 = false, string wkle = "df223")
2248
+ {
2249
+ }
2219
2250
 
2220
- const snippet = snippets[0];
2221
- expect(snippet.originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
2222
- expect(snippet.convertedCode).toBe('Tr.Format("pre", Func(), "sub") + other.TR()');
2223
- expect(snippet.literals).toEqual(['pre', 'sub']);
2224
- expect(snippet.isChanged).toBe(true);
2225
- })
2226
- test('should handle .text = assignments contains chinese', () => {
2227
- const code = `
2228
- 为栓饭cxs12.text = 南方eflkj.d文件 + "完善ew";
2229
- m_btn_为栓饭cxs12.title = bw尅kljekl.完善 + "栏栏nfc";
2251
+ public void BindData(string wkle = "(*&%\\\\*(@Fwfe))&(")
2252
+ {
2253
+ }
2254
+
2255
+ public void BindData(string we, string wkle = "aaaaa", bool needCompare = false, int needCompare2 = false)
2256
+ {
2257
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2258
+ }
2259
+ }
2260
+ }
2230
2261
  `;
2231
2262
  const snippets = extractor.extractStrings(code);
2232
2263
  {
2233
- let snippet = snippets[0];
2234
- expect(snippet.originalCode).toBe('南方eflkj.d文件 + "完善ew"');
2235
- expect(snippet.convertedCode).toBe('南方eflkj.d文件.TR() + "完善ew".TR()');
2236
- expect(snippet.literals).toEqual([
2237
- '完善ew'
2264
+ let targetSnippet = snippets[0];
2265
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2266
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"11111"`));
2267
+ expect(targetSnippet.originalCode).toBe(`"11111"`);
2268
+ expect(targetSnippet.convertedCode).toBe(`"11111"`);
2269
+ expect(targetSnippet.literals).toEqual([
2270
+ '11111'
2271
+ ]);
2272
+ expect(targetSnippet.isChanged).toBe(false);
2273
+ }
2274
+ {
2275
+ let targetSnippet = snippets[1];
2276
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2277
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"22"`));
2278
+ expect(targetSnippet.originalCode).toBe(`"22"`);
2279
+ expect(targetSnippet.convertedCode).toBe(`"22"`);
2280
+ expect(targetSnippet.literals).toEqual([
2281
+ '22'
2282
+ ]);
2283
+ expect(targetSnippet.isChanged).toBe(false);
2284
+ }
2285
+ {
2286
+ let targetSnippet = snippets[2];
2287
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2288
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"werwer"`));
2289
+ expect(targetSnippet.originalCode).toBe(`"werwer"`);
2290
+ expect(targetSnippet.convertedCode).toBe(`"werwer"`);
2291
+ expect(targetSnippet.literals).toEqual([
2292
+ 'werwer'
2293
+ ]);
2294
+ expect(targetSnippet.isChanged).toBe(false);
2295
+ }
2296
+ {
2297
+ let targetSnippet = snippets[3];
2298
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2299
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"wefwewef31f"`));
2300
+ expect(targetSnippet.originalCode).toBe(`"wefwewef31f"`);
2301
+ expect(targetSnippet.convertedCode).toBe(`"wefwewef31f"`);
2302
+ expect(targetSnippet.literals).toEqual([
2303
+ 'wefwewef31f'
2304
+ ]);
2305
+ expect(targetSnippet.isChanged).toBe(false);
2306
+ }
2307
+ {
2308
+ let targetSnippet = snippets[4];
2309
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2310
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"df223"`));
2311
+ expect(targetSnippet.originalCode).toBe(`"df223"`);
2312
+ expect(targetSnippet.convertedCode).toBe(`"df223"`);
2313
+ expect(targetSnippet.literals).toEqual([
2314
+ 'df223'
2315
+ ]);
2316
+ expect(targetSnippet.isChanged).toBe(false);
2317
+ }
2318
+ {
2319
+ let targetSnippet = snippets[5];
2320
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2321
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"(*&%\\\\*(@Fwfe))&("`));
2322
+ expect(targetSnippet.originalCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
2323
+ expect(targetSnippet.convertedCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
2324
+ expect(targetSnippet.literals).toEqual([
2325
+ '(*&%\\\\*(@Fwfe))&('
2238
2326
  ]);
2327
+ expect(targetSnippet.isChanged).toBe(false);
2239
2328
  }
2240
2329
  {
2241
- let snippet = snippets[1];
2242
- expect(snippet.originalCode).toBe('bw尅kljekl.完善 + "栏栏nfc"');
2243
- expect(snippet.convertedCode).toBe('bw尅kljekl.完善.TR() + "栏栏nfc".TR()');
2244
- expect(snippet.literals).toEqual([
2245
- '栏栏nfc'
2330
+ let targetSnippet = snippets[6];
2331
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2332
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"aaaaa"`));
2333
+ expect(targetSnippet.originalCode).toBe(`"aaaaa"`);
2334
+ expect(targetSnippet.convertedCode).toBe(`"aaaaa"`);
2335
+ expect(targetSnippet.literals).toEqual([
2336
+ 'aaaaa'
2246
2337
  ]);
2338
+ expect(targetSnippet.isChanged).toBe(false);
2339
+ }
2340
+ {
2341
+ let targetSnippet = snippets[7];
2342
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2343
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2344
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2345
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2346
+ expect(targetSnippet.literals).toEqual([
2347
+ '{0}阶'
2348
+ ]);
2349
+ expect(targetSnippet.isChanged).toBe(false);
2247
2350
  }
2248
- })
2249
2351
  });
2250
2352
 
2251
- describe('.title赋值语句处理', () => {
2252
- test('should handle m_btn_xxx.title = yyy', () => {
2353
+ // 需要正确识别C#代码中 `if` 语句边界 和 创建对象表达式的边界, 识别 `if` 语句前、中、后部分中的字符串表达式
2354
+ test('should handle if border and object creation expression border', () => {
2253
2355
  const code = `
2254
- buy.title = "lkwjelkfj1";
2255
- m_btn1_buy.title = "lkwjelkfj2";
2256
- am_btn_buy.title = "lkwjelkfj3";
2257
- m_btn_buy.title = "lkwjelkfj4";
2258
- m_btn_wwefHwref.title = $"Hello";
2259
- m_btn_fxx_wf.title = $"Hello, {name}!";
2260
- `;
2261
- const snippets = extractor.extractStrings(code);
2262
- {
2263
- let snippet = snippets[0];
2264
- expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj1"'));
2265
- expect(snippet.originalCode).toBe('"lkwjelkfj1"');
2266
- // `buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
2267
- expect(snippet.convertedCode).toBe('"lkwjelkfj1"');
2268
- expect(snippet.literals).toContain('lkwjelkfj1');
2269
- expect(snippet.isChanged).toBe(false);
2270
- }
2356
+ namespace FaBao.UI.MainUI
2357
+ {
2358
+ public partial class SpellUpgradeDialog
2359
+ {
2360
+ private async void OnBtnUpgrade()
2271
2361
  {
2272
- let snippet = snippets[1];
2273
- expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj2"'));
2274
- expect(snippet.originalCode).toBe('"lkwjelkfj2"');
2275
- // `m_btn1_buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
2276
- expect(snippet.convertedCode).toBe('"lkwjelkfj2"');
2277
- expect(snippet.literals).toContain('lkwjelkfj2');
2278
- expect(snippet.isChanged).toBe(false);
2362
+ // new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id) 被方法调用传参的 () 包裹, 因此已经构成了完整的对象创建表达式
2363
+ if (await this.GetModel<SpellCardModel>().DispatchAction(new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id)))
2364
+ {
2365
+ //升级成功后刷新界面
2366
+ m_text_level.text = Tr.Format("{0}级", _symbolCard.Level);
2367
+ }
2279
2368
  }
2369
+ }
2370
+ }
2371
+ `;
2372
+ const snippets = extractor.extractStrings(code);
2280
2373
  {
2281
- let snippet = snippets[2];
2282
- expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj3"'));
2283
- expect(snippet.originalCode).toBe('"lkwjelkfj3"');
2284
- // `am_btn_buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
2285
- expect(snippet.convertedCode).toBe('"lkwjelkfj3"');
2286
- expect(snippet.literals).toContain('lkwjelkfj3');
2287
- expect(snippet.isChanged).toBe(false);
2374
+ let targetSnippet = snippets[0];
2375
+ // 需要正确识别 `if` 语句边界, 识别 `if` 语句前、中、后部分中的字符串表达式
2376
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}级", _symbolCard.Level)`));
2377
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
2378
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
2379
+ expect(targetSnippet.literals).toEqual([
2380
+ '{0}级'
2381
+ ]);
2382
+ expect(targetSnippet.isChanged).toBe(false);
2288
2383
  }
2384
+ expect(snippets.length).toBe(1);
2385
+ });
2386
+
2387
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2388
+ test('should handle class member initialization assignment with anonymous function 1', () => {
2389
+ const code = `
2390
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2391
+ {
2392
+ Title = "提示",
2393
+ ConfirmCallback = async () =>
2394
+ {
2395
+ //灵纹一键下阵
2396
+ if (await this.GetModel<CardModel>().DispatchAction(new Rune_Action_CleanRune(_sutraCardData.Card.Id)))
2397
+ {
2398
+ Toast.Info("卸载成功");
2399
+ }
2400
+ },
2401
+ CancelText = "取消",
2402
+ }).Forget();
2403
+ `;
2404
+ const snippets = extractor.extractStrings(code);
2289
2405
  {
2290
- let snippet = snippets[3];
2291
- expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj4"'));
2292
- expect(snippet.originalCode).toBe('"lkwjelkfj4"');
2293
- // `m_btn_buy` 符合 `m_btn_` 开头的条件, 需要强制加 `.TR()`
2294
- expect(snippet.convertedCode).toBe('"lkwjelkfj4".TR()');
2295
- expect(snippet.literals).toContain('lkwjelkfj4');
2296
- expect(snippet.isChanged).toBe(true);
2406
+ let targetSnippet = snippets[0];
2407
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"提示"`));
2408
+ expect(targetSnippet.originalCode).toBe(`"提示"`);
2409
+ expect(targetSnippet.convertedCode).toBe(`"提示"`);
2410
+ expect(targetSnippet.literals).toEqual([
2411
+ '提示'
2412
+ ]);
2413
+ expect(targetSnippet.isChanged).toBe(false);
2297
2414
  }
2298
2415
  {
2299
- let snippet = snippets[4];
2300
- expect(snippet.originalIndex).toBe(code.indexOf('$"Hello"'));
2301
- expect(snippet.originalCode).toBe('$"Hello"');
2302
- // `m_btn_wwefHwref` 符合 `m_btn_` 开头的条件, 需要强制加 `.TR()`
2303
- expect(snippet.convertedCode).toBe('$"Hello".TR()');
2304
- expect(snippet.literals).toContain('Hello');
2305
- expect(snippet.isChanged).toBe(true);
2416
+ let targetSnippet = snippets[1];
2417
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2418
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2419
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2420
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2421
+ expect(targetSnippet.literals).toEqual([
2422
+ '卸载成功'
2423
+ ]);
2424
+ expect(targetSnippet.isChanged).toBe(false);
2306
2425
  }
2307
2426
  {
2308
- let snippet = snippets[5];
2309
- expect(snippet.originalIndex).toBe(code.indexOf('$"Hello, {name}!"'));
2310
- expect(snippet.originalCode).toBe('$"Hello, {name}!"');
2311
- // `m_btn_fxx_wf` 符合 `m_btn_` 开头的条件
2312
- expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
2313
- expect(snippet.literals).toContain('Hello, {0}!');
2314
- expect(snippet.isChanged).toBe(true);
2427
+ let targetSnippet = snippets[2];
2428
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"取消"`));
2429
+ expect(targetSnippet.originalCode).toBe(`"取消"`);
2430
+ expect(targetSnippet.convertedCode).toBe(`"取消"`);
2431
+ expect(targetSnippet.literals).toEqual([
2432
+ '取消'
2433
+ ]);
2434
+ expect(targetSnippet.isChanged).toBe(false);
2315
2435
  }
2316
- expect(snippets.length).toBe(6);
2317
- })
2318
- test('should handle m_btn_xxx.title = yyy assignments contains chinese', () => {
2436
+ });
2437
+
2438
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2439
+ test('should handle class member initialization assignment with anonymous function 2', () => {
2319
2440
  const code = `
2320
- switch (_roomData.RoomState)
2321
- {
2322
- case Data.Match.RoomState.IsDismiss:
2323
- m_btn_join.title = "已解散";
2324
- m_btn_join.enabled = false;
2325
- grayed = true;
2326
- break;
2327
- case Data.Match.RoomState.Rejected:
2441
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2328
2442
  {
2329
- m_btn_join.title = $"被拒绝{Kff}";
2330
- m_btn_join.enabled = false;
2331
- break;
2332
- }
2333
- case Data.Match.RoomState.OutOfDate:
2334
- m_dd_join.text = "已到期";
2335
- break;
2336
- default:
2337
- m_btn_join.title = "申请";
2338
- m_btn_joi23n.title = $"申请{Kjf}";
2339
- break;
2340
- }
2443
+ ConfirmCallback = async () =>
2444
+ {
2445
+ Toast.Info("卸载成功");
2446
+ },
2447
+ }).Forget();
2341
2448
  `;
2342
2449
  const snippets = extractor.extractStrings(code);
2343
2450
  {
2344
- let snippet = snippets[0];
2345
- expect(snippet.originalCode).toBe('"已解散"');
2346
- expect(snippet.convertedCode).toBe('"已解散".TR()');
2347
- expect(snippet.literals).toEqual([
2348
- '已解散'
2349
- ]);
2350
- }
2351
- {
2352
- let snippet = snippets[1];
2353
- expect(snippet.originalCode).toBe('$"被拒绝{Kff}"');
2354
- expect(snippet.convertedCode).toBe('Tr.Format("被拒绝{0}", Kff)');
2355
- expect(snippet.literals).toEqual([
2356
- '被拒绝{0}'
2451
+ let targetSnippet = snippets[0];
2452
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2453
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2454
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2455
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2456
+ expect(targetSnippet.literals).toEqual([
2457
+ '卸载成功'
2357
2458
  ]);
2459
+ expect(targetSnippet.isChanged).toBe(false);
2358
2460
  }
2461
+ });
2462
+
2463
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2464
+ test('should handle class member initialization assignment with anonymous function 3', () => {
2465
+ const code = `
2466
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2467
+ {
2468
+ ConfirmCallback = (int qwfewe) =>
2469
+ {
2470
+ Toast.Info($"卸载成功: {qwfewe}");
2471
+ },
2472
+ }).Forget();
2473
+ `;
2474
+ const snippets = extractor.extractStrings(code);
2359
2475
  {
2360
- let snippet = snippets[2];
2361
- expect(snippet.originalCode).toBe('"已到期"');
2362
- expect(snippet.convertedCode).toBe('"已到期".TR()');
2363
- expect(snippet.literals).toEqual([
2364
- '已到期'
2476
+ let targetSnippet = snippets[0];
2477
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2478
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"卸载成功: {qwfewe}"`));
2479
+ expect(targetSnippet.originalCode).toBe(`$"卸载成功: {qwfewe}"`);
2480
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("卸载成功: {0}", qwfewe)`);
2481
+ expect(targetSnippet.literals).toEqual([
2482
+ '卸载成功: {0}'
2365
2483
  ]);
2484
+ expect(targetSnippet.isChanged).toBe(true);
2366
2485
  }
2486
+ });
2487
+
2488
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2489
+ test('should handle class member initialization assignment with anonymous function 4', () => {
2490
+ const code = `
2491
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2492
+ {
2493
+ ConfirmCallback = async () => Toast.Info("卸载成功"),
2494
+ }).Forget();
2495
+ `;
2496
+ const snippets = extractor.extractStrings(code);
2367
2497
  {
2368
- let snippet = snippets[3];
2369
- expect(snippet.originalCode).toBe('"申请"');
2370
- expect(snippet.convertedCode).toBe('"申请".TR()');
2371
- expect(snippet.literals).toEqual([
2372
- '申请'
2498
+ let targetSnippet = snippets[0];
2499
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2500
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2501
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2502
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2503
+ expect(targetSnippet.literals).toEqual([
2504
+ '卸载成功'
2373
2505
  ]);
2506
+ expect(targetSnippet.isChanged).toBe(false);
2374
2507
  }
2508
+ });
2509
+
2510
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2511
+ test('should handle class member initialization assignment with anonymous function 5', () => {
2512
+ const code = `
2513
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2514
+ {
2515
+ ConfirmCallback = () => Toast.Info("卸载成功"),
2516
+ }).Forget();
2517
+ `;
2518
+ const snippets = extractor.extractStrings(code);
2375
2519
  {
2376
- let snippet = snippets[4];
2377
- expect(snippet.originalCode).toBe('$"申请{Kjf}"');
2378
- expect(snippet.convertedCode).toBe('Tr.Format("申请{0}", Kjf)');
2379
- expect(snippet.literals).toEqual([
2380
- '申请{0}'
2520
+ let targetSnippet = snippets[0];
2521
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2522
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2523
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2524
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2525
+ expect(targetSnippet.literals).toEqual([
2526
+ '卸载成功'
2381
2527
  ]);
2528
+ expect(targetSnippet.isChanged).toBe(false);
2382
2529
  }
2383
- })
2384
2530
  });
2385
2531
 
2386
- describe('字符串拼接处理', () => {
2387
- test('should handle string concatenation', () => {
2388
- const code = 'var label2 = "Hello, " + name + "!";';
2532
+ // 测试处理 `xxx.text = yyy;` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2533
+ test('should handle .text = format 1', () => {
2534
+ const code = `m_text_iwff.text = info ;`;
2389
2535
  const snippets = extractor.extractStrings(code);
2536
+ {
2537
+ let targetSnippet = snippets[0];
2538
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`info`));
2539
+ expect(targetSnippet.originalCode).toBe(`info`);
2540
+ expect(targetSnippet.convertedCode).toBe(`info.TR()`);
2541
+ expect(targetSnippet.literals).toEqual([
2542
+ ]);
2543
+ expect(targetSnippet.isChanged).toBe(true);
2544
+ }
2545
+ });
2390
2546
 
2391
- expect(snippets.length).toBe(1);
2392
- expect(snippets[0].originalCode).toBe('"Hello, " + name + "!"');
2393
- expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
2394
- })
2395
- test('should handle string concatenations', () => {
2396
- const code = 'var wkleee = "Hello, " + name + "!";';
2547
+ // 测试处理 `xxx.text = yyy;` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2548
+ test('should handle .text = format 2', () => {
2549
+ const code = `m_text_info.text = info1.member1 + info2.member2;`;
2397
2550
  const snippets = extractor.extractStrings(code);
2398
-
2399
- expect(snippets.length).toBeGreaterThan(0);
2400
- const snippet = snippets[0];
2401
- expect(snippet.originalCode).toBe('"Hello, " + name + "!"');
2402
- expect(snippet.convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
2403
- expect(snippet.literals).toEqual(['Hello, ', '!']);
2404
- expect(snippet.isChanged).toBe(true);
2405
- })
2551
+ {
2552
+ let targetSnippet = snippets[0];
2553
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`info1.member1 + info2.member2`));
2554
+ expect(targetSnippet.originalCode).toBe(`info1.member1 + info2.member2`);
2555
+ expect(targetSnippet.convertedCode).toBe(`info1.member1.TR() + info2.member2.TR()`);
2556
+ expect(targetSnippet.literals).toEqual([
2557
+ ]);
2558
+ expect(targetSnippet.isChanged).toBe(true);
2559
+ }
2406
2560
  });
2407
2561
 
2408
- describe('switch case语句处理', () => {
2409
- test('should handle switch expression string', () => {
2562
+ // 处理字符串出现在赋值表达式的 `=` 左边(左值表达式)的情况
2563
+ test('should handle right value expression', () => {
2410
2564
  const code = `
2411
- case (int)State.Lock:
2412
- Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
2413
- break;
2565
+ ((GLoader)GetChild($"m_loader_dailyItem{i + 1}")).url = FUISys.Instance.GetIconUrl(ItemTable.GetConfigById(_cardConfig.EveryDayAwards[i].ResId).Icon);
2414
2566
  `;
2415
2567
  const snippets = extractor.extractStrings(code);
2416
2568
  {
2417
2569
  let targetSnippet = snippets[0];
2418
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
2419
- expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
2420
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
2570
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_loader_dailyItem{i + 1}"`));
2571
+ expect(targetSnippet.originalCode).toBe(`$"m_loader_dailyItem{i + 1}"`); // 左边表达式函数调用参数中的字符串表达式
2572
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_loader_dailyItem{0}", i + 1)`);
2421
2573
  expect(targetSnippet.literals).toEqual([
2422
- '灵田{0}级解锁'
2574
+ `m_loader_dailyItem{0}`
2423
2575
  ]);
2424
2576
  expect(targetSnippet.isChanged).toBe(true);
2425
2577
  }
2426
- })
2427
- });
2428
-
2429
- describe('转义字符与特殊字符串处理', () => {
2430
- test('should handle escaped quotes in strings', () => {
2431
- const code = 'Debug.Log("aaa\\\"bbb\\\"c\\\"d\\\\\\"cc");';
2432
- const snippets = extractor.extractStrings(code);
2433
-
2434
2578
  expect(snippets.length).toBe(1);
2435
- expect(snippets[0].originalCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
2436
- expect(snippets[0].convertedCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
2437
- // 由于JavaScript字符串转义的复杂性,我们只检查是否提取了字符串
2438
- expect(snippets[0].literals).toEqual(['aaa\\\"bbb\\\"c\\\"d\\\\\\"cc']);
2439
- })
2440
- test('should handle escaped strings', () => {
2441
- const code = 'a.text = "aaa\\"bbb\\"c\\"d\\\\\\"cc";';
2442
- const snippets = extractor.extractStrings(code);
2579
+ });
2443
2580
 
2444
- const snippet = snippets[0];
2445
- expect(snippet.originalCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc"');
2446
- expect(snippet.convertedCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc".TR()');
2447
- expect(snippet.literals).toEqual(['aaa\\"bbb\\"c\\"d\\\\\\"cc']);
2448
- expect(snippet.isChanged).toBe(true);
2449
- })
2450
- test('should handle special characters in string expression of return sentence', () => {
2451
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
2581
+ // 支持处理字符串里出现各种特殊符号的情况
2582
+ test('should handle special characters in string 8', () => {
2583
+ const code = `
2584
+ m_btn_paid.text = $"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}";
2585
+ `;
2452
2586
  const snippets = extractor.extractStrings(code);
2453
2587
  {
2454
- let targetSnippet = snippets[7];
2455
- // 需要统一匹配 `return` 语句中的字符串表达式
2456
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
2457
- expect(targetSnippet.originalCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
2458
- expect(targetSnippet.convertedCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
2588
+ let targetSnippet = snippets[0];
2589
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}"`));
2590
+ expect(targetSnippet.originalCode).toBe(`$"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}"`);
2591
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}", paidConfig.Quantity)`);
2459
2592
  expect(targetSnippet.literals).toEqual([
2460
- '5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
2593
+ `<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}`
2461
2594
  ]);
2462
- expect(targetSnippet.isChanged).toBe(false);
2595
+ expect(targetSnippet.isChanged).toBe(true);
2463
2596
  }
2464
- })
2597
+ expect(snippets.length).toBe(1);
2465
2598
  });
2466
2599
 
2467
- describe('多行语句与注释处理', () => {
2468
- test('should handle doc comment boundary for class/method/members', () => {
2600
+ // 处理赋值表达式 `=` 左右两边(左值表达式和右值表达式)都存在字符串的情况
2601
+ test('should handle assignment expression with both sides strings', () => {
2469
2602
  const code = `
2470
- /// <summary>
2471
- /// "fwef 类注释1"
2472
- /// $"fwef 类注释2"
2473
- /// $"fwef 类注释2 {wef}"
2474
- /// wef $"fwef 类注释2 {wef}" wef
2475
- /// </summary>
2476
- /// <sumary "lkwjfelj"/>
2477
- public partial class TimeLimitedChargeDialog
2478
- {
2479
- /// <summary>
2480
- /// $" Property成员注释 dsvdf {wlpoe}"
2481
- /// </summary>
2482
- private int Ljfw { get; set; } = 342;
2483
-
2484
- /// <summary>
2485
- /// $" Field成员注释 wfewe {vvwe}"
2486
- /// $" Field成员注释 wfewe {vvwe}" wfe
2487
- /// </summary>
2488
- private int Ljfw = 342;
2489
-
2490
- /// <summary>
2491
- ///</summary>
2492
- private int Ljfw = 342;
2493
-
2494
- /// <summary>
2495
- /// " wef方法注释 wef"
2496
- /// $" wef方法注释 wef"
2497
- /// $" wef方法注释 wef{wef}"
2498
- /// wfe $" wef方法注释 wef{wef}"
2499
- /// wfe $" wef方法注释 wef{wef}" wef
2500
- ///wfe $" wef方法注释 wef{wef}" wef
2501
- /// wefw $" wef方法注释 wef{wef}" we
2502
- /// </summary>
2503
- /// <sumary wf $"klwf"/>
2504
- /// <param name="force">是否重新创建页签列表</param>
2505
- private void RefreshGoodsInfo()
2506
- {
2507
- m_text_timeleft.text = curShop.TimeLimitData.RemainTime() + "后消失";
2508
- }
2509
- }
2603
+ ((GTextField)GetChild($"m_text_dailyCount{i + 1}")).text = $"x{_cardConfig.EveryDayAwards[i].Count}";
2510
2604
  `;
2511
2605
  const snippets = extractor.extractStrings(code);
2512
2606
  {
2513
2607
  let targetSnippet = snippets[0];
2514
- // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2515
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime() + "后消失"`));
2516
- expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime() + "后消失"`);
2517
- expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
2608
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_text_dailyCount{i + 1}"`));
2609
+ expect(targetSnippet.originalCode).toBe(`$"m_text_dailyCount{i + 1}"`);
2610
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_text_dailyCount{0}", i + 1)`);
2518
2611
  expect(targetSnippet.literals).toEqual([
2519
- '后消失'
2612
+ `m_text_dailyCount{0}`
2520
2613
  ]);
2521
2614
  expect(targetSnippet.isChanged).toBe(true);
2522
2615
  }
2523
- })
2616
+ {
2617
+ let targetSnippet = snippets[1];
2618
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"x{_cardConfig.EveryDayAwards[i].Count}"`));
2619
+ expect(targetSnippet.originalCode).toBe(`$"x{_cardConfig.EveryDayAwards[i].Count}"`);
2620
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("x{0}", _cardConfig.EveryDayAwards[i].Count)`);
2621
+ expect(targetSnippet.literals).toEqual([
2622
+ `x{0}`
2623
+ ]);
2624
+ expect(targetSnippet.isChanged).toBe(true);
2625
+ }
2626
+ expect(snippets.length).toBe(2);
2524
2627
  });
2525
2628
 
2526
- describe('边界情况与复杂场景处理', () => {
2527
- test('should capture statement boundaries correctly', () => {
2528
- const code = 'obj.text = "Test property" + "Test property2";';
2529
- const snippets = extractor.extractStrings(code);
2530
-
2531
- expect(snippets.length).toBe(1);
2532
- expect(snippets[0].originalCode).toBe('"Test property" + "Test property2"');
2533
- expect(snippets[0].convertedCode).toBe('"Test property".TR() + "Test property2".TR()');
2534
- })
2535
- test('should handle complex text assignments', () => {
2536
- const code = 'label.text = string.Format("pre{0}sub", Func()) + other;';
2537
- const snippets = extractor.extractStrings(code);
2538
-
2539
- const snippet = snippets[0];
2540
- expect(snippet.originalCode).toBe('string.Format("pre{0}sub", Func()) + other');
2541
- expect(snippet.convertedCode).toBe('Tr.Format("pre{0}sub", Func()) + other.TR()');
2542
- expect(snippet.literals).toEqual(['pre{0}sub']);
2543
- expect(snippet.isChanged).toBe(true);
2544
- })
2545
- test('should handle member initial assignment', () => {
2546
- const code = readFileSync("test/TestSpecialString.cs", "utf8");
2629
+ // 支持处理字符串里出现各种特殊符号的情况
2630
+ test('should handle special characters in string 9', () => {
2631
+ const code = `
2632
+ (GetChild($"m_textAnime{GetNextNumber()}") as TurtleTextAnimeComp)?.SetText(word);
2633
+ `;
2547
2634
  const snippets = extractor.extractStrings(code);
2548
2635
  {
2549
2636
  let targetSnippet = snippets[0];
2550
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\\\"`));
2551
- expect(targetSnippet.originalCode).toBe(`"\\\\"`);
2552
- expect(targetSnippet.convertedCode).toBe(`"\\\\"`);
2637
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_textAnime{GetNextNumber()}"`));
2638
+ expect(targetSnippet.originalCode).toBe(`$"m_textAnime{GetNextNumber()}"`);
2639
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_textAnime{0}", GetNextNumber())`);
2553
2640
  expect(targetSnippet.literals).toEqual([
2554
- '\\\\'
2641
+ `m_textAnime{0}`
2555
2642
  ]);
2556
- expect(targetSnippet.isChanged).toBe(false);
2643
+ expect(targetSnippet.isChanged).toBe(true);
2557
2644
  }
2645
+ expect(snippets.length).toBe(1);
2646
+ });
2647
+
2648
+ // 支持处理字符串里出现各种特殊符号的情况
2649
+ test('should handle special characters in string 10', () => {
2650
+ const code = `
2651
+ (GetChild($"m_textAnime{GetNextNumber()}") as TurtleTextAnimeComp)?.SetText(word);
2652
+ `;
2653
+ const snippets = extractor.extractStrings(code);
2558
2654
  {
2559
- let targetSnippet = snippets[1];
2560
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\n"`));
2561
- expect(targetSnippet.originalCode).toBe(`"\\n"`);
2562
- expect(targetSnippet.convertedCode).toBe(`"\\n"`);
2655
+ let targetSnippet = snippets[0];
2656
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_textAnime{GetNextNumber()}"`));
2657
+ expect(targetSnippet.originalCode).toBe(`$"m_textAnime{GetNextNumber()}"`);
2658
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_textAnime{0}", GetNextNumber())`);
2563
2659
  expect(targetSnippet.literals).toEqual([
2564
- '\\n'
2660
+ `m_textAnime{0}`
2565
2661
  ]);
2566
- expect(targetSnippet.isChanged).toBe(false);
2662
+ expect(targetSnippet.isChanged).toBe(true);
2567
2663
  }
2664
+ expect(snippets.length).toBe(1);
2665
+ });
2666
+
2667
+ // 修正字符串表达式捕获范围
2668
+ test('should handle string expression capture range 1', () => {
2669
+ const code = `
2670
+ (GetChild($"vwvwe{index + 1}") as LargeLotterySpellCardComp)?.InitData(spellCardId, true);
2671
+ `;
2672
+ const snippets = extractor.extractStrings(code);
2568
2673
  {
2569
- let targetSnippet = snippets[2];
2570
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"\\t"`));
2571
- expect(targetSnippet.originalCode).toBe(`$"\\t"`);
2572
- expect(targetSnippet.convertedCode).toBe(`$"\\t"`);
2674
+ let targetSnippet = snippets[0];
2675
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"vwvwe{index + 1}"`));
2676
+ expect(targetSnippet.originalCode).toBe(`$"vwvwe{index + 1}"`);
2677
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("vwvwe{0}", index + 1)`);
2573
2678
  expect(targetSnippet.literals).toEqual([
2574
- '\\t'
2679
+ `vwvwe{0}`
2575
2680
  ]);
2576
- expect(targetSnippet.isChanged).toBe(false);
2681
+ expect(targetSnippet.isChanged).toBe(true);
2577
2682
  }
2578
- })
2579
- test('should handle optional value assignment', () => {
2580
- const code = `m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";`;
2683
+ expect(snippets.length).toBe(1);
2684
+ });
2685
+
2686
+ // 修正字符串表达式捕获范围
2687
+ test('should handle string expression capture range 2', () => {
2688
+ const code = `
2689
+ (GetChild($"brrr_{index + 1}") as LargeLotterySpellCardComp)?.InitData(spellCardId, true);
2690
+ `;
2581
2691
  const snippets = extractor.extractStrings(code);
2582
- {
2583
- let targetSnippet = snippets[0];
2584
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
2585
- expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
2586
- expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
2692
+ {
2693
+ let targetSnippet = snippets[0];
2694
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"brrr_{index + 1}"`));
2695
+ expect(targetSnippet.originalCode).toBe(`$"brrr_{index + 1}"`);
2696
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("brrr_{0}", index + 1)`);
2587
2697
  expect(targetSnippet.literals).toEqual([
2588
- ''
2698
+ `brrr_{0}`
2589
2699
  ]);
2590
2700
  expect(targetSnippet.isChanged).toBe(true);
2591
2701
  }
2592
- })
2593
- test('should handle if border and object creation expression border', () => {
2702
+ expect(snippets.length).toBe(1);
2703
+ });
2704
+
2705
+ // 修正字符串表达式捕获范围
2706
+ test('should handle string expression capture range 3', () => {
2594
2707
  const code = `
2595
- namespace FaBao.UI.MainUI
2596
- {
2597
- public partial class SpellUpgradeDialog
2598
- {
2599
- private async void OnBtnUpgrade()
2600
- {
2601
- // new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id) 被方法调用传参的 () 包裹, 因此已经构成了完整的对象创建表达式
2602
- if (await this.GetModel<SpellCardModel>().DispatchAction(new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id)))
2603
- {
2604
- //升级成功后刷新界面
2605
- m_text_level.text = Tr.Format("{0}级", _symbolCard.Level);
2606
- }
2607
- }
2608
- }
2609
- }
2708
+ (GetChild($"m_card_{index}") as LargeLotterySpellCardComp)?.PlayEff();
2610
2709
  `;
2611
2710
  const snippets = extractor.extractStrings(code);
2612
2711
  {
2613
2712
  let targetSnippet = snippets[0];
2614
- // 需要正确识别 `if` 语句边界, 识别 `if` 语句前、中、后部分中的字符串表达式
2615
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}", _symbolCard.Level)`));
2616
- expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}", _symbolCard.Level)`);
2617
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
2713
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_card_{index}"`));
2714
+ expect(targetSnippet.originalCode).toBe(`$"m_card_{index}"`);
2715
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_card_{0}", index)`);
2618
2716
  expect(targetSnippet.literals).toEqual([
2619
- '{0}级'
2717
+ `m_card_{0}`
2620
2718
  ]);
2621
- expect(targetSnippet.isChanged).toBe(false);
2719
+ expect(targetSnippet.isChanged).toBe(true);
2622
2720
  }
2623
2721
  expect(snippets.length).toBe(1);
2624
- })
2625
- test('should handle right value expression', () => {
2626
- const code = `
2627
- ((GLoader)GetChild($"m_loader_dailyItem{i + 1}")).url = FUISys.Instance.GetIconUrl(ItemTable.GetConfigById(_cardConfig.EveryDayAwards[i].ResId).Icon);
2628
- `;
2722
+ });
2723
+
2724
+ // 字符串表达式字面量直接为null时, 无需转换null
2725
+ test('should handle null 1', () => {
2726
+ const code = `m_text_notice.text = null;`;
2629
2727
  const snippets = extractor.extractStrings(code);
2630
2728
  {
2631
2729
  let targetSnippet = snippets[0];
2632
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_loader_dailyItem{i + 1}"`));
2633
- expect(targetSnippet.originalCode).toBe(`$"m_loader_dailyItem{i + 1}"`); // 左边表达式函数调用参数中的字符串表达式
2634
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_loader_dailyItem{0}", i + 1)`);
2730
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`null`));
2731
+ expect(targetSnippet.originalCode).toBe(`null`);
2732
+ expect(targetSnippet.convertedCode).toBe(`null`);
2635
2733
  expect(targetSnippet.literals).toEqual([
2636
- `m_loader_dailyItem{0}`
2637
2734
  ]);
2638
- expect(targetSnippet.isChanged).toBe(true);
2735
+ expect(targetSnippet.isChanged).toBe(false);
2639
2736
  }
2640
2737
  expect(snippets.length).toBe(1);
2641
- })
2642
- test('should handle calculate originalIndex', () => {
2738
+ });
2739
+
2740
+ // 字符串表达式字面量直接为null时, 无需转换null
2741
+ test('should handle null 2', () => {
2742
+ const code = `m_text_notice.wfefe = null;`;
2743
+ const snippets = extractor.extractStrings(code);
2744
+ expect(snippets.length).toBe(0);
2745
+ });
2746
+
2747
+ // 需要正确获取 `originalIndex`, `originalIndex` 的定义为 `originalIndex=code.indexOf(originalCode)`, 其中 `code` 表示原始代码, `originalCode` 表示匹配出的原始代码中的字符串表达式
2748
+ test('should handle calculate originalIndex', () => {
2643
2749
  const code = `
2644
2750
  namespace FaBao
2645
2751
  {
@@ -2669,109 +2775,373 @@ namespace FaBao
2669
2775
  ]);
2670
2776
  expect(targetSnippet.isChanged).toBe(false);
2671
2777
  }
2672
- })
2673
2778
  });
2674
2779
 
2675
- describe('其他测试', () => {
2676
- test('should handle multiple statements', () => {
2677
- const code = 'obj1.text = "Hello"; obj2.text = "World";';
2780
+ // 测试 `xxx.text = yyy;` 和 `m_btn_xxx.title = yyy;` 形式的赋值语句都需要支持中文, 包括字符串表达式中也要支持包含中文的情况
2781
+ test('should handle .text = assignments contains chinese', () => {
2782
+ const code = `
2783
+ 为栓饭cxs12.text = 南方eflkj.d文件 + "完善ew";
2784
+ m_btn_为栓饭cxs12.title = bw尅kljekl.完善 + "栏栏nfc";
2785
+ `;
2678
2786
  const snippets = extractor.extractStrings(code);
2787
+ {
2788
+ let snippet = snippets[0];
2789
+ expect(snippet.originalCode).toBe('南方eflkj.d文件 + "完善ew"');
2790
+ expect(snippet.convertedCode).toBe('南方eflkj.d文件.TR() + "完善ew".TR()');
2791
+ expect(snippet.literals).toEqual([
2792
+ '完善ew'
2793
+ ]);
2794
+ }
2795
+ {
2796
+ let snippet = snippets[1];
2797
+ expect(snippet.originalCode).toBe('bw尅kljekl.完善 + "栏栏nfc"');
2798
+ expect(snippet.convertedCode).toBe('bw尅kljekl.完善.TR() + "栏栏nfc".TR()');
2799
+ expect(snippet.literals).toEqual([
2800
+ '栏栏nfc'
2801
+ ]);
2802
+ }
2803
+ });
2679
2804
 
2680
- expect(snippets.length).toBe(2);
2681
- expect(snippets[0].originalCode).toBe('"Hello"');
2682
- expect(snippets[0].convertedCode).toBe('"Hello".TR()');
2683
- expect(snippets[1].originalCode).toBe('"World"');
2684
- expect(snippets[1].convertedCode).toBe('"World".TR()');
2685
- })
2686
- test('should handle function calls with string arguments', () => {
2687
- const code = 'CallFunc("Hello", "World");';
2805
+ // 测试`m_btn_xxx.title = yyy;` 形式的赋值语句要和`xxx.text = yyy;`形式的赋值语句完全等同处理
2806
+ test('should handle m_btn_xxx.title = yyy assignments contains chinese', () => {
2807
+ const code = `
2808
+ switch (_roomData.RoomState)
2809
+ {
2810
+ case Data.Match.RoomState.IsDismiss:
2811
+ m_btn_join.title = "已解散";
2812
+ m_btn_join.enabled = false;
2813
+ grayed = true;
2814
+ break;
2815
+ case Data.Match.RoomState.Rejected:
2816
+ {
2817
+ m_btn_join.title = $"被拒绝{Kff}";
2818
+ m_btn_join.enabled = false;
2819
+ break;
2820
+ }
2821
+ case Data.Match.RoomState.OutOfDate:
2822
+ m_dd_join.text = "已到期";
2823
+ break;
2824
+ default:
2825
+ m_btn_join.title = "申请";
2826
+ m_btn_joi23n.title = $"申请{Kjf}";
2827
+ break;
2828
+ }
2829
+ `;
2688
2830
  const snippets = extractor.extractStrings(code);
2689
2831
  {
2690
- const snippet = snippets[0];
2691
- expect(snippet.originalCode).toBe('"Hello"');
2692
- expect(snippet.convertedCode).toBe('"Hello"');
2693
- expect(snippet.literals).toEqual(['Hello']);
2694
- expect(snippet.isChanged).toBe(false);
2832
+ let snippet = snippets[0];
2833
+ expect(snippet.originalCode).toBe('"已解散"');
2834
+ expect(snippet.convertedCode).toBe('"已解散".TR()');
2835
+ expect(snippet.literals).toEqual([
2836
+ '已解散'
2837
+ ]);
2838
+ }
2839
+ {
2840
+ let snippet = snippets[1];
2841
+ expect(snippet.originalCode).toBe('$"被拒绝{Kff}"');
2842
+ expect(snippet.convertedCode).toBe('Tr.Format("被拒绝{0}", Kff)');
2843
+ expect(snippet.literals).toEqual([
2844
+ '被拒绝{0}'
2845
+ ]);
2846
+ }
2847
+ {
2848
+ let snippet = snippets[2];
2849
+ expect(snippet.originalCode).toBe('"已到期"');
2850
+ expect(snippet.convertedCode).toBe('"已到期".TR()');
2851
+ expect(snippet.literals).toEqual([
2852
+ '已到期'
2853
+ ]);
2854
+ }
2855
+ {
2856
+ let snippet = snippets[3];
2857
+ expect(snippet.originalCode).toBe('"申请"');
2858
+ expect(snippet.convertedCode).toBe('"申请".TR()');
2859
+ expect(snippet.literals).toEqual([
2860
+ '申请'
2861
+ ]);
2695
2862
  }
2696
2863
  {
2864
+ let snippet = snippets[4];
2865
+ expect(snippet.originalCode).toBe('$"申请{Kjf}"');
2866
+ expect(snippet.convertedCode).toBe('Tr.Format("申请{0}", Kjf)');
2867
+ expect(snippet.literals).toEqual([
2868
+ '申请{0}'
2869
+ ]);
2870
+ }
2871
+ });
2697
2872
 
2698
- const snippet = snippets[1];
2699
- expect(snippet.originalCode).toBe('"World"');
2700
- expect(snippet.convertedCode).toBe('"World"');
2701
- expect(snippet.literals).toEqual(['World']);
2702
- expect(snippet.isChanged).toBe(false);
2873
+ // 测试三元表达式中的字符串表达式处理
2874
+ test('should handle thriple expression with string expression 1', () => {
2875
+ const code = `m_btn_confirm.title = isUsed ? "使用中" : "使用";`;
2876
+ const snippets = extractor.extractStrings(code);
2877
+ {
2878
+ let snippet = snippets[0];
2879
+ expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
2880
+ expect(snippet.convertedCode).toBe('isUsed ? "使用中".TR() : "使用".TR()');
2881
+ expect(snippet.literals).toEqual([
2882
+ '使用中',
2883
+ '使用'
2884
+ ]);
2703
2885
  }
2704
- })
2705
- test('should handle string with `//`', () => {
2706
- const code = `m_btn_paid.text = "//";`
2886
+ });
2887
+
2888
+ // 测试三元表达式中的字符串表达式处理
2889
+ test('should handle thriple expression with string expression 2', () => {
2890
+ const code = `wefe.text = isUsed ? "使用中" : "使用";`;
2707
2891
  const snippets = extractor.extractStrings(code);
2892
+ {
2893
+ let snippet = snippets[0];
2894
+ expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
2895
+ expect(snippet.convertedCode).toBe('isUsed ? "使用中".TR() : "使用".TR()');
2896
+ expect(snippet.literals).toEqual([
2897
+ '使用中',
2898
+ '使用'
2899
+ ]);
2900
+ }
2901
+ });
2708
2902
 
2709
- // 验证提取的片段数量
2710
- expect(snippets.length).toBeGreaterThan(0);
2903
+ // 测试三元表达式中的字符串表达式处理
2904
+ test('should handle thriple expression with string expression 3', () => {
2905
+ const code = `wefetext = isUsed ? "使用中" : "使用";`;
2906
+ const snippets = extractor.extractStrings(code);
2907
+ {
2908
+ let snippet = snippets[0];
2909
+ expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
2910
+ expect(snippet.convertedCode).toBe('isUsed ? "使用中" : "使用"');
2911
+ expect(snippet.literals).toEqual([
2912
+ '使用中',
2913
+ '使用'
2914
+ ]);
2915
+ }
2916
+ });
2711
2917
 
2712
- let targetSnippet = snippets[0];
2713
- expect(targetSnippet.originalCode).toBe(`"//"`);
2714
- expect(targetSnippet.convertedCode).toBe(`"//".TR()`);
2715
- expect(targetSnippet.literals).toEqual(['//']);
2716
- expect(targetSnippet.isChanged).toBe(true);
2918
+ // 测试三元表达式中的字符串表达式处理
2919
+ test('should handle thriple expression with string expression 4', () => {
2920
+ const code = `wefetext(isUsed ? "使用中" : "使用");`;
2921
+ const snippets = extractor.extractStrings(code);
2922
+ {
2923
+ let snippet = snippets[0];
2924
+ expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
2925
+ expect(snippet.convertedCode).toBe('isUsed ? "使用中" : "使用"');
2926
+ expect(snippet.literals).toEqual([
2927
+ '使用中',
2928
+ '使用'
2929
+ ]);
2930
+ }
2931
+ });
2717
2932
 
2718
- })
2719
- test('should handle strings in function call arguments', () => {
2933
+ // 测试if语句有花括号包围的单句表达式语句中的字符串表达式处理
2934
+ test('should handle assignment expression within if{}else{}', () => {
2720
2935
  const code = `
2721
- private void OnBtnIaa(EventContext context)
2722
- {
2723
- var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
2724
- if (iaaConfig == null)
2936
+ if (_eventIdx < _table.MainDescription.Length)
2725
2937
  {
2726
- return;
2938
+ m_text_desc.text = _table.MainDescription[_eventIdx];
2939
+ }
2940
+ else
2941
+ {
2942
+ m_text_desc.text = _table.MainDescription[0];
2727
2943
  }
2728
-
2729
- string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
2730
- this.GetModel<IAAModel>().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
2731
- }
2732
2944
  `;
2733
2945
  const snippets = extractor.extractStrings(code);
2946
+ {
2947
+ let targetSnippet = snippets[0];
2948
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
2949
+ expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
2950
+ expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
2951
+ expect(targetSnippet.literals).toEqual([
2952
+ ]);
2953
+ expect(targetSnippet.isChanged).toBe(true);
2954
+ }
2734
2955
  {
2735
2956
  let targetSnippet = snippets[1];
2736
- // 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
2737
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`"加速时间"`));
2738
- expect(targetSnippet.originalCode).toBe(`"加速时间"`);
2739
- expect(targetSnippet.convertedCode).toBe(`"加速时间"`);
2957
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
2958
+ expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
2959
+ expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
2740
2960
  expect(targetSnippet.literals).toEqual([
2741
- '加速时间'
2742
2961
  ]);
2743
- expect(targetSnippet.isChanged).toBe(false);
2962
+ expect(targetSnippet.isChanged).toBe(true);
2744
2963
  }
2964
+ expect(snippets.length).toBe(2);
2745
2965
  })
2746
- test('should handle assignment expression with both sides strings', () => {
2966
+
2967
+ // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
2968
+ test('should handle assignment expression within if-else-end', () => {
2747
2969
  const code = `
2748
- ((GTextField)GetChild($"m_text_dailyCount{i + 1}")).text = $"x{_cardConfig.EveryDayAwards[i].Count}";
2970
+ if (_eventIdx < _table.MainDescription.Length)
2971
+ m_text_desc.text = _table.MainDescription[_eventIdx];
2972
+ else
2973
+ m_text_desc.text = _table.MainDescription[0];
2749
2974
  `;
2750
2975
  const snippets = extractor.extractStrings(code);
2751
2976
  {
2752
2977
  let targetSnippet = snippets[0];
2753
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_text_dailyCount{i + 1}"`));
2754
- expect(targetSnippet.originalCode).toBe(`$"m_text_dailyCount{i + 1}"`);
2755
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_text_dailyCount{0}", i + 1)`);
2978
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
2979
+ expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
2980
+ expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
2756
2981
  expect(targetSnippet.literals).toEqual([
2757
- `m_text_dailyCount{0}`
2758
2982
  ]);
2759
2983
  expect(targetSnippet.isChanged).toBe(true);
2760
2984
  }
2761
2985
  {
2762
2986
  let targetSnippet = snippets[1];
2763
- expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"x{_cardConfig.EveryDayAwards[i].Count}"`));
2764
- expect(targetSnippet.originalCode).toBe(`$"x{_cardConfig.EveryDayAwards[i].Count}"`);
2765
- expect(targetSnippet.convertedCode).toBe(`Tr.Format("x{0}", _cardConfig.EveryDayAwards[i].Count)`);
2987
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
2988
+ expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
2989
+ expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
2766
2990
  expect(targetSnippet.literals).toEqual([
2767
- `x{0}`
2768
2991
  ]);
2769
2992
  expect(targetSnippet.isChanged).toBe(true);
2770
2993
  }
2771
2994
  expect(snippets.length).toBe(2);
2772
2995
  })
2773
- });
2774
2996
 
2775
- ;
2997
+ // // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
2998
+ // test('should handle assignment expression within if-else-end', () => {
2999
+ // const code = `
3000
+ // private void RefreshRedDot()
3001
+ // {
3002
+ // m_redDot.visible = _data.NeedShowRedDot();
3003
+ // }
3004
+
3005
+ // private void OnSelfClick()
3006
+ // {
3007
+ // this.GetSystem<AudioSys>().PlayAudioByName(AudioName.SoundUI_Button);
3008
+
3009
+ // if (!_data.IsOpen)
3010
+ // {
3011
+ // string tip = _data.Config.LockTip;
3012
+ // if (_data.Config.TaskIds != null && _data.Config.TaskIds.Count > 0)
3013
+ // {
3014
+ // int taskId = App.Instance.GetModel<MissionModel>().CurGuideMissionConfig.Id;
3015
+ // int remainingTaskCount = _data.Config.TaskIds[0] - taskId + 1;
3016
+ // remainingTaskCount = Mathf.Max(0, remainingTaskCount);
3017
+ // tip = string.Format(_data.Config.LockTip, remainingTaskCount);
3018
+ // }
3019
+
3020
+ // Toast.Info(tip);
3021
+ // return;
3022
+ // }
3023
+
3024
+
3025
+ // `;
3026
+
3027
+ // const snippets = extractor.extractStrings(code);
3028
+ // {
3029
+ // let targetSnippet = snippets[0];
3030
+ // expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
3031
+ // expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
3032
+ // expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
3033
+ // expect(targetSnippet.literals).toEqual([
3034
+ // ]);
3035
+ // expect(targetSnippet.isChanged).toBe(true);
3036
+ // }
3037
+ // {
3038
+ // let targetSnippet = snippets[1];
3039
+ // expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
3040
+ // expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
3041
+ // expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
3042
+ // expect(targetSnippet.literals).toEqual([
3043
+ // ]);
3044
+ // expect(targetSnippet.isChanged).toBe(true);
3045
+ // }
3046
+ // expect(snippets.length).toBe(2);
3047
+ // })
3048
+
3049
+ // // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
3050
+ // test('should handle assignment expression within if-else-end', () => {
3051
+ // const code = `
3052
+ // protected override void OnCreate()
3053
+ // {
3054
+ // base.OnCreate();
3055
+ // }
3056
+
3057
+ // protected override void OnShow(object param)
3058
+ // {
3059
+ // base.OnShow(param);
3060
+
3061
+ // var mailContent = (MailContentDialogParam)param;
3062
+ // if (mailContent == null) return;
3063
+
3064
+ // var mailData = this.GetModel<MailModel>().GetMail(mailContent.MailId);
3065
+ // if (mailData == null) return;
3066
+
3067
+ // _mailId = mailData.MailId;
3068
+ // m_text_title.text = mailData.Title.TR();
3069
+ // m_text_name.text = $"道友如晤:".TR();
3070
+ // m_text_sender.text = Tr.Format("{0}稽首", mailData.Name);
3071
+ // //转换时区
3072
+ // // var dateTimeOffset = TimeZoneInfo.ConvertTime(DateTimeOffset.FromUnixTimeSeconds(mailData.ReceiveTime), TimeZoneInfo.Local);
3073
+ // m_text_time.text = Date.ConvertTimestampToTimeString(mailData.ReceiveTime, "yyyy年M月d日 HH:mm").TR();
3074
+ // m_scroll_content.title = mailData.Content;
3075
+
3076
+ // `;
3077
+
3078
+ // const snippets = extractor.extractStrings(code);
3079
+ // {
3080
+ // let targetSnippet = snippets[0];
3081
+ // expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
3082
+ // expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
3083
+ // expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
3084
+ // expect(targetSnippet.literals).toEqual([
3085
+ // ]);
3086
+ // expect(targetSnippet.isChanged).toBe(true);
3087
+ // }
3088
+ // {
3089
+ // let targetSnippet = snippets[1];
3090
+ // expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
3091
+ // expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
3092
+ // expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
3093
+ // expect(targetSnippet.literals).toEqual([
3094
+ // ]);
3095
+ // expect(targetSnippet.isChanged).toBe(true);
3096
+ // }
3097
+ // expect(snippets.length).toBe(2);
3098
+ // })
3099
+
3100
+ // // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
3101
+ // test('should handle assignment expression within if-else-end', () => {
3102
+ // const code = `
3103
+ // public void BindData(MailData mailData)
3104
+ // {
3105
+ // if (mailData == null) return;
3106
+ // _mailData = mailData;
3107
+ // //状态
3108
+ // m_hasRead.selectedIndex = _mailData.HasRead ? (int)HasRead.True : (int)HasRead.False;
3109
+ // m_hasFetch.selectedIndex = mailData.Rewards.Count > 0 ? (int)HasFetch.True : (int)HasFetch.False;
3110
+
3111
+ // m_text_title.text = _mailData.Title.TR();
3112
+ // m_text_subTitle.text = _mailData.SubTitle.TR();
3113
+ // //显示时间
3114
+ // long interval = DateTimeOffset.Now.ToUnixTimeSeconds() - mailData.ReceiveTime;
3115
+ // if (interval <= 0) interval = 1;
3116
+ // if (interval < 60) m_text_time.text = "现在";
3117
+ // else if (interval < 3600) m_text_time.text = $"{interval / 60}分钟前";
3118
+ // else if (interval < 86400) m_text_time.text = $"{interval / 3600}小时前";
3119
+ // else if (interval < 2592000) m_text_time.text = $"{interval / 86400}天前";
3120
+ // else if (interval < 31104000) m_text_time.text = $"{interval / 2592000}月前";
3121
+ // else m_text_time.text = Tr.Format("{0}年前", interval / 31104000);
3122
+ // }
3123
+ // `;
3124
+
3125
+ // const snippets = extractor.extractStrings(code);
3126
+ // {
3127
+ // let targetSnippet = snippets[0];
3128
+ // expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
3129
+ // expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
3130
+ // expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
3131
+ // expect(targetSnippet.literals).toEqual([
3132
+ // ]);
3133
+ // expect(targetSnippet.isChanged).toBe(true);
3134
+ // }
3135
+ // {
3136
+ // let targetSnippet = snippets[1];
3137
+ // expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
3138
+ // expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
3139
+ // expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
3140
+ // expect(targetSnippet.literals).toEqual([
3141
+ // ]);
3142
+ // expect(targetSnippet.isChanged).toBe(true);
3143
+ // }
3144
+ // expect(snippets.length).toBe(2);
3145
+ // })
2776
3146
 
2777
3147
  });