scancscode 1.0.31 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.trae/specs/fix-doc-comment-boundary/checklist.md +7 -0
  2. package/.trae/specs/fix-doc-comment-boundary/spec.md +52 -0
  3. package/.trae/specs/fix-doc-comment-boundary/tasks.md +34 -0
  4. package/.trae/specs/fix-interpolated-string-nested-literals/checklist.md +7 -0
  5. package/.trae/specs/fix-interpolated-string-nested-literals/spec.md +55 -0
  6. package/.trae/specs/fix-interpolated-string-nested-literals/tasks.md +25 -0
  7. package/.trae/specs/fix-remaining-interpolated-string-index/checklist.md +9 -0
  8. package/.trae/specs/fix-remaining-interpolated-string-index/spec.md +59 -0
  9. package/.trae/specs/fix-remaining-interpolated-string-index/tasks.md +41 -0
  10. package/.trae/specs/fix-return-interpolated-string/checklist.md +8 -0
  11. package/.trae/specs/fix-return-interpolated-string/spec.md +60 -0
  12. package/.trae/specs/fix-return-interpolated-string/tasks.md +39 -0
  13. package/.trae/specs/handle-anonymous-function-strings/checklist.md +11 -0
  14. package/.trae/specs/handle-anonymous-function-strings/spec.md +137 -0
  15. package/.trae/specs/handle-anonymous-function-strings/tasks.md +65 -0
  16. package/.trae/specs/handle-interpolated-string-double-braces/checklist.md +9 -0
  17. package/.trae/specs/handle-interpolated-string-double-braces/spec.md +61 -0
  18. package/.trae/specs/handle-interpolated-string-double-braces/tasks.md +41 -0
  19. package/.trae/specs/handle-return-statement/checklist.md +11 -0
  20. package/.trae/specs/handle-return-statement/spec.md +76 -0
  21. package/.trae/specs/handle-return-statement/tasks.md +44 -0
  22. package/.trae/specs/handle-special-string-characters/checklist.md +13 -0
  23. package/.trae/specs/handle-special-string-characters/spec.md +94 -0
  24. package/.trae/specs/handle-special-string-characters/tasks.md +74 -0
  25. package/.trae/specs/unify-return-statement-string-extraction/checklist.md +10 -0
  26. package/.trae/specs/unify-return-statement-string-extraction/spec.md +70 -0
  27. package/.trae/specs/unify-return-statement-string-extraction/tasks.md +54 -0
  28. package/bin/scanliterals.js +3 -3
  29. package/bin/slimlangs.js +3 -3
  30. package/dist/debug-arg.js +30 -0
  31. package/dist/debug-args.js +34 -0
  32. package/dist/debug-comment-5.js +25 -0
  33. package/dist/debug-comment-strings.js +24 -0
  34. package/dist/debug-full.js +14 -0
  35. package/dist/debug-template-issue.js +33 -0
  36. package/dist/debug-test-5.js +23 -0
  37. package/dist/debug-test.js +21 -0
  38. package/dist/debug.js +15 -0
  39. package/dist/simple-debug.js +27 -0
  40. package/dist/simple-test.js +61 -0
  41. package/dist/src/CSharpStringExtractor.js +1791 -358
  42. package/dist/src/CmdExecutor.js +6 -8
  43. package/dist/temp-original-source.js +1 -0
  44. package/dist/test/CSharpStringExtractor.test.js +1587 -207
  45. package/dist/test-logic.js +79 -0
  46. package/dist/test-regex.js +13 -0
  47. package/docs/CSharpStringExtractor/344/273/243/347/240/201/347/224/237/346/210/220/346/217/220/347/244/272/350/257/215.txt +73 -0
  48. package/jest.config.js +9 -9
  49. package/package.json +1 -1
  50. package/src/CSCodeScanner.ts +305 -305
  51. package/src/CSVUtils.ts +181 -181
  52. package/src/CSharpStringExtractor.ts +2058 -479
  53. package/src/CmdExecutor.ts +107 -106
  54. package/src/LiteralCollector.ts +143 -143
  55. package/src/RunConvert.ts +3 -3
  56. package/src/RunSlimLangs.ts +3 -3
  57. package/src/TableScanner.ts +92 -92
  58. package/test/CSharpStringExtractor.test.ts +1673 -208
  59. package/test/KeeperDialog.cs +114 -0
  60. package/test/TestSpecialString.cs +24 -0
  61. package/tsconfig.json +109 -109
@@ -8,6 +8,7 @@ describe('CSharpStringExtractor', () => {
8
8
  extractor = new CSharpStringExtractor();
9
9
  });
10
10
 
11
+ // 测试从普通函数调用参数中提取字符串表达式信息, 只需要提取参数中的字符串值表达式, 不需要对提取的字符串表达式进行处理
11
12
  test('should extract plain strings', () => {
12
13
  const code = 'Console.WriteLine("Hello, world!");';
13
14
  const snippets = extractor.extractStrings(code);
@@ -16,431 +17,502 @@ describe('CSharpStringExtractor', () => {
16
17
  expect(snippets[0].literals).toEqual(['Hello, world!']);
17
18
  });
18
19
 
20
+ // 已经追加 `.TR()` 后缀的字符串表达式不需要重复追加 `.TR()`
19
21
  test('should handle strings with .TR() already appended', () => {
20
22
  const code = 'obj.prop = "Hello, world!".TR();';
21
23
  const snippets = extractor.extractStrings(code);
22
24
 
23
25
  expect(snippets.length).toBe(1);
24
- expect(snippets[0].originalCode).toBe('obj.prop = "Hello, world!".TR();');
25
- expect(snippets[0].convertedCode).toBe('obj.prop = "Hello, world!".TR();');
26
+ expect(snippets[0].originalCode).toBe('"Hello, world!".TR()');
27
+ expect(snippets[0].convertedCode).toBe('"Hello, world!".TR()');
26
28
  expect(snippets[0].isChanged).toBe(false);
27
29
  });
28
30
 
31
+ // 测试从普通函数调用参数中提取字符串表达式信息, 只需要提取参数中的字符串值表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
29
32
  test('should not add .TR() to function arguments', () => {
30
33
  const code = 'CallFunc("Hello, world!");';
31
34
  const snippets = extractor.extractStrings(code);
32
35
 
33
36
  expect(snippets.length).toBe(1);
34
- expect(snippets[0].convertedCode).toBe('CallFunc("Hello, world!");');
37
+ expect(snippets[0].originalCode).toBe('"Hello, world!"');
38
+ expect(snippets[0].convertedCode).toBe('"Hello, world!"');
35
39
  });
36
40
 
41
+ // 测试从普通属性赋值语句中提取字符串表达式信息, 只需要提取赋值表达式中的字符串值表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
37
42
  test('should not add .TR() to regular property assignments', () => {
38
43
  const code = 'obj.prop = "Hello, world!";';
39
44
  const snippets = extractor.extractStrings(code);
40
45
 
41
46
  expect(snippets.length).toBe(1);
42
- expect(snippets[0].convertedCode).toBe('obj.prop = "Hello, world!";');
47
+ expect(snippets[0].originalCode).toBe('"Hello, world!"');
48
+ expect(snippets[0].convertedCode).toBe('"Hello, world!"');
43
49
  });
44
50
 
51
+ // 测试从 $"" 字符串模板(也叫内插字符串)中提取字符串表达式信息, 需要先将字符串模板转换为 `string.Format(...)` 格式, 然后将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
45
52
  test('should handle $"" string templates', () => {
46
- const code = '$"Hello, {name}!";';
53
+ const code = 'var label1 = $"Hello, {name}!";';
47
54
  const snippets = extractor.extractStrings(code);
48
55
 
49
56
  expect(snippets.length).toBe(1);
57
+ expect(snippets[0].originalCode).toBe('$"Hello, {name}!"');
50
58
  expect(snippets[0].literals).toContain('Hello, {0}!');
51
- expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, {0}!", name);');
59
+ expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
52
60
  });
53
61
 
62
+ // 测试从普通函数调用参数中提取 $"" 字符串模板(也叫内插字符串), 需要先将字符串模板转换为 `string.Format(...)` 格式, 然后将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
54
63
  test('should handle $"" string templates in function calls', () => {
55
64
  const code = 'CallFunc($"Hello, world: {obj.Func1()}");';
56
65
  const snippets = extractor.extractStrings(code);
57
66
 
58
67
  expect(snippets.length).toBe(1);
59
- expect(snippets[0].convertedCode).toBe('CallFunc(Tr.Format("Hello, world: {0}", obj.Func1()));');
68
+ expect(snippets[0].originalCode).toBe('$"Hello, world: {obj.Func1()}"');
69
+ expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, world: {0}", obj.Func1())');
60
70
  });
61
71
 
72
+ // 测试遇到 `string.Format(...)` 形式的字符串表达式, 需要先将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息, 内插字符串概念参考: `https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated`
62
73
  test('should convert string.Format to Tr.Format', () => {
63
74
  const code = 'string.Format("Hello, ", name, "!");';
64
75
  const snippets = extractor.extractStrings(code);
65
76
 
66
77
  expect(snippets.length).toBe(1);
67
- expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, ", name, "!");');
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, "!")');
68
82
  });
69
83
 
84
+ // 测试遇到 `string.Format(...)` 形式的字符串表达式, 需要先将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
85
+ // 同时测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式
70
86
  test('should handle string.Format in .text assignments', () => {
71
87
  const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
72
88
  const snippets = extractor.extractStrings(code);
73
89
 
74
90
  expect(snippets.length).toBe(1);
91
+ expect(snippets[0].originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
75
92
  expect(snippets[0].literals).toContain('pre');
76
93
  expect(snippets[0].literals).toContain('sub');
77
94
  });
78
95
 
96
+ // 测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式; 并且给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式
79
97
  test('should handle string concatenation', () => {
80
- const code = '"Hello, " + name + "!";';
98
+ const code = 'var label2 = "Hello, " + name + "!";';
81
99
  const snippets = extractor.extractStrings(code);
82
100
 
83
101
  expect(snippets.length).toBe(1);
84
- expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR();');
102
+ expect(snippets[0].originalCode).toBe('"Hello, " + name + "!"');
103
+ expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
85
104
  });
86
105
 
106
+ // 测试 `xxx.text = yyy` 形式的赋值语句, 确保将 `yyy` 中的字符串表达式信息提取出来, 并追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#字面量, 可以是对象、字符串、或其他表达式
87
107
  test('should handle .text = assignments with function calls', () => {
88
108
  const code = 'label.text = Func();';
89
109
  const snippets = extractor.extractStrings(code);
90
110
 
91
111
  expect(snippets.length).toBe(1);
92
- expect(snippets[0].convertedCode).toBe('label.text = Func().TR();');
112
+ expect(snippets[0].originalCode).toBe('Func()');
113
+ expect(snippets[0].convertedCode).toBe('Func().TR()');
93
114
  });
94
115
 
116
+ // 测试 `xxx.text = xxx + xxx` 形式的赋值语句, 确保将 `xxx.text` 中的 `xxx` 字符串表达式提取出来, 并追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#表达式, 可以是对象、字符串、或其他表达式
95
117
  test('should handle .text = assignments with multiple function calls', () => {
96
118
  const code = 'label.text = Func() + Func();';
97
119
  const snippets = extractor.extractStrings(code);
98
120
 
99
121
  expect(snippets.length).toBe(1);
100
- expect(snippets[0].convertedCode).toBe('label.text = Func().TR() + Func().TR();');
122
+ expect(snippets[0].originalCode).toBe('Func() + Func()');
123
+ expect(snippets[0].convertedCode).toBe('Func().TR() + Func().TR()');
101
124
  });
102
125
 
126
+ // 测试 `xxx.text = yyy` 形式的赋值语句, 确保将 `yyy` 中的 `xxx` 内插字符串提取出来, 并转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#表达式, 可以是对象、字符串、或其他表达式
103
127
  test('should handle .text = assignments with string templates', () => {
104
128
  const code = 'label.text = $"pre{Func()}sub";';
105
129
  const snippets = extractor.extractStrings(code);
106
130
 
107
131
  expect(snippets.length).toBe(1);
132
+ expect(snippets[0].originalCode).toBe('$"pre{Func()}sub"');
108
133
  expect(snippets[0].literals).toContain('pre{0}sub');
109
- expect(snippets[0].convertedCode).toBe('label.text = Tr.Format("pre{0}sub", Func());');
134
+ expect(snippets[0].convertedCode).toBe('Tr.Format("pre{0}sub", Func())');
110
135
  });
111
136
 
137
+ // 测试从内插字符串中提取字符串表达式信息时, 需要注意转换和捕获内插字符串格式参数, 内插字符串和对应格式参数参考: `https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated`
112
138
  test('should handle .text = assignments with string.Format', () => {
113
139
  const code = 'label.text = string.Format("pre{0:F2}{1}", Func(), "sub") + other;';
114
140
  const snippets = extractor.extractStrings(code);
115
141
 
116
142
  expect(snippets.length).toBe(1);
143
+ expect(snippets[0].originalCode).toBe('string.Format("pre{0:F2}{1}", Func(), "sub") + other');
117
144
  expect(snippets[0].literals).toContain('pre{0:F2}{1}');
118
145
  expect(snippets[0].literals).toContain('sub');
119
146
  });
120
147
 
148
+ // 测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式; 并且给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
121
149
  test('should handle complex concatenation with existing .TR() calls', () => {
122
- const code = '"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!";';
150
+ const code = 'var hello3 = "Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!";';
123
151
  const snippets = extractor.extractStrings(code);
124
152
 
125
153
  expect(snippets.length).toBe(1);
126
- expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop.TR() + "!".TR();');
154
+ expect(snippets[0].originalCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!"');
155
+ expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop.TR() + "!".TR()');
127
156
  });
128
157
 
158
+ // 测试处理包含转义引号的字符串表达式, 确保能正确提取和转换转义引号, 而不会影响字符串边界的正常解析
129
159
  test('should handle escaped quotes in strings', () => {
130
- const code = 'Debug.Log("aaa\\\"bbb\\\"c\\\"d\\\\\"cc");';
160
+ const code = 'Debug.Log("aaa\\\"bbb\\\"c\\\"d\\\\\\"cc");';
131
161
  const snippets = extractor.extractStrings(code);
132
162
 
133
163
  expect(snippets.length).toBe(1);
164
+ expect(snippets[0].originalCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
165
+ expect(snippets[0].convertedCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
134
166
  // 由于JavaScript字符串转义的复杂性,我们只检查是否提取了字符串
135
- expect(snippets[0].literals.length).toBeGreaterThan(0);
167
+ expect(snippets[0].literals).toEqual(['aaa\\\"bbb\\\"c\\\"d\\\\\\"cc']);
136
168
  });
137
169
 
170
+ // 测试处理包含 `+` 连接的字符串表达式, 需要给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
138
171
  test('should capture statement boundaries correctly', () => {
139
172
  const code = 'obj.text = "Test property" + "Test property2";';
140
173
  const snippets = extractor.extractStrings(code);
141
174
 
142
175
  expect(snippets.length).toBe(1);
143
- expect(snippets[0].originalCode).toBe('obj.text = "Test property" + "Test property2";');
176
+ expect(snippets[0].originalCode).toBe('"Test property" + "Test property2"');
177
+ expect(snippets[0].convertedCode).toBe('"Test property".TR() + "Test property2".TR()');
144
178
  });
145
179
 
180
+ // 测试处理包含多个语句的C#代码, 确保能从各个语句中正确提取和转换每个语句中的字符串表达式, 而不会影响其他语句的正常解析
146
181
  test('should handle multiple statements', () => {
147
182
  const code = 'obj1.text = "Hello"; obj2.text = "World";';
148
183
  const snippets = extractor.extractStrings(code);
149
184
 
150
185
  expect(snippets.length).toBe(2);
151
- expect(snippets[0].originalCode).toBe('obj1.text = "Hello";');
152
- expect(snippets[1].originalCode).toBe('obj2.text = "World";');
186
+ expect(snippets[0].originalCode).toBe('"Hello"');
187
+ expect(snippets[0].convertedCode).toBe('"Hello".TR()');
188
+ expect(snippets[1].originalCode).toBe('"World"');
189
+ expect(snippets[1].convertedCode).toBe('"World".TR()');
153
190
  });
154
191
 
192
+ // 测试处理作为函数参数的内插字符串表达式, 确保能从内插字符串中提取字符串表达式信息, 并转换为 `Tr.Format(...)` 形式
155
193
  test('should extract and convert string templates', () => {
156
194
  const code = 'Ljk.Ilk($"Hello, {name}!");';
157
195
  const snippets = extractor.extractStrings(code);
158
196
 
159
197
  expect(snippets.length).toBeGreaterThan(0);
160
198
  const snippet = snippets[0];
161
- expect(snippet.originalCode).toBe('Ljk.Ilk($"Hello, {name}!");');
162
- expect(snippet.convertedCode).toBe('Ljk.Ilk(Tr.Format("Hello, {0}!", name));');
199
+ expect(snippet.originalCode).toBe('$"Hello, {name}!"');
200
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
163
201
  expect(snippet.literals).toEqual(['Hello, {0}!']);
164
202
  expect(snippet.isChanged).toBe(true);
165
203
  });
166
204
 
205
+ // 测试处理 `string.Format` 调用, 确保能将其转换为 `Tr.Format(...)` 形式, 并正确提取字符串表达式信息
167
206
  test('should convert string.Format to Tr.Format', () => {
168
207
  const code = 'string.Format("Hello, {0}!", name);';
169
208
  const snippets = extractor.extractStrings(code);
170
209
 
171
210
  expect(snippets.length).toBeGreaterThan(0);
172
211
  const snippet = snippets[0];
173
- expect(snippet.originalCode).toBe('string.Format("Hello, {0}!", name);');
174
- expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name);');
212
+ expect(snippet.originalCode).toBe('string.Format("Hello, {0}!", name)');
213
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
175
214
  expect(snippet.literals).toEqual(['Hello, {0}!']);
176
215
  expect(snippet.isChanged).toBe(true);
177
216
  });
178
217
 
218
+ // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式, 需要从 `yyy` 中正确提取字符串表达式信息, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
179
219
  test('should handle .text = assignments', () => {
180
220
  const code = 'label.text = "Test";';
181
221
  const snippets = extractor.extractStrings(code);
182
222
 
183
223
  expect(snippets.length).toBeGreaterThan(0);
184
224
  const snippet = snippets[0];
185
- expect(snippet.originalCode).toBe('label.text = "Test";');
186
- expect(snippet.convertedCode).toBe('label.text = "Test".TR();');
225
+ expect(snippet.originalCode).toBe('"Test"');
226
+ expect(snippet.convertedCode).toBe('"Test".TR()');
187
227
  expect(snippet.literals).toEqual(['Test']);
188
228
  expect(snippet.isChanged).toBe(true);
189
229
  });
190
230
 
231
+ // 测试处理包含 `+` 连接的字符串表达式, 需要给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
191
232
  test('should handle string concatenations', () => {
192
- const code = '"Hello, " + name + "!";';
233
+ const code = 'var wkleee = "Hello, " + name + "!";';
193
234
  const snippets = extractor.extractStrings(code);
194
235
 
195
236
  expect(snippets.length).toBeGreaterThan(0);
196
237
  const snippet = snippets[0];
197
- expect(snippet.originalCode).toBe('"Hello, " + name + "!";');
198
- expect(snippet.convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR();');
238
+ expect(snippet.originalCode).toBe('"Hello, " + name + "!"');
239
+ expect(snippet.convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
199
240
  expect(snippet.literals).toEqual(['Hello, ', '!']);
200
241
  expect(snippet.isChanged).toBe(true);
201
242
  });
202
243
 
244
+ // 测试处理 `+` 连接的字符串表达式时, 确保不会给已经包含 `.TR()` 的字符串表达式再次追加 `.TR()`
203
245
  test('should not add .TR() to strings already having it', () => {
204
- const code = '"Hello".TR();';
246
+ const code = 'var wkle = "Hello".TR();';
205
247
  const snippets = extractor.extractStrings(code);
206
248
 
207
249
  const snippet = snippets[0];
208
- expect(snippet.originalCode).toBe('"Hello".TR();');
209
- expect(snippet.convertedCode).toBe('"Hello".TR();');
250
+ expect(snippet.originalCode).toBe('"Hello".TR()');
251
+ expect(snippet.convertedCode).toBe('"Hello".TR()');
210
252
  expect(snippet.literals).toEqual(['Hello']);
211
253
  expect(snippet.isChanged).toBe(false);
212
254
  });
213
255
 
256
+ // 测试处理复杂的 `.text =` 赋值语句, 确保能正确处理包含 `string.Format` 调用和 `+` 连接的字符串表达式, 并转换为 `Tr.Format(...)` 形式或进一步递归处理
214
257
  test('should handle complex text assignments', () => {
215
258
  const code = 'label.text = string.Format("pre{0}sub", Func()) + other;';
216
259
  const snippets = extractor.extractStrings(code);
217
260
 
218
261
  const snippet = snippets[0];
219
- expect(snippet.originalCode).toBe('label.text = string.Format("pre{0}sub", Func()) + other;');
220
- expect(snippet.convertedCode).toBe('label.text = Tr.Format("pre{0}sub", Func()) + other.TR();');
262
+ expect(snippet.originalCode).toBe('string.Format("pre{0}sub", Func()) + other');
263
+ expect(snippet.convertedCode).toBe('Tr.Format("pre{0}sub", Func()) + other.TR()');
221
264
  expect(snippet.literals).toEqual(['pre{0}sub']);
222
265
  expect(snippet.isChanged).toBe(true);
223
266
  });
224
267
 
268
+ // 测试处理转义字符串, 确保能正确处理包含转义字符的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
225
269
  test('should handle escaped strings', () => {
226
270
  const code = 'a.text = "aaa\\"bbb\\"c\\"d\\\\\\"cc";';
227
271
  const snippets = extractor.extractStrings(code);
228
272
 
229
273
  const snippet = snippets[0];
230
- expect(snippet.originalCode).toBe('a.text = "aaa\\"bbb\\"c\\"d\\\\\\"cc";');
231
- expect(snippet.convertedCode).toBe('a.text = "aaa\\"bbb\\"c\\"d\\\\\\"cc".TR();');
274
+ expect(snippet.originalCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc"');
275
+ expect(snippet.convertedCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc".TR()');
232
276
  expect(snippet.literals).toEqual(['aaa\\"bbb\\"c\\"d\\\\\\"cc']);
233
277
  expect(snippet.isChanged).toBe(true);
234
278
  });
235
279
 
280
+ // 测试处理 `@$""` 和 `$@""` 格式的字符串表达式, 确保能将其转换为 `Tr.Format(...)` 形式, 并正确提取字符串表达式信息
236
281
  test('should handle @$"" and $@"" formats', () => {
237
282
  const code = 'Fcx.Kjl(@$"Hello, {name}!");';
238
283
  const snippets = extractor.extractStrings(code);
239
284
 
240
285
  const snippet = snippets[0];
241
- expect(snippet.originalCode).toBe('Fcx.Kjl(@$"Hello, {name}!");');
242
- expect(snippet.convertedCode).toBe('Fcx.Kjl(Tr.Format("Hello, {0}!", name));');
286
+ expect(snippet.originalCode).toBe('@$"Hello, {name}!"');
287
+ expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
243
288
  expect(snippet.literals).toEqual(['Hello, {0}!']);
244
289
  expect(snippet.isChanged).toBe(true);
245
290
  });
246
291
 
292
+ // 测试处理函数调用时, 确保能正确提取参数中的字符串表达式, 不需要给字符串表达式追加 `.TR()` 或 转换为 `Tr.Format(...)` 形式, 除非遇到由 `+` 连接的字符串表达式
247
293
  test('should handle function calls with string arguments', () => {
248
294
  const code = 'CallFunc("Hello", "World");';
249
295
  const snippets = extractor.extractStrings(code);
296
+ {
297
+ const snippet = snippets[0];
298
+ expect(snippet.originalCode).toBe('"Hello"');
299
+ expect(snippet.convertedCode).toBe('"Hello"');
300
+ expect(snippet.literals).toEqual(['Hello']);
301
+ expect(snippet.isChanged).toBe(false);
302
+ }
303
+ {
250
304
 
251
- const snippet = snippets[0];
252
- expect(snippet.originalCode).toBe('CallFunc("Hello", "World");');
253
- expect(snippet.convertedCode).toBe('CallFunc("Hello", "World");');
254
- expect(snippet.literals).toEqual(['Hello', 'World']);
255
- expect(snippet.isChanged).toBe(false);
305
+ const snippet = snippets[1];
306
+ expect(snippet.originalCode).toBe('"World"');
307
+ expect(snippet.convertedCode).toBe('"World"');
308
+ expect(snippet.literals).toEqual(['World']);
309
+ expect(snippet.isChanged).toBe(false);
310
+ }
256
311
  });
257
312
 
313
+ // 测试处理 `.text =` 赋值语句时, 确保能正确处理包含 `Tr.Format(...)` 调用的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
258
314
  test('should handle .text = with Tr.Format', () => {
259
315
  const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
260
316
  const snippets = extractor.extractStrings(code);
261
317
 
262
318
  const snippet = snippets[0];
263
- expect(snippet.originalCode).toBe('label.text = Tr.Format("pre", Func(), "sub") + other;');
264
- expect(snippet.convertedCode).toBe('label.text = Tr.Format("pre", Func(), "sub") + other.TR();');
319
+ expect(snippet.originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
320
+ expect(snippet.convertedCode).toBe('Tr.Format("pre", Func(), "sub") + other.TR()');
265
321
  expect(snippet.literals).toEqual(['pre', 'sub']);
266
322
  expect(snippet.isChanged).toBe(true);
267
323
  });
268
324
 
325
+ // 测试处理 `.text =` 赋值语句时, 确保能正确处理包含 `Tr.Format(...)` 调用的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式, 除非遇到由 `+` 连接的字符串表达式
269
326
  test('should handle .text = with Tr.Format 2', () => {
270
327
  const code = 'm_text_lastAward.text = $"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]";';
271
328
  const snippets = extractor.extractStrings(code);
272
329
 
273
330
  const snippet = snippets[0];
274
- expect(snippet.originalCode).toBe('m_text_lastAward.text = $"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]";');
275
- expect(snippet.convertedCode).toBe('m_text_lastAward.text = $"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]";');
331
+ expect(snippet.originalCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]"');
332
+ expect(snippet.convertedCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]"');
276
333
  expect(snippet.literals).toEqual(['[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]']);
277
334
  expect(snippet.isChanged).toBe(false);
278
335
  });
279
336
 
337
+ // 处理C#多行语句
280
338
  test('should handle non string assignment 1', () => {
281
339
  const code = 'var d1 = 12;var d2 = 13;var d3 = d1 + d2;var aa = "aaa";';
282
340
  const snippets = extractor.extractStrings(code);
283
341
  {
284
342
  const snippet = snippets[0];
285
- expect(snippet.originalCode).toBe('var aa = "aaa";');
286
- expect(snippet.convertedCode).toBe('var aa = "aaa";');
343
+ expect(snippet.originalCode).toBe('"aaa"');
344
+ expect(snippet.convertedCode).toBe('"aaa"');
287
345
  expect(snippet.literals).toEqual(['aaa']);
288
346
  expect(snippet.isChanged).toBe(false);
289
347
  }
290
348
  });
291
349
 
350
+ // 处理C#多行语句
292
351
  test('should handle non string assignment 2', () => {
293
352
  const code = 'var d3 = d1 + d2;var aa = "aaa";';
294
353
  const snippets = extractor.extractStrings(code);
295
354
  {
296
355
  const snippet = snippets[0];
297
- expect(snippet.originalCode).toBe('var aa = "aaa";');
298
- expect(snippet.convertedCode).toBe('var aa = "aaa";');
356
+ expect(snippet.originalCode).toBe('"aaa"');
357
+ expect(snippet.convertedCode).toBe('"aaa"');
299
358
  expect(snippet.literals).toEqual(['aaa']);
300
359
  expect(snippet.isChanged).toBe(false);
301
360
  }
302
361
  });
303
362
 
363
+ // 处理C#多行语句
304
364
  test('should handle multilne content1', () => {
305
365
  const code = 'var aa = "aaa";\nvar bb = "bbb";\nvar cc = "ccc";';
306
366
  const snippets = extractor.extractStrings(code);
307
367
  {
308
368
  const snippet = snippets[0];
309
- expect(snippet.originalCode).toBe('var aa = "aaa";');
310
- expect(snippet.convertedCode).toBe('var aa = "aaa";');
369
+ expect(snippet.originalCode).toBe('"aaa"');
370
+ expect(snippet.convertedCode).toBe('"aaa"');
311
371
  expect(snippet.literals).toEqual(['aaa']);
312
372
  expect(snippet.isChanged).toBe(false);
313
373
  }
314
374
  {
315
375
  const snippet = snippets[1];
316
- expect(snippet.originalCode).toBe('var bb = "bbb";');
317
- expect(snippet.convertedCode).toBe('var bb = "bbb";');
376
+ expect(snippet.originalCode).toBe('"bbb"');
377
+ expect(snippet.convertedCode).toBe('"bbb"');
318
378
  expect(snippet.literals).toEqual(['bbb']);
319
379
  expect(snippet.isChanged).toBe(false);
320
380
  }
321
381
  {
322
382
  const snippet = snippets[2];
323
- expect(snippet.originalCode).toBe('var cc = "ccc";');
324
- expect(snippet.convertedCode).toBe('var cc = "ccc";');
383
+ expect(snippet.originalCode).toBe('"ccc"');
384
+ expect(snippet.convertedCode).toBe('"ccc"');
325
385
  expect(snippet.literals).toEqual(['ccc']);
326
386
  expect(snippet.isChanged).toBe(false);
327
387
  }
328
388
  });
329
389
 
390
+ // 处理C#多行语句
330
391
  test('should handle multilne content2', () => {
331
392
  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;';
332
393
  const snippets = extractor.extractStrings(code);
333
394
  {
334
395
  const snippet = snippets[0];
335
- expect(snippet.originalCode).toBe('var aa = "aaa";');
336
- expect(snippet.convertedCode).toBe('var aa = "aaa";');
396
+ expect(snippet.originalCode).toBe('"aaa"');
397
+ expect(snippet.convertedCode).toBe('"aaa"');
337
398
  expect(snippet.literals).toEqual(['aaa']);
338
399
  expect(snippet.isChanged).toBe(false);
339
400
  }
340
401
  {
341
402
  const snippet = snippets[1];
342
- expect(snippet.originalCode).toBe('var bb = "bbb";');
343
- expect(snippet.convertedCode).toBe('var bb = "bbb";');
403
+ expect(snippet.originalCode).toBe('"bbb"');
404
+ expect(snippet.convertedCode).toBe('"bbb"');
344
405
  expect(snippet.literals).toEqual(['bbb']);
345
406
  expect(snippet.isChanged).toBe(false);
346
407
  }
347
408
  {
348
409
  const snippet = snippets[2];
349
- expect(snippet.originalCode).toBe('var hh = aa + bb + "hhh";');
350
- expect(snippet.convertedCode).toBe('var hh = aa.TR() + bb.TR() + "hhh".TR();');
410
+ expect(snippet.originalCode).toBe('aa + bb + "hhh"');
411
+ expect(snippet.convertedCode).toBe('aa.TR() + bb.TR() + "hhh".TR()');
351
412
  expect(snippet.literals).toEqual(['hhh']);
352
413
  expect(snippet.isChanged).toBe(true);
353
414
  }
354
415
  {
355
416
  const snippet = snippets[3];
356
- expect(snippet.originalCode).toBe('cc.text = "ccc" + aa + bb;');
357
- expect(snippet.convertedCode).toBe('cc.text = "ccc".TR() + aa.TR() + bb.TR();');
417
+ expect(snippet.originalCode).toBe('"ccc" + aa + bb');
418
+ expect(snippet.convertedCode).toBe('"ccc".TR() + aa.TR() + bb.TR()');
358
419
  expect(snippet.literals).toEqual(['ccc']);
359
420
  expect(snippet.isChanged).toBe(true);
360
421
  }
361
422
  {
362
423
  const snippet = snippets[4];
363
- expect(snippet.originalCode).toBe('var ii = "iii";');
364
- expect(snippet.convertedCode).toBe('var ii = "iii";');
424
+ expect(snippet.originalCode).toBe('"iii"');
425
+ expect(snippet.convertedCode).toBe('"iii"');
365
426
  expect(snippet.literals).toEqual(['iii']);
366
427
  expect(snippet.isChanged).toBe(false);
367
428
  }
368
429
  {
369
430
  const snippet = snippets[5];
370
- expect(snippet.originalCode).toBe('jj.text = "jjj";');
371
- expect(snippet.convertedCode).toBe('jj.text = "jjj".TR();');
431
+ expect(snippet.originalCode).toBe('"jjj"');
432
+ expect(snippet.convertedCode).toBe('"jjj".TR()');
372
433
  expect(snippet.literals).toEqual(['jjj']);
373
434
  expect(snippet.isChanged).toBe(true);
374
435
  }
375
436
  {
376
437
  const snippet = snippets[6];
377
- expect(snippet.originalCode).toBe('CallFunc("jjj");');
378
- expect(snippet.convertedCode).toBe('CallFunc("jjj");');
438
+ expect(snippet.originalCode).toBe('"jjj"');
439
+ expect(snippet.convertedCode).toBe('"jjj"');
379
440
  expect(snippet.literals).toEqual(['jjj']);
380
441
  expect(snippet.isChanged).toBe(false);
381
442
  }
382
443
  {
383
444
  const snippet = snippets[7];
384
- expect(snippet.originalCode).toBe('CallFunc2("jjj", "kkk");');
385
- expect(snippet.convertedCode).toBe('CallFunc2("jjj", "kkk");');
386
- expect(snippet.literals).toEqual(['jjj', 'kkk']);
445
+ expect(snippet.originalCode).toBe('"jjj"');
446
+ expect(snippet.convertedCode).toBe('"jjj"');
447
+ expect(snippet.literals).toEqual(['jjj']);
387
448
  expect(snippet.isChanged).toBe(false);
388
449
  }
389
450
  {
390
451
  const snippet = snippets[8];
391
- expect(snippet.originalCode).toBe('jj.text = "jjj" + ll;');
392
- expect(snippet.convertedCode).toBe('jj.text = "jjj".TR() + ll.TR();');
452
+ expect(snippet.originalCode).toBe('"kkk"');
453
+ expect(snippet.convertedCode).toBe('"kkk"');
454
+ expect(snippet.literals).toEqual(['kkk']);
455
+ expect(snippet.isChanged).toBe(false);
456
+ }
457
+ {
458
+ const snippet = snippets[9];
459
+ expect(snippet.originalCode).toBe('"jjj" + ll');
460
+ expect(snippet.convertedCode).toBe('"jjj".TR() + ll.TR()');
393
461
  expect(snippet.literals).toEqual(['jjj']);
394
462
  expect(snippet.isChanged).toBe(true);
395
463
  }
396
464
  });
397
465
 
466
+ // 处理C#多行语句
398
467
  test('should handle multilne content3', () => {
399
468
  const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;\nlabel2.text = Tr.Format("pre2", Func(), "sub2") + other2;';
400
469
  const snippets = extractor.extractStrings(code);
401
470
  {
402
471
  const snippet = snippets[0];
403
- expect(snippet.originalCode).toBe('label.text = Tr.Format("pre", Func(), "sub") + other;');
404
- expect(snippet.convertedCode).toBe('label.text = Tr.Format("pre", Func(), "sub") + other.TR();');
472
+ expect(snippet.originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
473
+ expect(snippet.convertedCode).toBe('Tr.Format("pre", Func(), "sub") + other.TR()');
405
474
  expect(snippet.literals).toEqual(['pre', 'sub']);
406
475
  expect(snippet.isChanged).toBe(true);
407
476
  }
408
477
  {
409
478
  const snippet = snippets[1];
410
- expect(snippet.originalCode).toBe('label2.text = Tr.Format("pre2", Func(), "sub2") + other2;');
411
- expect(snippet.convertedCode).toBe('label2.text = Tr.Format("pre2", Func(), "sub2") + other2.TR();');
479
+ expect(snippet.originalCode).toBe('Tr.Format("pre2", Func(), "sub2") + other2');
480
+ expect(snippet.convertedCode).toBe('Tr.Format("pre2", Func(), "sub2") + other2.TR()');
412
481
  expect(snippet.literals).toEqual(['pre2', 'sub2']);
413
482
  expect(snippet.isChanged).toBe(true);
414
483
  }
415
484
  });
416
485
 
486
+ // 处理C#多行语句
417
487
  test('should handle multilne content4', () => {
418
488
  const code = 'label.text =\n Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other;';
419
489
  const snippets = extractor.extractStrings(code);
420
490
  {
421
491
  const snippet = snippets[0];
422
- expect(snippet.originalCode).toBe('label.text =\n Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other;');
423
- expect(snippet.convertedCode).toBe('label.text =\n Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other.TR();');
492
+ expect(snippet.originalCode).toBe('Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other');
493
+ expect(snippet.convertedCode).toBe('Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other.TR()');
424
494
  expect(snippet.literals).toEqual(['pre', 'sub']);
425
495
  expect(snippet.isChanged).toBe(true);
426
496
  }
427
497
  });
428
498
 
499
+ // 处理C#多行语句
429
500
  test('should handle multilne content5', () => {
430
501
  const code = `
431
- //注释不需要包含在捕获部分里
502
+ // 注释中无字符串表达式时, 不需要包含在捕获部分里
432
503
  m_text_name.text = cardTable.Name;
433
504
  `;
434
505
  const snippets = extractor.extractStrings(code);
435
506
  {
436
507
  const snippet = snippets[0];
437
- expect(snippet.originalCode).toBe('m_text_name.text = cardTable.Name;');
438
- expect(snippet.convertedCode).toBe('m_text_name.text = cardTable.Name.TR();');
508
+ expect(snippet.originalCode).toBe('cardTable.Name');
509
+ expect(snippet.convertedCode).toBe('cardTable.Name.TR()');
439
510
  expect(snippet.literals).toEqual([]);
440
511
  expect(snippet.isChanged).toBe(true);
441
512
  }
442
513
  });
443
514
 
515
+ // 处理C#多行语句
444
516
  test('should handle multilne content6', () => {
445
517
  const code = `
446
518
  if (condition)
@@ -451,14 +523,15 @@ describe('CSharpStringExtractor', () => {
451
523
  const snippets = extractor.extractStrings(code);
452
524
  {
453
525
  const snippet = snippets[0];
454
- expect(snippet.originalCode).toBe('Toast.Info("卸载成功");');
455
- expect(snippet.convertedCode).toBe('Toast.Info("卸载成功");');
526
+ expect(snippet.originalCode).toBe('"卸载成功"');
527
+ expect(snippet.convertedCode).toBe('"卸载成功"');
456
528
  expect(snippet.literals).toEqual(['卸载成功']);
457
- expect(snippet.originalIndex).toBe(code.indexOf('Toast.Info("卸载成功");'));
529
+ expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
458
530
  expect(snippet.isChanged).toBe(false);
459
531
  }
460
532
  });
461
533
 
534
+ // 处理C#多行语句
462
535
  test('should handle multilne content7', () => {
463
536
  const code = `
464
537
  while (condition)
@@ -469,14 +542,15 @@ describe('CSharpStringExtractor', () => {
469
542
  const snippets = extractor.extractStrings(code);
470
543
  {
471
544
  const snippet = snippets[0];
472
- expect(snippet.originalCode).toBe('TT1.Fn1("卸载成功");');
473
- expect(snippet.convertedCode).toBe('TT1.Fn1("卸载成功");');
545
+ expect(snippet.originalCode).toBe('"卸载成功"');
546
+ expect(snippet.convertedCode).toBe('"卸载成功"');
474
547
  expect(snippet.literals).toEqual(['卸载成功']);
475
- expect(snippet.originalIndex).toBe(code.indexOf('TT1.Fn1("卸载成功");'));
548
+ expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
476
549
  expect(snippet.isChanged).toBe(false);
477
550
  }
478
551
  });
479
552
 
553
+ // 处理C#多行语句
480
554
  test('should handle multilne content8', () => {
481
555
  const code = `
482
556
  for (scope;condition;continuex)
@@ -487,13 +561,14 @@ describe('CSharpStringExtractor', () => {
487
561
  const snippets = extractor.extractStrings(code);
488
562
  {
489
563
  const snippet = snippets[0];
490
- expect(snippet.originalCode).toBe('QR2.Fn2("卸载成功");');
491
- expect(snippet.convertedCode).toBe('QR2.Fn2("卸载成功");');
564
+ expect(snippet.originalCode).toBe('"卸载成功"');
565
+ expect(snippet.convertedCode).toBe('"卸载成功"');
492
566
  expect(snippet.literals).toEqual(['卸载成功']);
493
567
  expect(snippet.isChanged).toBe(false);
494
568
  }
495
569
  });
496
570
 
571
+ // 处理C#多行语句
497
572
  test('should handle multilne content9', () => {
498
573
  const code = `
499
574
  {
@@ -503,13 +578,14 @@ describe('CSharpStringExtractor', () => {
503
578
  const snippets = extractor.extractStrings(code);
504
579
  {
505
580
  const snippet = snippets[0];
506
- expect(snippet.originalCode).toBe('KK3.Ca3("卸载成功");');
507
- expect(snippet.convertedCode).toBe('KK3.Ca3("卸载成功");');
581
+ expect(snippet.originalCode).toBe('"卸载成功"');
582
+ expect(snippet.convertedCode).toBe('"卸载成功"');
508
583
  expect(snippet.literals).toEqual(['卸载成功']);
509
584
  expect(snippet.isChanged).toBe(false);
510
585
  }
511
586
  });
512
587
 
588
+ // 处理C#多行语句
513
589
  test('should handle multilne content10', () => {
514
590
  const code = `
515
591
  var lambda1 = () => {
@@ -519,13 +595,14 @@ describe('CSharpStringExtractor', () => {
519
595
  const snippets = extractor.extractStrings(code);
520
596
  {
521
597
  const snippet = snippets[0];
522
- expect(snippet.originalCode).toBe('LJN4.Fn4("卸载成功");');
523
- expect(snippet.convertedCode).toBe('LJN4.Fn4("卸载成功");');
598
+ expect(snippet.originalCode).toBe('"卸载成功"');
599
+ expect(snippet.convertedCode).toBe('"卸载成功"');
524
600
  expect(snippet.literals).toEqual(['卸载成功']);
525
601
  expect(snippet.isChanged).toBe(false);
526
602
  }
527
603
  });
528
604
 
605
+ // 处理C#多行语句
529
606
  test('should handle multilne content11', () => {
530
607
  const code = `
531
608
  void lambda1() {
@@ -535,13 +612,14 @@ describe('CSharpStringExtractor', () => {
535
612
  const snippets = extractor.extractStrings(code);
536
613
  {
537
614
  const snippet = snippets[0];
538
- expect(snippet.originalCode).toBe('KK5.Call5("卸载成功");');
539
- expect(snippet.convertedCode).toBe('KK5.Call5("卸载成功");');
615
+ expect(snippet.originalCode).toBe('"卸载成功"');
616
+ expect(snippet.convertedCode).toBe('"卸载成功"');
540
617
  expect(snippet.literals).toEqual(['卸载成功']);
541
618
  expect(snippet.isChanged).toBe(false);
542
619
  }
543
620
  });
544
621
 
622
+ // 处理C#多行语句
545
623
  test('should handle multilne content12', () => {
546
624
  const code = `
547
625
  //星级
@@ -562,12 +640,14 @@ describe('CSharpStringExtractor', () => {
562
640
  const snippets = extractor.extractStrings(code);
563
641
  {
564
642
  const snippet = snippets[0];
565
- expect(snippet.originalCode).toBe('m_text_quality.text = Tr.Format("{0}阶", _sutraCardData.Quality);');
566
- expect(snippet.convertedCode).toBe('m_text_quality.text = Tr.Format("{0}阶", _sutraCardData.Quality);');
643
+ expect(snippet.originalCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
644
+ expect(snippet.convertedCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
567
645
  expect(snippet.literals).toEqual(['{0}阶']);
568
646
  expect(snippet.isChanged).toBe(false);
569
647
  }
570
648
  });
649
+
650
+ // 处理C#多行语句
571
651
  test('should handle multilne content13', () => {
572
652
  const code = `
573
653
  Toast.Info("卸载成功");
@@ -576,22 +656,23 @@ describe('CSharpStringExtractor', () => {
576
656
  const snippets = extractor.extractStrings(code);
577
657
  {
578
658
  const snippet = snippets[0];
579
- expect(snippet.originalIndex).toBe(code.indexOf('Toast.Info("卸载成功");'));
580
- expect(snippet.originalCode).toBe('Toast.Info("卸载成功");');
581
- expect(snippet.convertedCode).toBe('Toast.Info("卸载成功");');
659
+ expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
660
+ expect(snippet.originalCode).toBe('"卸载成功"');
661
+ expect(snippet.convertedCode).toBe('"卸载成功"');
582
662
  expect(snippet.literals).toEqual(['卸载成功']);
583
663
  expect(snippet.isChanged).toBe(false);
584
664
  }
585
665
  {
586
666
  const snippet = snippets[1];
587
- expect(snippet.originalIndex).toBe(code.indexOf('Toast.Info("卸载成功");', snippets[0].originalIndex + 1));
588
- expect(snippet.originalCode).toBe('Toast.Info("卸载成功");');
589
- expect(snippet.convertedCode).toBe('Toast.Info("卸载成功");');
667
+ expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"', snippets[0].originalIndex + 1));
668
+ expect(snippet.originalCode).toBe('"卸载成功"');
669
+ expect(snippet.convertedCode).toBe('"卸载成功"');
590
670
  expect(snippet.literals).toEqual(['卸载成功']);
591
671
  expect(snippet.isChanged).toBe(false);
592
672
  }
593
673
  });
594
674
 
675
+ // 处理C#多行语句
595
676
  test('should handle multilne content14', () => {
596
677
  const code = readFileSync('./test/MainSutraDetailDialog.cs', 'utf8');
597
678
  const snippets = extractor.extractStrings(code);
@@ -602,129 +683,178 @@ describe('CSharpStringExtractor', () => {
602
683
  // 针对每个snippet展开对每个属性值的断言
603
684
 
604
685
  // Snippet 1
605
- expect(snippets[0].originalIndex).toBe(2807);
606
- expect(snippets[0].originalCode).toBe('m_text_name.text = cardTable.Name.TR();');
607
- expect(snippets[0].convertedCode).toBe('m_text_name.text = cardTable.Name.TR();');
686
+ expect(snippets[0].originalIndex).toBe(code.indexOf('cardTable.Name.TR()'));
687
+ expect(snippets[0].originalCode).toBe('cardTable.Name.TR()');
688
+ expect(snippets[0].convertedCode).toBe('cardTable.Name.TR()');
608
689
  expect(snippets[0].literals).toEqual([]);
609
690
  expect(snippets[0].isChanged).toBe(false);
610
691
 
611
692
  // Snippet 2
612
- expect(snippets[1].originalIndex).toBe(3550);
613
- expect(snippets[1].originalCode).toBe('m_text_potrem1.text = sutraConfig.Poetry[0].TR();');
614
- expect(snippets[1].convertedCode).toBe('m_text_potrem1.text = sutraConfig.Poetry[0].TR();');
693
+ expect(snippets[1].originalIndex).toBe(code.indexOf('sutraConfig.Poetry[0].TR()'));
694
+ expect(snippets[1].originalCode).toBe('sutraConfig.Poetry[0].TR()');
695
+ expect(snippets[1].convertedCode).toBe('sutraConfig.Poetry[0].TR()');
615
696
  expect(snippets[1].literals).toEqual([]);
616
697
  expect(snippets[1].isChanged).toBe(false);
617
698
 
618
699
  // Snippet 3
619
- expect(snippets[2].originalIndex).toBe(3612);
620
- expect(snippets[2].originalCode).toBe('m_text_potrem2.text = sutraConfig.Poetry[1].TR();');
621
- expect(snippets[2].convertedCode).toBe('m_text_potrem2.text = sutraConfig.Poetry[1].TR();');
700
+ expect(snippets[2].originalIndex).toBe(code.indexOf('sutraConfig.Poetry[1].TR()'));
701
+ expect(snippets[2].originalCode).toBe('sutraConfig.Poetry[1].TR()');
702
+ expect(snippets[2].convertedCode).toBe('sutraConfig.Poetry[1].TR()');
622
703
  expect(snippets[2].literals).toEqual([]);
623
704
  expect(snippets[2].isChanged).toBe(false);
624
705
 
625
706
  // Snippet 4
626
- expect(snippets[3].originalIndex).toBe(3912);
627
- expect(snippets[3].originalCode).toBe('m_text_atkAdd.text = (_sutraCardData.AttackUp() / 100).ToString("0.0") + "%".TR();');
628
- expect(snippets[3].convertedCode).toBe('m_text_atkAdd.text = (_sutraCardData.AttackUp() / 100).ToString("0.0").TR() + "%".TR();');
707
+ expect(snippets[3].originalIndex).toBe(code.indexOf('(_sutraCardData.AttackUp() / 100).ToString("0.0") + "%".TR()'));
708
+ expect(snippets[3].originalCode).toBe('(_sutraCardData.AttackUp() / 100).ToString("0.0") + "%".TR()');
709
+ expect(snippets[3].convertedCode).toBe('(_sutraCardData.AttackUp() / 100).ToString("0.0").TR() + "%".TR()');
629
710
  expect(snippets[3].literals).toEqual(['0.0', '%']);
630
711
  expect(snippets[3].isChanged).toBe(true);
631
712
 
632
713
  // Snippet 5
633
- expect(snippets[4].originalIndex).toBe(4007);
634
- expect(snippets[4].originalCode).toBe('m_text_hpAdd.text = (_sutraCardData.HpUp() / 100).ToString("0.0") + "%".TR();');
635
- expect(snippets[4].convertedCode).toBe('m_text_hpAdd.text = (_sutraCardData.HpUp() / 100).ToString("0.0").TR() + "%".TR();');
714
+ expect(snippets[4].originalIndex).toBe(code.indexOf('(_sutraCardData.HpUp() / 100).ToString("0.0") + "%".TR()'));
715
+ expect(snippets[4].originalCode).toBe('(_sutraCardData.HpUp() / 100).ToString("0.0") + "%".TR()');
716
+ expect(snippets[4].convertedCode).toBe('(_sutraCardData.HpUp() / 100).ToString("0.0").TR() + "%".TR()');
636
717
  expect(snippets[4].literals).toEqual(['0.0', '%']);
637
718
  expect(snippets[4].isChanged).toBe(true);
638
719
 
639
720
  // Snippet 6
640
- expect(snippets[5].originalIndex).toBe(4187);
641
- expect(snippets[5].originalCode).toBe('m_text_skillName.text = skillConfig.Name.TR();');
642
- expect(snippets[5].convertedCode).toBe('m_text_skillName.text = skillConfig.Name.TR();');
721
+ expect(snippets[5].originalIndex).toBe(code.indexOf('skillConfig.Name.TR()'));
722
+ expect(snippets[5].originalCode).toBe('skillConfig.Name.TR()');
723
+ expect(snippets[5].convertedCode).toBe('skillConfig.Name.TR()');
643
724
  expect(snippets[5].literals).toEqual([]);
644
725
  expect(snippets[5].isChanged).toBe(false);
645
726
 
646
727
  // Snippet 7
647
- expect(snippets[6].originalIndex).toBe(4324);
648
- expect(snippets[6].originalCode).toBe('m_text_skill.text = passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR();');
649
- expect(snippets[6].convertedCode).toBe('m_text_skill.text = passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR();');
728
+ expect(snippets[6].originalIndex).toBe(code.indexOf('passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR()'));
729
+ expect(snippets[6].originalCode).toBe('passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR()');
730
+ expect(snippets[6].convertedCode).toBe('passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR()');
650
731
  expect(snippets[6].literals).toEqual(['被动技能未解锁']);
651
732
  expect(snippets[6].isChanged).toBe(false);
652
733
 
653
734
  // Snippet 8
654
- expect(snippets[7].originalIndex).toBe(4505);
655
- expect(snippets[7].originalCode).toBe('m_effect_skillUnlock.ShowEffect("Effect_FaBao_Unlock");');
656
- expect(snippets[7].convertedCode).toBe('m_effect_skillUnlock.ShowEffect("Effect_FaBao_Unlock");');
735
+ expect(snippets[7].originalIndex).toBe(code.indexOf('"Effect_FaBao_Unlock"'));
736
+ expect(snippets[7].originalCode).toBe('"Effect_FaBao_Unlock"');
737
+ expect(snippets[7].convertedCode).toBe('"Effect_FaBao_Unlock"');
657
738
  expect(snippets[7].literals).toEqual(['Effect_FaBao_Unlock']);
658
739
  expect(snippets[7].isChanged).toBe(false);
659
740
 
660
741
  // Snippet 9
661
- expect(snippets[8].originalIndex).toBe(4952);
662
- expect(snippets[8].originalCode).toBe('m_text_quality.text = Tr.Format("{0}阶", _sutraCardData.Quality);');
663
- expect(snippets[8].convertedCode).toBe('m_text_quality.text = Tr.Format("{0}阶", _sutraCardData.Quality);');
742
+ expect(snippets[8].originalIndex).toBe(code.indexOf('Tr.Format("{0}阶", _sutraCardData.Quality)'));
743
+ expect(snippets[8].originalCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
744
+ expect(snippets[8].convertedCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
664
745
  expect(snippets[8].literals).toEqual(['{0}阶']);
665
746
  expect(snippets[8].isChanged).toBe(false);
666
747
 
667
748
  // Snippet 10
668
- expect(snippets[9].originalIndex).toBe(5050);
669
- expect(snippets[9].originalCode).toBe('m_text_tongXuan.text = "通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0") + "%".TR();');
670
- expect(snippets[9].convertedCode).toBe('m_text_tongXuan.text = "通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0").TR() + "%".TR();');
749
+ expect(snippets[9].originalIndex).toBe(code.indexOf('"通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0") + "%".TR()'));
750
+ expect(snippets[9].originalCode).toBe('"通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0") + "%".TR()');
751
+ expect(snippets[9].convertedCode).toBe('"通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0").TR() + "%".TR()');
671
752
  expect(snippets[9].literals).toEqual(['通玄:', '0.0', '%']);
672
753
  expect(snippets[9].isChanged).toBe(true);
673
754
 
674
755
  // Snippet 11
675
- expect(snippets[10].originalIndex).toBe(5943);
676
- expect(snippets[10].originalCode).toBe(`m_text_cost.text = enough
756
+ expect(snippets[10].originalIndex).toBe(code.indexOf(`enough
757
+ ? Tr.Format("[color=#1B8049]{0}/{1}[/color]", curCount, costCount)
758
+ : Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount)`));
759
+ expect(snippets[10].originalCode).toBe(`enough
677
760
  ? Tr.Format("[color=#1B8049]{0}/{1}[/color]", curCount, costCount)
678
- : Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount);`);
679
- expect(snippets[10].convertedCode).toBe(`m_text_cost.text = enough
761
+ : Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount)`);
762
+ expect(snippets[10].convertedCode).toBe(`enough
680
763
  ? Tr.Format("[color=#1B8049]{0}/{1}[/color]", curCount, costCount)
681
- : Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount);`);
764
+ : Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount)`);
682
765
  expect(snippets[10].literals).toEqual(['[color=#1B8049]{0}/{1}[/color]', '[color=#E55E5A]{0}/{1}[/color]']);
683
766
  expect(snippets[10].isChanged).toBe(false);
684
767
 
685
768
  // Snippet 12
686
- expect(snippets[11].originalIndex).toBe(6370);
687
- expect(snippets[11].originalCode).toBe('m_text_upgradeTip.text = dependLockInfo.Item2.TR();');
688
- expect(snippets[11].convertedCode).toBe('m_text_upgradeTip.text = dependLockInfo.Item2.TR();');
769
+ expect(snippets[11].originalIndex).toBe(code.indexOf('dependLockInfo.Item2.TR()'));
770
+ expect(snippets[11].originalCode).toBe('dependLockInfo.Item2.TR()');
771
+ expect(snippets[11].convertedCode).toBe('dependLockInfo.Item2.TR()');
689
772
  expect(snippets[11].literals).toEqual([]);
690
773
  expect(snippets[11].isChanged).toBe(false);
691
774
 
692
775
  // Snippet 13
693
- expect(snippets[12].originalIndex).toBe(7951);
694
- expect(snippets[12].originalCode).toBe('m_effect_fateUnlock.ShowEffect("Effect_FaBao_Unlock");');
695
- expect(snippets[12].convertedCode).toBe('m_effect_fateUnlock.ShowEffect("Effect_FaBao_Unlock");');
776
+ expect(snippets[12].originalIndex).toBe(code.indexOf('"Effect_FaBao_Unlock"', code.indexOf('"Effect_FaBao_Unlock"') + 1));
777
+ expect(snippets[12].originalCode).toBe('"Effect_FaBao_Unlock"');
778
+ expect(snippets[12].convertedCode).toBe('"Effect_FaBao_Unlock"');
696
779
  expect(snippets[12].literals).toEqual(['Effect_FaBao_Unlock']);
697
780
  expect(snippets[12].isChanged).toBe(false);
698
781
 
699
782
  // Snippet 14
700
- expect(snippets[13].originalIndex).toBe(8033);
701
- expect(snippets[13].originalCode).toBe('#region 灵纹信息\n\n m_text_runeSlot.text = Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount);');
702
- expect(snippets[13].convertedCode).toBe('#region 灵纹信息\n\n m_text_runeSlot.text = Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount);');
703
- expect(snippets[13].literals).toEqual([]);
783
+ expect(snippets[13].originalIndex).toBe(code.indexOf('Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount)'));
784
+ expect(snippets[13].originalCode).toBe('Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount)');
785
+ expect(snippets[13].convertedCode).toBe('Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount)');
786
+ expect(snippets[13].literals).toEqual(['({0}/6)']);
704
787
  expect(snippets[13].isChanged).toBe(false);
705
788
 
706
789
  // Snippet 15
707
- expect(snippets[14].originalIndex).toBe(9680);
708
- expect(snippets[14].originalCode).toBe('Log.Error($"Can not find tagId in sutra : {_sutraCardData.Card.Id}");');
709
- expect(snippets[14].convertedCode).toBe('Log.Error(Tr.Format("Can not find tagId in sutra : {0}", _sutraCardData.Card.Id));');
790
+ expect(snippets[14].originalIndex).toBe(code.indexOf('$"Can not find tagId in sutra : {_sutraCardData.Card.Id}"'));
791
+ expect(snippets[14].originalCode).toBe('$"Can not find tagId in sutra : {_sutraCardData.Card.Id}"');
792
+ expect(snippets[14].convertedCode).toBe('Tr.Format("Can not find tagId in sutra : {0}", _sutraCardData.Card.Id)');
710
793
  expect(snippets[14].literals).toEqual(['Can not find tagId in sutra : {0}']);
711
794
  expect(snippets[14].isChanged).toBe(true);
712
795
 
796
+ //#region 需要捕获类成员赋值表达式中, 出现在赋值操作符`=`右侧的字符串值表达式
713
797
  // Snippet 16
714
- expect(snippets[15].originalIndex).toBe(11955);
715
- expect(snippets[15].originalCode).toBe('Toast.Info("卸载成功");');
716
- expect(snippets[15].convertedCode).toBe('Toast.Info("卸载成功");');
717
- expect(snippets[15].literals).toEqual(['卸载成功']);
798
+ expect(snippets[15].originalIndex).toBeGreaterThan(0);
799
+ expect(snippets[15].originalIndex).toBe(code.indexOf('"提示"'));
800
+ expect(snippets[15].originalCode).toBe('"提示"');
801
+ expect(snippets[15].convertedCode).toBe('"提示"');
802
+ expect(snippets[15].literals).toEqual(['提示']);
718
803
  expect(snippets[15].isChanged).toBe(false);
719
804
 
720
805
  // Snippet 17
721
- expect(snippets[16].originalIndex).toBe(12549);
722
- expect(snippets[16].originalCode).toBe('Toast.Info("卸载成功");');
723
- expect(snippets[16].convertedCode).toBe('Toast.Info("卸载成功");');
724
- expect(snippets[16].literals).toEqual(['卸载成功']);
806
+ expect(snippets[16].originalIndex).toBeGreaterThan(0);
807
+ expect(snippets[16].originalIndex).toBe(code.indexOf('"是否一键卸下法宝当前镶嵌灵纹"'));
808
+ expect(snippets[16].originalCode).toBe('"是否一键卸下法宝当前镶嵌灵纹"');
809
+ expect(snippets[16].convertedCode).toBe('"是否一键卸下法宝当前镶嵌灵纹"');
810
+ expect(snippets[16].literals).toEqual(['是否一键卸下法宝当前镶嵌灵纹']);
725
811
  expect(snippets[16].isChanged).toBe(false);
812
+
813
+ // Snippet 18
814
+ expect(snippets[17].originalIndex).toBeGreaterThan(0);
815
+ expect(snippets[17].originalIndex).toBe(code.indexOf('"确认"'));
816
+ expect(snippets[17].originalCode).toBe('"确认"');
817
+ expect(snippets[17].convertedCode).toBe('"确认"');
818
+ expect(snippets[17].literals).toEqual(['确认']);
819
+ expect(snippets[17].isChanged).toBe(false);
820
+ //#endregion 需要捕获类成员赋值句式中, 出现在赋值操作符`=`右侧的字符串值表达式
821
+
822
+ // Snippet 19
823
+ expect(snippets[18].originalIndex).toBeGreaterThan(0);
824
+ expect(snippets[18].originalIndex).toBe(code.indexOf('"卸载成功"'));
825
+ expect(snippets[18].originalCode).toBe('"卸载成功"');
826
+ expect(snippets[18].convertedCode).toBe('"卸载成功"');
827
+ expect(snippets[18].literals).toEqual(['卸载成功']);
828
+ expect(snippets[18].isChanged).toBe(false);
829
+
830
+ //#region 需要捕获类成员赋值句式中, 出现在赋值操作符`=`右侧的字符串值表达式
831
+ // Snippet 20
832
+ expect(snippets[19].originalIndex).toBeGreaterThan(0);
833
+ expect(snippets[19].originalIndex).toBe(code.indexOf('"取消"'));
834
+ expect(snippets[19].originalCode).toBe('"取消"');
835
+ expect(snippets[19].convertedCode).toBe('"取消"');
836
+ expect(snippets[19].literals).toEqual(['取消']);
837
+ expect(snippets[19].isChanged).toBe(false);
838
+
839
+ // Snippet 21
840
+ expect(snippets[20].originalIndex).toBeGreaterThan(0);
841
+ expect(snippets[20].originalIndex).toBe(code.indexOf('"本次登录不再提示"'));
842
+ expect(snippets[20].originalCode).toBe('"本次登录不再提示"');
843
+ expect(snippets[20].convertedCode).toBe('"本次登录不再提示"');
844
+ expect(snippets[20].literals).toEqual(['本次登录不再提示']);
845
+ expect(snippets[20].isChanged).toBe(false);
846
+ //#endregion 需要捕获类成员赋值句式中, 出现在赋值操作符`=`右侧的字符串值表达式
847
+
848
+ // Snippet 22
849
+ expect(snippets[21].originalIndex).toBeGreaterThan(0);
850
+ expect(snippets[21].originalIndex).toBe(code.indexOf('"卸载成功"', code.indexOf('"卸载成功"') + 1));
851
+ expect(snippets[21].originalCode).toBe('"卸载成功"');
852
+ expect(snippets[21].convertedCode).toBe('"卸载成功"');
853
+ expect(snippets[21].literals).toEqual(['卸载成功']);
854
+ expect(snippets[21].isChanged).toBe(false);
726
855
  });
727
856
 
857
+ // 处理C#多行语句
728
858
  test('should handle multilne content15', () => {
729
859
  const code = readFileSync('./test/GuildDonateDialog.cs', 'utf8');
730
860
  const snippets = extractor.extractStrings(code);
@@ -739,16 +869,17 @@ describe('CSharpStringExtractor', () => {
739
869
  expect(targetSnippet).toBeDefined();
740
870
 
741
871
  if (targetSnippet) {
742
- expect(targetSnippet.originalIndex).toBe(2685);
743
- expect(targetSnippet.originalCode).toBe(`m_btn_paid.text = $"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}";`);
744
- expect(targetSnippet.convertedCode).toBe(`m_btn_paid.text = Tr.Format("<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}", paidConfig.Quantity);`);
872
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}"`));
873
+ expect(targetSnippet.originalCode).toBe(`$"<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{paidConfig.Quantity}"`);
874
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}", paidConfig.Quantity)`);
745
875
  expect(targetSnippet.literals).toEqual(["<img src='ui://a0w66rlc7bhfusff'/ width = '70' height = '70'>{0}"]);
746
876
  expect(targetSnippet.isChanged).toBe(true);
747
877
  }
748
878
 
749
879
  });
750
880
 
751
- test('should handle multilne content16', () => {
881
+ // 处理C#普通字符串包含 `//` 的情形
882
+ test('should handle string with `//`', () => {
752
883
  const code = `m_btn_paid.text = "//";`
753
884
  const snippets = extractor.extractStrings(code);
754
885
 
@@ -756,13 +887,14 @@ describe('CSharpStringExtractor', () => {
756
887
  expect(snippets.length).toBeGreaterThan(0);
757
888
 
758
889
  let targetSnippet = snippets[0];
759
- expect(targetSnippet.originalCode).toBe(`m_btn_paid.text = "//";`);
760
- expect(targetSnippet.convertedCode).toBe(`m_btn_paid.text = "//".TR();`);
890
+ expect(targetSnippet.originalCode).toBe(`"//"`);
891
+ expect(targetSnippet.convertedCode).toBe(`"//".TR()`);
761
892
  expect(targetSnippet.literals).toEqual(['//']);
762
893
  expect(targetSnippet.isChanged).toBe(true);
763
894
 
764
895
  });
765
896
 
897
+ // 处理C# `+=` 形式赋值语句
766
898
  test('should handle string template with format specifier', () => {
767
899
  const code = `infoStr += $"最终抗性: {resistReduce:F}\\n";`
768
900
  const snippets = extractor.extractStrings(code);
@@ -771,12 +903,13 @@ describe('CSharpStringExtractor', () => {
771
903
  expect(snippets.length).toBeGreaterThan(0);
772
904
 
773
905
  let targetSnippet = snippets[0];
774
- expect(targetSnippet.originalCode).toBe(`infoStr += $"最终抗性: {resistReduce:F}\\n";`);
775
- expect(targetSnippet.convertedCode).toBe(`infoStr += Tr.Format("最终抗性: {0:F}\\n", resistReduce);`);
906
+ expect(targetSnippet.originalCode).toBe(`$"最终抗性: {resistReduce:F}\\n"`);
907
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("最终抗性: {0:F}\\n", resistReduce)`);
776
908
  expect(targetSnippet.literals).toEqual(['最终抗性: {0:F}\\n']);
777
909
  expect(targetSnippet.isChanged).toBe(true);
778
910
  });
779
911
 
912
+ // 处理C#多行语句, 特别是包含 `\n` 的字符串
780
913
  test('should handle multilne content17', () => {
781
914
  const code = `
782
915
  infoStr += $"aaa: {aa}\n" +
@@ -788,10 +921,10 @@ describe('CSharpStringExtractor', () => {
788
921
  expect(snippets.length).toBeGreaterThan(0);
789
922
 
790
923
  let targetSnippet = snippets[0];
791
- expect(targetSnippet.originalCode).toBe(`infoStr += $"aaa: {aa}\n" +
792
- $"bbb = {bb}";`);
793
- expect(targetSnippet.convertedCode).toBe(`infoStr += Tr.Format("aaa: {0}\n", aa) +
794
- Tr.Format("bbb = {0}", bb);`);
924
+ expect(targetSnippet.originalCode).toBe(`$"aaa: {aa}\n" +
925
+ $"bbb = {bb}"`);
926
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("aaa: {0}\n", aa) +
927
+ Tr.Format("bbb = {0}", bb)`);
795
928
  expect(targetSnippet.literals).toEqual([
796
929
  'aaa: {0}\n',
797
930
  'bbb = {0}'
@@ -799,6 +932,7 @@ describe('CSharpStringExtractor', () => {
799
932
  expect(targetSnippet.isChanged).toBe(true);
800
933
  });
801
934
 
935
+ // 处理C#多行语句, 特别是包含 `\\n` 的内插字符串
802
936
  test('should handle multilne content19', () => {
803
937
  const code = `
804
938
  infoStr += $"aaa: {aa}\\n" +
@@ -810,10 +944,10 @@ describe('CSharpStringExtractor', () => {
810
944
  expect(snippets.length).toBeGreaterThan(0);
811
945
 
812
946
  let targetSnippet = snippets[0];
813
- expect(targetSnippet.originalCode).toBe(`infoStr += $"aaa: {aa}\\n" +
814
- $"bbb = {bb}";`);
815
- expect(targetSnippet.convertedCode).toBe(`infoStr += Tr.Format("aaa: {0}\\n", aa) +
816
- Tr.Format("bbb = {0}", bb);`);
947
+ expect(targetSnippet.originalCode).toBe(`$"aaa: {aa}\\n" +
948
+ $"bbb = {bb}"`);
949
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("aaa: {0}\\n", aa) +
950
+ Tr.Format("bbb = {0}", bb)`);
817
951
  expect(targetSnippet.literals).toEqual([
818
952
  'aaa: {0}\\n',
819
953
  'bbb = {0}'
@@ -821,6 +955,7 @@ describe('CSharpStringExtractor', () => {
821
955
  expect(targetSnippet.isChanged).toBe(true);
822
956
  });
823
957
 
958
+ // 处理C#中多行字符串拼接成的字符串表达式
824
959
  test('should handle multilne content18', () => {
825
960
  const code = `
826
961
  infoStr += "伤害公式: \\n" +
@@ -838,22 +973,22 @@ describe('CSharpStringExtractor', () => {
838
973
  expect(snippets.length).toBeGreaterThan(0);
839
974
 
840
975
  let targetSnippet = snippets[0];
841
- expect(targetSnippet.originalCode).toBe(`infoStr += "伤害公式: \\n" +
976
+ expect(targetSnippet.originalCode).toBe(`"伤害公式: \\n" +
842
977
  $"最终伤害[color={colorCode}][{(float)damageValue:F}][/color] = \\n" +
843
978
  $"基础伤害[b][{baseValue:F}][/b] X \\n" +
844
979
  $"暴击伤害加成[b][{damageInfo.CritDamageRatio:F}][/b] X \\n" +
845
980
  $"最终伤害加成[b][{1 + damageInfo.FinalDamageRatio:F}][/b] X \\n" +
846
981
  $"最终承伤加成[b][{1 + damageInfo.FinalInjuryRatio:F}][/b] / \\n" +
847
982
  $"最终伤害减免[b][{1 + damageInfo.FinalDamageReduce:F}][/b] X \\n" +
848
- $"抗性减免[b][{resistReduce:F}]";`);
849
- expect(targetSnippet.convertedCode).toBe(`infoStr += "伤害公式: \\n".TR() +
983
+ $"抗性减免[b][{resistReduce:F}]"`);
984
+ expect(targetSnippet.convertedCode).toBe(`"伤害公式: \\n".TR() +
850
985
  Tr.Format("最终伤害[color={0}][{1:F}][/color] = \\n", colorCode, (float)damageValue) +
851
986
  Tr.Format("基础伤害[b][{0:F}][/b] X \\n", baseValue) +
852
987
  Tr.Format("暴击伤害加成[b][{0:F}][/b] X \\n", damageInfo.CritDamageRatio) +
853
988
  Tr.Format("最终伤害加成[b][{0:F}][/b] X \\n", 1 + damageInfo.FinalDamageRatio) +
854
989
  Tr.Format("最终承伤加成[b][{0:F}][/b] / \\n", 1 + damageInfo.FinalInjuryRatio) +
855
990
  Tr.Format("最终伤害减免[b][{0:F}][/b] X \\n", 1 + damageInfo.FinalDamageReduce) +
856
- Tr.Format("抗性减免[b][{0:F}]", resistReduce);`);
991
+ Tr.Format("抗性减免[b][{0:F}]", resistReduce)`);
857
992
  expect(targetSnippet.literals).toEqual([
858
993
  '伤害公式: \\n',
859
994
  '最终伤害[color={0}][{1:F}][/color] = \\n',
@@ -867,7 +1002,8 @@ describe('CSharpStringExtractor', () => {
867
1002
  expect(targetSnippet.isChanged).toBe(true);
868
1003
  });
869
1004
 
870
- test('should handle lack bracket 1', () => {
1005
+ // 处理C#中包含括号的内插字符串
1006
+ test('should handle bracket 1', () => {
871
1007
  const code = `m_text_time.text = $"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}";`
872
1008
  const snippets = extractor.extractStrings(code);
873
1009
 
@@ -875,15 +1011,16 @@ describe('CSharpStringExtractor', () => {
875
1011
  expect(snippets.length).toBeGreaterThan(0);
876
1012
 
877
1013
  let targetSnippet = snippets[0];
878
- expect(targetSnippet.originalCode).toBe(`m_text_time.text = $"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}";`);
879
- expect(targetSnippet.convertedCode).toBe(`m_text_time.text = Tr.Format("下一轮神兽出现倒计时: {0}", Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp));`);
1014
+ expect(targetSnippet.originalCode).toBe(`$"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}"`);
1015
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("下一轮神兽出现倒计时: {0}", Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp))`);
880
1016
  expect(targetSnippet.literals).toEqual([
881
1017
  '下一轮神兽出现倒计时: {0}'
882
1018
  ]);
883
1019
  expect(targetSnippet.isChanged).toBe(true);
884
1020
  });
885
1021
 
886
- test('should handle lack bracket 2', () => {
1022
+ // 处理C#中包含括号的内插字符串
1023
+ test('should handle bracket 2', () => {
887
1024
  const code = `m_text_time.text = $"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}";`
888
1025
  const snippets = extractor.extractStrings(code);
889
1026
 
@@ -891,15 +1028,16 @@ describe('CSharpStringExtractor', () => {
891
1028
  expect(snippets.length).toBeGreaterThan(0);
892
1029
 
893
1030
  let targetSnippet = snippets[0];
894
- expect(targetSnippet.originalCode).toBe(`m_text_time.text = $"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}";`);
895
- expect(targetSnippet.convertedCode).toBe(`m_text_time.text = Tr.Format("神兽离去时间: {0}", Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp));`);
1031
+ expect(targetSnippet.originalCode).toBe(`$"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}"`);
1032
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("神兽离去时间: {0}", Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp))`);
896
1033
  expect(targetSnippet.literals).toEqual([
897
1034
  '神兽离去时间: {0}'
898
1035
  ]);
899
1036
  expect(targetSnippet.isChanged).toBe(true);
900
1037
  });
901
1038
 
902
- test('should handle lack bracket 3', () => {
1039
+ // 处理C#中包含括号的内插字符串
1040
+ test('should handle bracket 3', () => {
903
1041
  const code = `desc += $"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}";`
904
1042
  const snippets = extractor.extractStrings(code);
905
1043
 
@@ -907,15 +1045,16 @@ describe('CSharpStringExtractor', () => {
907
1045
  expect(snippets.length).toBeGreaterThan(0);
908
1046
 
909
1047
  let targetSnippet = snippets[0];
910
- expect(targetSnippet.originalCode).toBe(`desc += $"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}";`);
911
- expect(targetSnippet.convertedCode).toBe(`desc += Tr.Format("剩余{0}", ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time));`);
1048
+ expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}"`);
1049
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time))`);
912
1050
  expect(targetSnippet.literals).toEqual([
913
1051
  '剩余{0}'
914
1052
  ]);
915
1053
  expect(targetSnippet.isChanged).toBe(true);
916
1054
  });
917
1055
 
918
- test('should handle lack bracket 4', () => {
1056
+ // 处理C#中包含括号的内插字符串
1057
+ test('should handle bracket 4', () => {
919
1058
  const code = `desc += $"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}";`
920
1059
  const snippets = extractor.extractStrings(code);
921
1060
 
@@ -923,14 +1062,15 @@ describe('CSharpStringExtractor', () => {
923
1062
  expect(snippets.length).toBeGreaterThan(0);
924
1063
 
925
1064
  let targetSnippet = snippets[0];
926
- expect(targetSnippet.originalCode).toBe(`desc += $"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}";`);
927
- expect(targetSnippet.convertedCode).toBe(`desc += Tr.Format("剩余{0}", ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time())));`);
1065
+ expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}"`);
1066
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time())))`);
928
1067
  expect(targetSnippet.literals).toEqual([
929
1068
  '剩余{0}'
930
1069
  ]);
931
1070
  expect(targetSnippet.isChanged).toBe(true);
932
1071
  });
933
1072
 
1073
+ // 处理C#中包含`+=`符号的内插字符串
934
1074
  test('should handle duplicate tr handle 1', () => {
935
1075
  const code = `infoStr = $"+={colorCode}";`
936
1076
  const snippets = extractor.extractStrings(code);
@@ -939,14 +1079,15 @@ describe('CSharpStringExtractor', () => {
939
1079
  expect(snippets.length).toBeGreaterThan(0);
940
1080
 
941
1081
  let targetSnippet = snippets[0];
942
- expect(targetSnippet.originalCode).toBe(`infoStr = $"+={colorCode}";`);
943
- expect(targetSnippet.convertedCode).toBe(`infoStr = Tr.Format("+={0}", colorCode);`);
1082
+ expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
1083
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
944
1084
  expect(targetSnippet.literals).toEqual([
945
1085
  '+={0}'
946
1086
  ]);
947
1087
  expect(targetSnippet.isChanged).toBe(true);
948
1088
  });
949
1089
 
1090
+ // 处理C# `+=` 形式的赋值语句后接 包含 `=` 符号的内插字符串
950
1091
  test('should handle duplicate tr handle 2', () => {
951
1092
  const code = `infoStr += $"={colorCode}";`
952
1093
  const snippets = extractor.extractStrings(code);
@@ -955,14 +1096,15 @@ describe('CSharpStringExtractor', () => {
955
1096
  expect(snippets.length).toBeGreaterThan(0);
956
1097
 
957
1098
  let targetSnippet = snippets[0];
958
- expect(targetSnippet.originalCode).toBe(`infoStr += $"={colorCode}";`);
959
- expect(targetSnippet.convertedCode).toBe(`infoStr += Tr.Format("={0}", colorCode);`);
1099
+ expect(targetSnippet.originalCode).toBe(`$"={colorCode}"`);
1100
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("={0}", colorCode)`);
960
1101
  expect(targetSnippet.literals).toEqual([
961
1102
  '={0}'
962
1103
  ]);
963
1104
  expect(targetSnippet.isChanged).toBe(true);
964
1105
  });
965
1106
 
1107
+ // 处理C# `+=` 形式的赋值语句后接 包含 `+=` 符号的内插字符串
966
1108
  test('should handle duplicate tr handle 3', () => {
967
1109
  const code = `infoStr += $"+={colorCode}";`
968
1110
  const snippets = extractor.extractStrings(code);
@@ -971,14 +1113,15 @@ describe('CSharpStringExtractor', () => {
971
1113
  expect(snippets.length).toBeGreaterThan(0);
972
1114
 
973
1115
  let targetSnippet = snippets[0];
974
- expect(targetSnippet.originalCode).toBe(`infoStr += $"+={colorCode}";`);
975
- expect(targetSnippet.convertedCode).toBe(`infoStr += Tr.Format("+={0}", colorCode);`);
1116
+ expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
1117
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
976
1118
  expect(targetSnippet.literals).toEqual([
977
1119
  '+={0}'
978
1120
  ]);
979
1121
  expect(targetSnippet.isChanged).toBe(true);
980
1122
  });
981
-
1123
+
1124
+ // 处理C# `+=` 形式的赋值语句后接包含 `=`、`[`、`]`、`{`、`}`、`=` 等特殊符号的内插字符串
982
1125
  test('should handle duplicate tr handle 4', () => {
983
1126
  const code = `infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";`
984
1127
  const snippets = extractor.extractStrings(code);
@@ -987,14 +1130,15 @@ describe('CSharpStringExtractor', () => {
987
1130
  expect(snippets.length).toBeGreaterThan(0);
988
1131
 
989
1132
  let targetSnippet = snippets[0];
990
- expect(targetSnippet.originalCode).toBe(`infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";`);
991
- expect(targetSnippet.convertedCode).toBe(`infoStr += Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType);`);
1133
+ expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
1134
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
992
1135
  expect(targetSnippet.literals).toEqual([
993
1136
  '伤害类型: [color={0}][b]{1}[/b][/color]\n'
994
1137
  ]);
995
1138
  expect(targetSnippet.isChanged).toBe(true);
996
1139
  });
997
1140
 
1141
+ // 处理C#中函数调用参数中包含普通字符串的情况, 函数参数需要拆分, 逐个参数捕获
998
1142
  test('should handle para 1', () => {
999
1143
  const code = `SetData("等级", level + 1);`
1000
1144
  const snippets = extractor.extractStrings(code);
@@ -1003,14 +1147,16 @@ describe('CSharpStringExtractor', () => {
1003
1147
  expect(snippets.length).toBeGreaterThan(0);
1004
1148
 
1005
1149
  let targetSnippet = snippets[0];
1006
- expect(targetSnippet.originalCode).toBe(`SetData("等级", level + 1);`);
1007
- expect(targetSnippet.convertedCode).toBe(`SetData("等级", level + 1);`);
1150
+ // 函数参数需要拆分, 逐个参数捕获
1151
+ expect(targetSnippet.originalCode).toBe(`"等级"`);
1152
+ expect(targetSnippet.convertedCode).toBe(`"等级"`);
1008
1153
  expect(targetSnippet.literals).toEqual([
1009
1154
  "等级"
1010
1155
  ]);
1011
1156
  expect(targetSnippet.isChanged).toBe(false);
1012
1157
  });
1013
1158
 
1159
+ // 处理C#中函数调用参数中包含普通字符串的情况
1014
1160
  test('should handle para 2', () => {
1015
1161
  const code = `bbb.Func2("等级", level, level + 1, false, isMax);`
1016
1162
  const snippets = extractor.extractStrings(code);
@@ -1019,11 +1165,1330 @@ describe('CSharpStringExtractor', () => {
1019
1165
  expect(snippets.length).toBeGreaterThan(0);
1020
1166
 
1021
1167
  let targetSnippet = snippets[0];
1022
- expect(targetSnippet.originalCode).toBe(`bbb.Func2("等级", level, level + 1, false, isMax);`);
1023
- expect(targetSnippet.convertedCode).toBe(`bbb.Func2("等级", level, level + 1, false, isMax);`);
1168
+ expect(targetSnippet.originalCode).toBe(`"等级"`);
1169
+ expect(targetSnippet.convertedCode).toBe(`"等级"`);
1024
1170
  expect(targetSnippet.literals).toEqual([
1025
1171
  "等级"
1026
1172
  ]);
1027
1173
  expect(targetSnippet.isChanged).toBe(false);
1028
1174
  });
1175
+
1176
+ // 处理C#中switch语句中包含普通字符串的情况
1177
+ test('should handle switch 1', () => {
1178
+ const code = `
1179
+ switch(condition){
1180
+ default:
1181
+ Log.Warning("实时更新自:" + rankData.Type);
1182
+ break;
1183
+ }`
1184
+ const snippets = extractor.extractStrings(code);
1185
+
1186
+ // 验证提取的片段数量
1187
+ expect(snippets.length).toBeGreaterThan(0);
1188
+
1189
+ let targetSnippet = snippets[0];
1190
+ expect(targetSnippet.originalCode).toBe(`"实时更新自:" + rankData.Type`);
1191
+ // 普通函数参数不需要追加 `.TR()`, 除非包含由 `+` 符号连接字符串等的情况
1192
+ expect(targetSnippet.convertedCode).toBe(`"实时更新自:" + rankData.Type`);
1193
+ expect(targetSnippet.literals).toEqual([
1194
+ "实时更新自:"
1195
+ ]);
1196
+ expect(targetSnippet.isChanged).toBe(false);
1197
+ });
1198
+
1199
+ // 处理C#中switch语句中包含普通字符串的情况
1200
+ test('should handle switch 2', () => {
1201
+ const code = `
1202
+ switch(kwjkwlje){
1203
+ case (int)RANK_TYPE.天梯段位:
1204
+ Debug.Log1("jkwhfehwfjkh:" + rank2Data.T4zype);
1205
+ break;
1206
+ }`
1207
+ const snippets = extractor.extractStrings(code);
1208
+
1209
+ // 验证提取的片段数量, switch 语句中包含普通字符串的情况
1210
+ expect(snippets.length).toBeGreaterThan(0);
1211
+
1212
+ let targetSnippet = snippets[0];
1213
+ expect(targetSnippet.originalCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
1214
+ expect(targetSnippet.convertedCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
1215
+ expect(targetSnippet.literals).toEqual([
1216
+ "jkwhfehwfjkh:"
1217
+ ]);
1218
+ expect(targetSnippet.isChanged).toBe(false);
1219
+ });
1220
+
1221
+ // 处理C#中switch语句中包含普通字符串的情况
1222
+ test('should handle switch 3', () => {
1223
+ const code = `
1224
+ switch(lkw){
1225
+ case V434:
1226
+ {
1227
+ Action2("wegwfw:" + varnws);
1228
+ break;
1229
+ }
1230
+ }`
1231
+ const snippets = extractor.extractStrings(code);
1232
+
1233
+ // 验证提取的片段数量
1234
+ expect(snippets.length).toBeGreaterThan(0);
1235
+
1236
+ let targetSnippet = snippets[0];
1237
+ expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
1238
+ expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
1239
+ expect(targetSnippet.literals).toEqual([
1240
+ "wegwfw:"
1241
+ ]);
1242
+ expect(targetSnippet.isChanged).toBe(false);
1243
+ });
1244
+
1245
+ // 处理C#中switch语句中包含普通字符串的情况
1246
+ test('should handle switch 4', () => {
1247
+ const code = `
1248
+ case V434:
1249
+ Action2("wegwfw:" + varnws);
1250
+ break;`
1251
+ const snippets = extractor.extractStrings(code);
1252
+
1253
+ // 验证提取的片段数量
1254
+ expect(snippets.length).toBeGreaterThan(0);
1255
+
1256
+ let targetSnippet = snippets[0];
1257
+ expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
1258
+ expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
1259
+ expect(targetSnippet.literals).toEqual([
1260
+ "wegwfw:"
1261
+ ]);
1262
+ expect(targetSnippet.isChanged).toBe(false);
1263
+ });
1264
+
1265
+ // 处理 C#内容中存在多个相同的包含字符串的语句时, 要每个都提取出来
1266
+ test('should handle lost 1', () => {
1267
+ const code = readFileSync('./test/KeeperDialog.cs', 'utf8');
1268
+ const snippets = extractor.extractStrings(code);
1269
+
1270
+ let targetSnippet = snippets[3];
1271
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"已购买";
1272
+ m_btn_buy.enabled = false;
1273
+
1274
+ //购买后自动启用`));
1275
+ expect(targetSnippet.originalCode).toBe(`"已购买"`);
1276
+ expect(targetSnippet.convertedCode).toBe(`"已购买".TR()`);
1277
+ expect(targetSnippet.literals).toEqual([
1278
+ "已购买"
1279
+ ]);
1280
+ expect(targetSnippet.isChanged).toBe(true);
1281
+
1282
+ // 验证提取的片段数量
1283
+ expect(snippets.length).toBeGreaterThanOrEqual(5)
1284
+ });
1285
+
1286
+ // 字符串中包含 `)` 符号时, 也需要捕获该字符串
1287
+ test('should handle lost 2', () => {
1288
+ const code = `m_text_runeSlot.text = Tr.Format(")", _sutraCardData.Card.RuneOpenCount);`;
1289
+ const snippets = extractor.extractStrings(code);
1290
+
1291
+ let targetSnippet = snippets[0];
1292
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`));
1293
+ expect(targetSnippet.originalCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
1294
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
1295
+ // 字符串中包含 `)` 符号时, 也需要捕获该字符串
1296
+ expect(targetSnippet.literals).toEqual([
1297
+ ')'
1298
+ ]);
1299
+ expect(targetSnippet.isChanged).toBe(false);
1300
+
1301
+ });
1302
+
1303
+ // 处理 C#内容时, 需要匹配出包含 `(` 符号的字符串表达式
1304
+ test('should handle lost 3', () => {
1305
+ const code = `m_text_runeSlot.text = ")";`;
1306
+ const snippets = extractor.extractStrings(code);
1307
+
1308
+ let targetSnippet = snippets[0];
1309
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`")"`));
1310
+ expect(targetSnippet.originalCode).toBe(`")"`);
1311
+ expect(targetSnippet.convertedCode).toBe(`")".TR()`);
1312
+ expect(targetSnippet.literals).toEqual([
1313
+ ')'
1314
+ ]);
1315
+ expect(targetSnippet.isChanged).toBe(true);
1316
+
1317
+ });
1318
+
1319
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1320
+ test('should handle return statement 1', () => {
1321
+ const code = `return "参数错误";`;
1322
+ const snippets = extractor.extractStrings(code);
1323
+
1324
+ let targetSnippet = snippets[0];
1325
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"参数错误"`));
1326
+ expect(targetSnippet.originalCode).toBe(`"参数错误"`);
1327
+ expect(targetSnippet.convertedCode).toBe(`"参数错误"`);
1328
+ expect(targetSnippet.literals).toEqual([
1329
+ '参数错误'
1330
+ ]);
1331
+ expect(targetSnippet.isChanged).toBe(false);
1332
+
1333
+ });
1334
+
1335
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1336
+ test('should handle return statement 2', () => {
1337
+ const code = `return "";`;
1338
+ const snippets = extractor.extractStrings(code);
1339
+
1340
+ let targetSnippet = snippets[0];
1341
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`""`));
1342
+ expect(targetSnippet.originalCode).toBe(`""`);
1343
+ expect(targetSnippet.convertedCode).toBe(`""`);
1344
+ expect(targetSnippet.literals).toEqual([
1345
+ ''
1346
+ ]);
1347
+ expect(targetSnippet.isChanged).toBe(false);
1348
+
1349
+ });
1350
+
1351
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1352
+ test('should handle return statement 3', () => {
1353
+ const code = `return $"";`;
1354
+ const snippets = extractor.extractStrings(code);
1355
+
1356
+ let targetSnippet = snippets[0];
1357
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$""`));
1358
+ expect(targetSnippet.originalCode).toBe(`$""`);
1359
+ expect(targetSnippet.convertedCode).toBe(`$""`);
1360
+ expect(targetSnippet.literals).toEqual([
1361
+ ''
1362
+ ]);
1363
+ expect(targetSnippet.isChanged).toBe(false);
1364
+
1365
+ });
1366
+
1367
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1368
+ test('should handle return statement 4', () => {
1369
+ const code = `return $"fwefwe";`;
1370
+ const snippets = extractor.extractStrings(code);
1371
+
1372
+ let targetSnippet = snippets[0];
1373
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"fwefwe"`));
1374
+ expect(targetSnippet.originalCode).toBe(`$"fwefwe"`);
1375
+ expect(targetSnippet.convertedCode).toBe(`$"fwefwe"`);
1376
+ expect(targetSnippet.literals).toEqual([
1377
+ 'fwefwe'
1378
+ ]);
1379
+ expect(targetSnippet.isChanged).toBe(false);
1380
+
1381
+ });
1382
+
1383
+ // 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
1384
+ test('should handle return statement 5', () => {
1385
+ const code = `return $"wefwf{fwef}";`;
1386
+ const snippets = extractor.extractStrings(code);
1387
+
1388
+ let targetSnippet = snippets[0];
1389
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"wefwf{fwef}"`));
1390
+ expect(targetSnippet.originalCode).toBe(`$"wefwf{fwef}"`);
1391
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("wefwf{0}", fwef)`);
1392
+ expect(targetSnippet.literals).toEqual([
1393
+ 'wefwf{0}'
1394
+ ]);
1395
+ expect(targetSnippet.isChanged).toBe(true);
1396
+
1397
+ });
1398
+
1399
+ // 测试处理C#字符串表达式中包含各种特殊字符的情况
1400
+ test('should handle special characters in string expression 1', () => {
1401
+ const code = `return "abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\";`;
1402
+ const snippets = extractor.extractStrings(code);
1403
+
1404
+ let targetSnippet = snippets[0];
1405
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
1406
+ expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1407
+ expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1408
+ expect(targetSnippet.literals).toEqual([
1409
+ 'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
1410
+ ]);
1411
+ expect(targetSnippet.isChanged).toBe(false);
1412
+ });
1413
+
1414
+ // 测试处理在C#类成员初始赋值语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1415
+ test('should handle member initial assignment', () => {
1416
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1417
+ const snippets = extractor.extractStrings(code);
1418
+ {
1419
+ let targetSnippet = snippets[0];
1420
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\\\"`));
1421
+ expect(targetSnippet.originalCode).toBe(`"\\\\"`);
1422
+ expect(targetSnippet.convertedCode).toBe(`"\\\\"`);
1423
+ expect(targetSnippet.literals).toEqual([
1424
+ '\\\\'
1425
+ ]);
1426
+ expect(targetSnippet.isChanged).toBe(false);
1427
+ }
1428
+ {
1429
+ let targetSnippet = snippets[1];
1430
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\n"`));
1431
+ expect(targetSnippet.originalCode).toBe(`"\\n"`);
1432
+ expect(targetSnippet.convertedCode).toBe(`"\\n"`);
1433
+ expect(targetSnippet.literals).toEqual([
1434
+ '\\n'
1435
+ ]);
1436
+ expect(targetSnippet.isChanged).toBe(false);
1437
+ }
1438
+ {
1439
+ let targetSnippet = snippets[2];
1440
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"\\t"`));
1441
+ expect(targetSnippet.originalCode).toBe(`$"\\t"`);
1442
+ expect(targetSnippet.convertedCode).toBe(`$"\\t"`);
1443
+ expect(targetSnippet.literals).toEqual([
1444
+ '\\t'
1445
+ ]);
1446
+ expect(targetSnippet.isChanged).toBe(false);
1447
+ }
1448
+ });
1449
+
1450
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1451
+ test('should handle special characters in string expression 3', () => {
1452
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1453
+ const snippets = extractor.extractStrings(code);
1454
+ {
1455
+ let targetSnippet = snippets[3];
1456
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1457
+ expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1458
+ expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1459
+ expect(targetSnippet.literals).toEqual([
1460
+ '1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1461
+ ]);
1462
+ expect(targetSnippet.isChanged).toBe(false);
1463
+ }
1464
+ {
1465
+ let targetSnippet = snippets[4];
1466
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1467
+ expect(targetSnippet.originalCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1468
+ expect(targetSnippet.convertedCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1469
+ expect(targetSnippet.literals).toEqual([
1470
+ '2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1471
+ ]);
1472
+ expect(targetSnippet.isChanged).toBe(false);
1473
+ }
1474
+ {
1475
+ let targetSnippet = snippets[6];
1476
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1477
+ expect(targetSnippet.originalCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1478
+ expect(targetSnippet.convertedCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1479
+ expect(targetSnippet.literals).toEqual([
1480
+ '4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1481
+ ]);
1482
+ expect(targetSnippet.isChanged).toBe(false);
1483
+ }
1484
+ });
1485
+
1486
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1487
+ test('should handle special characters in string expression 3.1', () => {
1488
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1489
+ const snippets = extractor.extractStrings(code);
1490
+ {
1491
+ let targetSnippet = snippets[3];
1492
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1493
+ expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1494
+ expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1495
+ expect(targetSnippet.literals).toEqual([
1496
+ '1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1497
+ ]);
1498
+ expect(targetSnippet.isChanged).toBe(false);
1499
+ }
1500
+ });
1501
+
1502
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1503
+ test('should handle special characters in string expression of return sentence', () => {
1504
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1505
+ const snippets = extractor.extractStrings(code);
1506
+ {
1507
+ let targetSnippet = snippets[7];
1508
+ // 需要统一匹配 `return` 语句中的字符串表达式
1509
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1510
+ expect(targetSnippet.originalCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1511
+ expect(targetSnippet.convertedCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1512
+ expect(targetSnippet.literals).toEqual([
1513
+ '5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1514
+ ]);
1515
+ expect(targetSnippet.isChanged).toBe(false);
1516
+ }
1517
+ });
1518
+
1519
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1520
+ test('should handle special characters in string expression 3.2', () => {
1521
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1522
+ const snippets = extractor.extractStrings(code);
1523
+ {
1524
+ let targetSnippet = snippets[8];
1525
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1526
+ expect(targetSnippet.originalCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1527
+ expect(targetSnippet.convertedCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1528
+ expect(targetSnippet.literals).toEqual([
1529
+ '6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1530
+ ]);
1531
+ expect(targetSnippet.isChanged).toBe(false);
1532
+ }
1533
+ {
1534
+ let targetSnippet = snippets[9];
1535
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1536
+ expect(targetSnippet.originalCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1537
+ expect(targetSnippet.convertedCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1538
+ expect(targetSnippet.literals).toEqual([
1539
+ '7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1540
+ ]);
1541
+ expect(targetSnippet.isChanged).toBe(false);
1542
+ }
1543
+ });
1544
+
1545
+ // 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1546
+ test('should handle special characters in string expression 4', () => {
1547
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1548
+ const snippets = extractor.extractStrings(code);
1549
+ {
1550
+ let targetSnippet = snippets[5];
1551
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1552
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1553
+ expect(targetSnippet.originalCode).toBe(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1554
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wef)`);
1555
+ expect(targetSnippet.literals).toEqual([
1556
+ '3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1557
+ ]);
1558
+ expect(targetSnippet.isChanged).toBe(true);
1559
+ }
1560
+ });
1561
+
1562
+ // 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
1563
+ test('should handle special characters in string expression 5', () => {
1564
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1565
+ const snippets = extractor.extractStrings(code);
1566
+ {
1567
+ let targetSnippet = snippets[10];
1568
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1569
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1570
+ expect(targetSnippet.originalCode).toBe(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1571
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1572
+ expect(targetSnippet.literals).toEqual([
1573
+ '8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1574
+ ]);
1575
+ expect(targetSnippet.isChanged).toBe(true);
1576
+ }
1577
+ });
1578
+
1579
+ // 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
1580
+ test('should handle special characters in string expression 6', () => {
1581
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1582
+ const snippets = extractor.extractStrings(code);
1583
+ {
1584
+ let targetSnippet = snippets[11];
1585
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1586
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1587
+ expect(targetSnippet.originalCode).toBe(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1588
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1589
+ expect(targetSnippet.literals).toEqual([
1590
+ '9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1591
+ ]);
1592
+ expect(targetSnippet.isChanged).toBe(true);
1593
+ }
1594
+ });
1595
+
1596
+ // 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
1597
+ test('should handle special characters in string expression 7', () => {
1598
+ const code = readFileSync("test/TestSpecialString.cs", "utf8");
1599
+ const snippets = extractor.extractStrings(code);
1600
+ {
1601
+ let targetSnippet = snippets[12];
1602
+ // 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
1603
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1604
+ expect(targetSnippet.originalCode).toBe(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1605
+ // 需要统一匹配 `return` 语句中的字符串表达式
1606
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1607
+ expect(targetSnippet.literals).toEqual([
1608
+ '10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1609
+ ]);
1610
+ expect(targetSnippet.isChanged).toBe(true);
1611
+ }
1612
+ });
1613
+
1614
+ test('should handle optional value assignment', () => {
1615
+ const code = `m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";`;
1616
+ const snippets = extractor.extractStrings(code);
1617
+ {
1618
+ let targetSnippet = snippets[0];
1619
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
1620
+ expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
1621
+ expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
1622
+ expect(targetSnippet.literals).toEqual([
1623
+ ''
1624
+ ]);
1625
+ expect(targetSnippet.isChanged).toBe(true);
1626
+ }
1627
+ });
1628
+
1629
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1630
+ test('should handle string in comment 1', () => {
1631
+ const code = `
1632
+ protected override void OnShow(object param)
1633
+ {
1634
+ // Log.Info("AAA:ChooseSutraLayer显示1");
1635
+ base.OnShow(param);
1636
+ }`;
1637
+ const snippets = extractor.extractStrings(code);
1638
+ {
1639
+ let targetSnippet = snippets[0];
1640
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"AAA:ChooseSutraLayer显示1"`));
1641
+ expect(targetSnippet.originalCode).toBe(`"AAA:ChooseSutraLayer显示1"`);
1642
+ expect(targetSnippet.convertedCode).toBe(`"AAA:ChooseSutraLayer显示1"`);
1643
+ expect(targetSnippet.literals).toEqual([
1644
+ 'AAA:ChooseSutraLayer显示1'
1645
+ ]);
1646
+ expect(targetSnippet.isChanged).toBe(false);
1647
+ }
1648
+ });
1649
+
1650
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1651
+ test('should handle string in comment 2', () => {
1652
+ const code = `
1653
+ protected override void OnShow(object param)
1654
+ {
1655
+ // Log.Info($"BBB:Jowikwf");
1656
+ base.OnShow(param);
1657
+ }`;
1658
+ const snippets = extractor.extractStrings(code);
1659
+ {
1660
+ let targetSnippet = snippets[0];
1661
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"BBB:Jowikwf"`));
1662
+ expect(targetSnippet.originalCode).toBe(`$"BBB:Jowikwf"`);
1663
+ expect(targetSnippet.convertedCode).toBe(`$"BBB:Jowikwf"`);
1664
+ expect(targetSnippet.literals).toEqual([
1665
+ 'BBB:Jowikwf'
1666
+ ]);
1667
+ expect(targetSnippet.isChanged).toBe(false);
1668
+ }
1669
+ });
1670
+
1671
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1672
+ test('should handle string in comment 3', () => {
1673
+ const code = `
1674
+ protected override void OnShow(object param)
1675
+ {
1676
+ // Log.Info($"BBB:{wlek}Jowikwf");
1677
+ base.OnShow(param);
1678
+ }`;
1679
+ const snippets = extractor.extractStrings(code);
1680
+ {
1681
+ let targetSnippet = snippets[0];
1682
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"BBB:{wlek}Jowikwf"`));
1683
+ expect(targetSnippet.originalCode).toBe(`$"BBB:{wlek}Jowikwf"`);
1684
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("BBB:{0}Jowikwf", wlek)`);
1685
+ expect(targetSnippet.literals).toEqual([
1686
+ 'BBB:{0}Jowikwf'
1687
+ ]);
1688
+ expect(targetSnippet.isChanged).toBe(true);
1689
+ }
1690
+ });
1691
+
1692
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1693
+ test('should handle string in comment 4', () => {
1694
+ const code = `
1695
+ protected override void OnShow(object param)
1696
+ {
1697
+ // Log.Info("{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\");
1698
+ base.OnShow(param);
1699
+ }`;
1700
+ const snippets = extractor.extractStrings(code);
1701
+ {
1702
+ let targetSnippet = snippets[0];
1703
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1704
+ expect(targetSnippet.originalCode).toBe(`"{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1705
+ expect(targetSnippet.convertedCode).toBe(`"{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1706
+ expect(targetSnippet.literals).toEqual([
1707
+ '{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1708
+ ]);
1709
+ expect(targetSnippet.isChanged).toBe(false);
1710
+ }
1711
+ });
1712
+
1713
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1714
+ test('should handle string in comment 5', () => {
1715
+ const code = `
1716
+ protected override void OnShow(object param)
1717
+ {
1718
+ // Log.Info($"{wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\");
1719
+ base.OnShow(param);
1720
+ }`;
1721
+ const snippets = extractor.extractStrings(code);
1722
+ {
1723
+ let targetSnippet = snippets[0];
1724
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"{wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
1725
+ expect(targetSnippet.originalCode).toBe(`$"{wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
1726
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
1727
+ expect(targetSnippet.literals).toEqual([
1728
+ '{0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
1729
+ ]);
1730
+ expect(targetSnippet.isChanged).toBe(true);
1731
+ }
1732
+ });
1733
+
1734
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1735
+ test('should handle complex string 1', () => {
1736
+ const code = `
1737
+ infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";
1738
+ infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
1739
+ `;
1740
+ const snippets = extractor.extractStrings(code);
1741
+ {
1742
+ let targetSnippet = snippets[0];
1743
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`));
1744
+ expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
1745
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1746
+ expect(targetSnippet.literals).toEqual([
1747
+ '伤害类型: [color={0}][b]{1}[/b][/color]\n'
1748
+ ]);
1749
+ expect(targetSnippet.isChanged).toBe(true);
1750
+ }
1751
+ });
1752
+
1753
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1754
+ test('should handle complex string 2', () => {
1755
+ const code = `
1756
+ infoStr += Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType);
1757
+ infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
1758
+ `;
1759
+ const snippets = extractor.extractStrings(code);
1760
+ {
1761
+ let targetSnippet = snippets[0];
1762
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`));
1763
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1764
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
1765
+ expect(targetSnippet.literals).toEqual([
1766
+ '伤害类型: [color={0}][b]{1}[/b][/color]\n'
1767
+ ]);
1768
+ expect(targetSnippet.isChanged).toBe(false);
1769
+ }
1770
+ });
1771
+
1772
+ // 正确处理C# 字符串表达式中递归内嵌字符串表达式的情况: 需要正确转换内插字符串格式
1773
+ test('should handle fix interpolated string nested literals', () => {
1774
+ const code = `infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";`;
1775
+ const snippets = extractor.extractStrings(code);
1776
+ {
1777
+ let targetSnippet = snippets[0];
1778
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`));
1779
+ expect(targetSnippet.originalCode).toBe(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`);
1780
+ // 需要正确转换内插字符串格式
1781
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("是否暴击: [b]{0}[/b]\n", (_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否"))`);
1782
+ expect(targetSnippet.literals).toEqual([
1783
+ '是否暴击: [b]{0}[/b]\n'
1784
+ ]);
1785
+ expect(targetSnippet.isChanged).toBe(true);
1786
+ }
1787
+ });
1788
+
1789
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1790
+ test('should handle complex string with optional value 3', () => {
1791
+ const code = `
1792
+ else if (battleModel.CurGameType == BattleModel.GameType.Teamwork)
1793
+ {
1794
+ //合作模式显示队友名称
1795
+ m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";
1796
+ }
1797
+ `;
1798
+ const snippets = extractor.extractStrings(code);
1799
+ {
1800
+ let targetSnippet = snippets[0];
1801
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
1802
+ expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
1803
+ expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
1804
+ expect(targetSnippet.literals).toEqual([
1805
+ ''
1806
+ ]);
1807
+ expect(targetSnippet.isChanged).toBe(true);
1808
+ }
1809
+ });
1810
+
1811
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1812
+ test('should handle complex string 4', () => {
1813
+ const code = `
1814
+ private void OnBtnIaa(EventContext context)
1815
+ {
1816
+ var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
1817
+ if (iaaConfig == null)
1818
+ {
1819
+ return;
1820
+ }
1821
+
1822
+ string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
1823
+ this.GetModel<IAAModel>().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
1824
+ }
1825
+ `;
1826
+ const snippets = extractor.extractStrings(code);
1827
+ {
1828
+ let targetSnippet = snippets[0];
1829
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`));
1830
+ expect(targetSnippet.originalCode).toBe(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`);
1831
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("观看视频后\n等待时间减少{0}分钟", iaaConfig.EffectValue / 60)`);
1832
+ expect(targetSnippet.literals).toEqual([
1833
+ '观看视频后\n等待时间减少{0}分钟'
1834
+ ]);
1835
+ expect(targetSnippet.isChanged).toBe(true);
1836
+ }
1837
+ });
1838
+
1839
+ // 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
1840
+ test('should handle strings in function call arguments', () => {
1841
+ const code = `
1842
+ private void OnBtnIaa(EventContext context)
1843
+ {
1844
+ var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
1845
+ if (iaaConfig == null)
1846
+ {
1847
+ return;
1848
+ }
1849
+
1850
+ string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
1851
+ this.GetModel<IAAModel>().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
1852
+ }
1853
+ `;
1854
+ const snippets = extractor.extractStrings(code);
1855
+ {
1856
+ let targetSnippet = snippets[1];
1857
+ // 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
1858
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"加速时间"`));
1859
+ expect(targetSnippet.originalCode).toBe(`"加速时间"`);
1860
+ expect(targetSnippet.convertedCode).toBe(`"加速时间"`);
1861
+ expect(targetSnippet.literals).toEqual([
1862
+ '加速时间'
1863
+ ]);
1864
+ expect(targetSnippet.isChanged).toBe(false);
1865
+ }
1866
+ });
1867
+
1868
+ // 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
1869
+ test('should handle string in comment 5', () => {
1870
+ const code = `
1871
+ m_text_percent.text = $"{curGoodsConfig.RebateRate}%";
1872
+ m_name_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Name);
1873
+ m_thumbnail_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Thumbnail);
1874
+ m_btn_buy.text = $"¥ {curGoodsConfig.Price}";
1875
+ m_text_timeleft.text = curShop.TimeLimitData.RemainTime().TR() + "后消失".TR();
1876
+ `;
1877
+ const snippets = extractor.extractStrings(code);
1878
+ {
1879
+ let targetSnippet = snippets[2];
1880
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`));
1881
+ expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
1882
+ expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
1883
+ expect(targetSnippet.literals).toEqual([
1884
+ '后消失'
1885
+ ]);
1886
+ expect(targetSnippet.isChanged).toBe(false);
1887
+ }
1888
+ });
1889
+
1890
+ // 正确处理C# `switch` 表达式形式中的字符串表达式
1891
+ test('should handle switch expression string', () => {
1892
+ const code = `
1893
+ case (int)State.Lock:
1894
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
1895
+ break;
1896
+ `;
1897
+ const snippets = extractor.extractStrings(code);
1898
+ {
1899
+ let targetSnippet = snippets[0];
1900
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
1901
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
1902
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
1903
+ expect(targetSnippet.literals).toEqual([
1904
+ '灵田{0}级解锁'
1905
+ ]);
1906
+ expect(targetSnippet.isChanged).toBe(true);
1907
+ }
1908
+ });
1909
+
1910
+ // 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
1911
+ test('should handle comment boundary 1', () => {
1912
+ const code = `
1913
+ //上锁
1914
+ case (int)State.Lock:
1915
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
1916
+ break;
1917
+ `;
1918
+ const snippets = extractor.extractStrings(code);
1919
+ {
1920
+ let targetSnippet = snippets[0];
1921
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
1922
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
1923
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
1924
+ expect(targetSnippet.literals).toEqual([
1925
+ '灵田{0}级解锁'
1926
+ ]);
1927
+ expect(targetSnippet.isChanged).toBe(true);
1928
+ }
1929
+ });
1930
+
1931
+ // 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
1932
+ test('should handle comment boundary 2', () => {
1933
+ const code = `
1934
+ // klwjfe
1935
+ namespace TestNamespace{
1936
+ // lkwjfe
1937
+ public class FK{
1938
+ // klwf
1939
+ public void TestM(){
1940
+ // 模拟状态变量
1941
+ var _state = (int)State.Lock;
1942
+ // 模拟土地ID变量
1943
+ switch (_state){
1944
+ //上锁
1945
+ case (int)State.Lock:
1946
+ // lkwjefl
1947
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
1948
+ // klwejf
1949
+ break;
1950
+ // lkwjf
1951
+ }
1952
+ // klwjfel
1953
+ }
1954
+ }
1955
+ }
1956
+ `;
1957
+ const snippets = extractor.extractStrings(code);
1958
+ {
1959
+ let targetSnippet = snippets[0];
1960
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
1961
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
1962
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
1963
+ expect(targetSnippet.literals).toEqual([
1964
+ '灵田{0}级解锁'
1965
+ ]);
1966
+ expect(targetSnippet.isChanged).toBe(true);
1967
+ }
1968
+ });
1969
+ // 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
1970
+ test('should handle comment boundary 2', () => {
1971
+ const code = `
1972
+ // klwjfe
1973
+ namespace TestNamespace{
1974
+ // lkwjfe
1975
+ public class FK{
1976
+ // wlkjelj
1977
+ private int Llkwe = 23;
1978
+ // lkjlkj
1979
+ private int Dwekl { get; set; } = 23;
1980
+ // jklwef
1981
+ private static int Dwekl { get; set; } = 23;
1982
+ // lkwjeflk
1983
+ public static void TestM(){}
1984
+ // wesd
1985
+ public static void TestM()
1986
+ {
1987
+ }
1988
+ // wesd
1989
+ public static void TestM(){
1990
+ }
1991
+ // klwf
1992
+ public void TestM(){
1993
+ // 模拟状态变量
1994
+ var _state = (int)State.Lock;
1995
+ // 模拟土地ID变量
1996
+ switch (_state){
1997
+ //上锁
1998
+ case (int)State.Lock:
1999
+ // lkwjefl
2000
+ Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
2001
+ // klwejf
2002
+ break;
2003
+ // lkwjf
2004
+ }
2005
+ // klwjfel
2006
+ }
2007
+ }
2008
+ }
2009
+ `;
2010
+ const snippets = extractor.extractStrings(code);
2011
+ {
2012
+ let targetSnippet = snippets[0];
2013
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
2014
+ expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
2015
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
2016
+ expect(targetSnippet.literals).toEqual([
2017
+ '灵田{0}级解锁'
2018
+ ]);
2019
+ expect(targetSnippet.isChanged).toBe(true);
2020
+ }
2021
+ });
2022
+
2023
+ // 正确识别C#文档注释边界, 类、方法、类成员的文档注释需要直接忽略捕获, 不参与字符串表达式提取; 文档注释通常位于类、方法、类成员的定义上方, 文档注释的格式形如: ` /// <xxx> yyy\n /// </xxx>` 或 ` /// <xxx yyy/>`, 其中xxx为标签, yyy为注释内容, `<xxx>`和`</xxx>` 不一定在同一行或不同行, ` /// <xxx> yyy\n` 不一定只有一行; 文档注释通常以`<xxx>`标签开始, 以`</xxx>`标签结束, 或者以 `<xxx ` 开始, 以 `/>` 结束。
2024
+ test('should handle doc comment boundary for class/method/members', () => {
2025
+ const code = `
2026
+ /// <summary>
2027
+ /// "fwef 类注释1"
2028
+ /// $"fwef 类注释2"
2029
+ /// $"fwef 类注释2 {wef}"
2030
+ /// wef $"fwef 类注释2 {wef}" wef
2031
+ /// </summary>
2032
+ /// <sumary "lkwjfelj"/>
2033
+ public partial class TimeLimitedChargeDialog
2034
+ {
2035
+ /// <summary>
2036
+ /// $" Property成员注释 dsvdf {wlpoe}"
2037
+ /// </summary>
2038
+ private int Ljfw { get; set; } = 342;
2039
+
2040
+ /// <summary>
2041
+ /// $" Field成员注释 wfewe {vvwe}"
2042
+ /// $" Field成员注释 wfewe {vvwe}" wfe
2043
+ /// </summary>
2044
+ private int Ljfw = 342;
2045
+
2046
+ /// <summary>
2047
+ ///</summary>
2048
+ private int Ljfw = 342;
2049
+
2050
+ /// <summary>
2051
+ /// " wef方法注释 wef"
2052
+ /// $" wef方法注释 wef"
2053
+ /// $" wef方法注释 wef{wef}"
2054
+ /// wfe $" wef方法注释 wef{wef}"
2055
+ /// wfe $" wef方法注释 wef{wef}" wef
2056
+ ///wfe $" wef方法注释 wef{wef}" wef
2057
+ /// wefw $" wef方法注释 wef{wef}" we
2058
+ /// </summary>
2059
+ /// <sumary wf $"klwf"/>
2060
+ /// <param name="force">是否重新创建页签列表</param>
2061
+ private void RefreshGoodsInfo()
2062
+ {
2063
+ m_text_timeleft.text = curShop.TimeLimitData.RemainTime() + "后消失";
2064
+ }
2065
+ }
2066
+ `;
2067
+ const snippets = extractor.extractStrings(code);
2068
+ {
2069
+ let targetSnippet = snippets[0];
2070
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2071
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime() + "后消失"`));
2072
+ expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime() + "后消失"`);
2073
+ expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
2074
+ expect(targetSnippet.literals).toEqual([
2075
+ '后消失'
2076
+ ]);
2077
+ expect(targetSnippet.isChanged).toBe(true);
2078
+ }
2079
+ });
2080
+
2081
+ // 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
2082
+ test('should handle string expression in method call parameter 1', () => {
2083
+ const code = `
2084
+ namespace FaBao.UI.Comp
2085
+ {
2086
+ public partial class ItemCommonComp
2087
+ {
2088
+ public void BindData(bool needCompare = false)
2089
+ {
2090
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2091
+ }
2092
+ }
2093
+ }
2094
+ `;
2095
+ const snippets = extractor.extractStrings(code);
2096
+ {
2097
+ let targetSnippet = snippets[0];
2098
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2099
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2100
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2101
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2102
+ expect(targetSnippet.literals).toEqual([
2103
+ '{0}阶'
2104
+ ]);
2105
+ expect(targetSnippet.isChanged).toBe(false);
2106
+ }
2107
+ expect(snippets.length).toBe(1);
2108
+ });
2109
+
2110
+ // 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
2111
+ test('should handle string expression in method call parameter 2', () => {
2112
+ const code = `
2113
+ namespace FaBao.UI.Comp
2114
+ {
2115
+ public partial class ItemCommonComp
2116
+ {
2117
+ public void BindData(bool needCompare = false, long needCompare2 = 23, float needCompare2 = 324.0)
2118
+ {
2119
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2120
+ }
2121
+ }
2122
+ }
2123
+ `;
2124
+ const snippets = extractor.extractStrings(code);
2125
+ {
2126
+ let targetSnippet = snippets[0];
2127
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2128
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2129
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2130
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2131
+ expect(targetSnippet.literals).toEqual([
2132
+ '{0}阶'
2133
+ ]);
2134
+ expect(targetSnippet.isChanged).toBe(false);
2135
+ }
2136
+ expect(snippets.length).toBe(1);
2137
+ });
2138
+
2139
+ // 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
2140
+ test('should handle string expression in method call parameter 3', () => {
2141
+ const code = `
2142
+ namespace FaBao.UI.Comp
2143
+ {
2144
+ public partial class ItemCommonComp
2145
+ {
2146
+ public void BindData(string wkle, bool needCompare = false, int needCompare2 = false)
2147
+ {
2148
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2149
+ }
2150
+ }
2151
+ }
2152
+ `;
2153
+ const snippets = extractor.extractStrings(code);
2154
+ {
2155
+ let targetSnippet = snippets[0];
2156
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2157
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2158
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2159
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2160
+ expect(targetSnippet.literals).toEqual([
2161
+ '{0}阶'
2162
+ ]);
2163
+ expect(targetSnippet.isChanged).toBe(false);
2164
+ }
2165
+ expect(snippets.length).toBe(1);
2166
+ });
2167
+
2168
+ // 正确提取C#方法参数中的字符串表达式信息, 支持1到多个形参有默认值的情形
2169
+ test('should handle string expression in method call parameter 3', () => {
2170
+ const code = `
2171
+ namespace FaBao.UI.Comp
2172
+ {
2173
+ public partial class ItemCommonComp
2174
+ {
2175
+ public void BindData(string wkle = "11111", bool needCompare = false, int needCompare2 = false)
2176
+ {
2177
+ }
2178
+
2179
+ public void BindData(string wkle = "22", string wkle = "werwer", bool needCompare = false, string wkle = "wefwewef31f", int needCompare2 = false, string wkle = "df223")
2180
+ {
2181
+ }
2182
+
2183
+ public void BindData(string wkle = "(*&%\\\\*(@Fwfe))&(")
2184
+ {
2185
+ }
2186
+
2187
+ public void BindData(string we, string wkle = "aaaaa", bool needCompare = false, int needCompare2 = false)
2188
+ {
2189
+ m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
2190
+ }
2191
+ }
2192
+ }
2193
+ `;
2194
+ const snippets = extractor.extractStrings(code);
2195
+ {
2196
+ let targetSnippet = snippets[0];
2197
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2198
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"11111"`));
2199
+ expect(targetSnippet.originalCode).toBe(`"11111"`);
2200
+ expect(targetSnippet.convertedCode).toBe(`"11111"`);
2201
+ expect(targetSnippet.literals).toEqual([
2202
+ '11111'
2203
+ ]);
2204
+ expect(targetSnippet.isChanged).toBe(false);
2205
+ }
2206
+ {
2207
+ let targetSnippet = snippets[1];
2208
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2209
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"22"`));
2210
+ expect(targetSnippet.originalCode).toBe(`"22"`);
2211
+ expect(targetSnippet.convertedCode).toBe(`"22"`);
2212
+ expect(targetSnippet.literals).toEqual([
2213
+ '22'
2214
+ ]);
2215
+ expect(targetSnippet.isChanged).toBe(false);
2216
+ }
2217
+ {
2218
+ let targetSnippet = snippets[2];
2219
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2220
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"werwer"`));
2221
+ expect(targetSnippet.originalCode).toBe(`"werwer"`);
2222
+ expect(targetSnippet.convertedCode).toBe(`"werwer"`);
2223
+ expect(targetSnippet.literals).toEqual([
2224
+ 'werwer'
2225
+ ]);
2226
+ expect(targetSnippet.isChanged).toBe(false);
2227
+ }
2228
+ {
2229
+ let targetSnippet = snippets[3];
2230
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2231
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"wefwewef31f"`));
2232
+ expect(targetSnippet.originalCode).toBe(`"wefwewef31f"`);
2233
+ expect(targetSnippet.convertedCode).toBe(`"wefwewef31f"`);
2234
+ expect(targetSnippet.literals).toEqual([
2235
+ 'wefwewef31f'
2236
+ ]);
2237
+ expect(targetSnippet.isChanged).toBe(false);
2238
+ }
2239
+ {
2240
+ let targetSnippet = snippets[4];
2241
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2242
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"df223"`));
2243
+ expect(targetSnippet.originalCode).toBe(`"df223"`);
2244
+ expect(targetSnippet.convertedCode).toBe(`"df223"`);
2245
+ expect(targetSnippet.literals).toEqual([
2246
+ 'df223'
2247
+ ]);
2248
+ expect(targetSnippet.isChanged).toBe(false);
2249
+ }
2250
+ {
2251
+ let targetSnippet = snippets[5];
2252
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2253
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"(*&%\\\\*(@Fwfe))&("`));
2254
+ expect(targetSnippet.originalCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
2255
+ expect(targetSnippet.convertedCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
2256
+ expect(targetSnippet.literals).toEqual([
2257
+ '(*&%\\\\*(@Fwfe))&('
2258
+ ]);
2259
+ expect(targetSnippet.isChanged).toBe(false);
2260
+ }
2261
+ {
2262
+ let targetSnippet = snippets[6];
2263
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2264
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"aaaaa"`));
2265
+ expect(targetSnippet.originalCode).toBe(`"aaaaa"`);
2266
+ expect(targetSnippet.convertedCode).toBe(`"aaaaa"`);
2267
+ expect(targetSnippet.literals).toEqual([
2268
+ 'aaaaa'
2269
+ ]);
2270
+ expect(targetSnippet.isChanged).toBe(false);
2271
+ }
2272
+ {
2273
+ let targetSnippet = snippets[7];
2274
+ // 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
2275
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
2276
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2277
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
2278
+ expect(targetSnippet.literals).toEqual([
2279
+ '{0}阶'
2280
+ ]);
2281
+ expect(targetSnippet.isChanged).toBe(false);
2282
+ }
2283
+ });
2284
+
2285
+ // 需要正确识别C#代码中 `if` 语句边界 和 创建对象表达式的边界, 识别 `if` 语句前、中、后部分中的字符串表达式
2286
+ test('should handle if border and object creation expression border', () => {
2287
+ const code = `
2288
+ namespace FaBao.UI.MainUI
2289
+ {
2290
+ public partial class SpellUpgradeDialog
2291
+ {
2292
+ private async void OnBtnUpgrade()
2293
+ {
2294
+ // new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id) 被方法调用传参的 () 包裹, 因此已经构成了完整的对象创建表达式
2295
+ if (await this.GetModel<SpellCardModel>().DispatchAction(new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id)))
2296
+ {
2297
+ //升级成功后刷新界面
2298
+ m_text_level.text = Tr.Format("{0}级", _symbolCard.Level);
2299
+ }
2300
+ }
2301
+ }
2302
+ }
2303
+ `;
2304
+ const snippets = extractor.extractStrings(code);
2305
+ {
2306
+ let targetSnippet = snippets[0];
2307
+ // 需要正确识别 `if` 语句边界, 识别 `if` 语句前、中、后部分中的字符串表达式
2308
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}级", _symbolCard.Level)`));
2309
+ expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
2310
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
2311
+ expect(targetSnippet.literals).toEqual([
2312
+ '{0}级'
2313
+ ]);
2314
+ expect(targetSnippet.isChanged).toBe(false);
2315
+ }
2316
+ expect(snippets.length).toBe(1);
2317
+ });
2318
+
2319
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2320
+ test('should handle class member initialization assignment with anonymous function 1', () => {
2321
+ const code = `
2322
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2323
+ {
2324
+ Title = "提示",
2325
+ ConfirmCallback = async () =>
2326
+ {
2327
+ //灵纹一键下阵
2328
+ if (await this.GetModel<CardModel>().DispatchAction(new Rune_Action_CleanRune(_sutraCardData.Card.Id)))
2329
+ {
2330
+ Toast.Info("卸载成功");
2331
+ }
2332
+ },
2333
+ CancelText = "取消",
2334
+ }).Forget();
2335
+ `;
2336
+ const snippets = extractor.extractStrings(code);
2337
+ {
2338
+ let targetSnippet = snippets[0];
2339
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"提示"`));
2340
+ expect(targetSnippet.originalCode).toBe(`"提示"`);
2341
+ expect(targetSnippet.convertedCode).toBe(`"提示"`);
2342
+ expect(targetSnippet.literals).toEqual([
2343
+ '提示'
2344
+ ]);
2345
+ expect(targetSnippet.isChanged).toBe(false);
2346
+ }
2347
+ {
2348
+ let targetSnippet = snippets[1];
2349
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2350
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2351
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2352
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2353
+ expect(targetSnippet.literals).toEqual([
2354
+ '卸载成功'
2355
+ ]);
2356
+ expect(targetSnippet.isChanged).toBe(false);
2357
+ }
2358
+ {
2359
+ let targetSnippet = snippets[2];
2360
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"取消"`));
2361
+ expect(targetSnippet.originalCode).toBe(`"取消"`);
2362
+ expect(targetSnippet.convertedCode).toBe(`"取消"`);
2363
+ expect(targetSnippet.literals).toEqual([
2364
+ '取消'
2365
+ ]);
2366
+ expect(targetSnippet.isChanged).toBe(false);
2367
+ }
2368
+ });
2369
+
2370
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2371
+ test('should handle class member initialization assignment with anonymous function 2', () => {
2372
+ const code = `
2373
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2374
+ {
2375
+ ConfirmCallback = async () =>
2376
+ {
2377
+ Toast.Info("卸载成功");
2378
+ },
2379
+ }).Forget();
2380
+ `;
2381
+ const snippets = extractor.extractStrings(code);
2382
+ {
2383
+ let targetSnippet = snippets[0];
2384
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2385
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2386
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2387
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2388
+ expect(targetSnippet.literals).toEqual([
2389
+ '卸载成功'
2390
+ ]);
2391
+ expect(targetSnippet.isChanged).toBe(false);
2392
+ }
2393
+ });
2394
+
2395
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2396
+ test('should handle class member initialization assignment with anonymous function 2', () => {
2397
+ const code = `
2398
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2399
+ {
2400
+ ConfirmCallback = (int qwfewe) =>
2401
+ {
2402
+ Toast.Info($"卸载成功: {qwfewe}");
2403
+ },
2404
+ }).Forget();
2405
+ `;
2406
+ const snippets = extractor.extractStrings(code);
2407
+ {
2408
+ let targetSnippet = snippets[0];
2409
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2410
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"卸载成功: {qwfewe}"`));
2411
+ expect(targetSnippet.originalCode).toBe(`$"卸载成功: {qwfewe}"`);
2412
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("卸载成功: {0}", qwfewe)`);
2413
+ expect(targetSnippet.literals).toEqual([
2414
+ '卸载成功: {0}'
2415
+ ]);
2416
+ expect(targetSnippet.isChanged).toBe(true);
2417
+ }
2418
+ });
2419
+
2420
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2421
+ test('should handle class member initialization assignment with anonymous function 3', () => {
2422
+ const code = `
2423
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2424
+ {
2425
+ ConfirmCallback = async () => Toast.Info("卸载成功"),
2426
+ }).Forget();
2427
+ `;
2428
+ const snippets = extractor.extractStrings(code);
2429
+ {
2430
+ let targetSnippet = snippets[0];
2431
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2432
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2433
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2434
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2435
+ expect(targetSnippet.literals).toEqual([
2436
+ '卸载成功'
2437
+ ]);
2438
+ expect(targetSnippet.isChanged).toBe(false);
2439
+ }
2440
+ });
2441
+
2442
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2443
+ test('should handle class member initialization assignment with anonymous function 4', () => {
2444
+ const code = `
2445
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2446
+ {
2447
+ ConfirmCallback = () => Toast.Info("卸载成功"),
2448
+ }).Forget();
2449
+ `;
2450
+ const snippets = extractor.extractStrings(code);
2451
+ {
2452
+ let targetSnippet = snippets[0];
2453
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2454
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2455
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2456
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2457
+ expect(targetSnippet.literals).toEqual([
2458
+ '卸载成功'
2459
+ ]);
2460
+ expect(targetSnippet.isChanged).toBe(false);
2461
+ }
2462
+ });
2463
+
2464
+ // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2465
+ test('should handle .text = format 1', () => {
2466
+ const code = `m_text_iwff.text = info ;`;
2467
+ const snippets = extractor.extractStrings(code);
2468
+ {
2469
+ let targetSnippet = snippets[0];
2470
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`info`));
2471
+ expect(targetSnippet.originalCode).toBe(`info`);
2472
+ expect(targetSnippet.convertedCode).toBe(`info.TR()`);
2473
+ expect(targetSnippet.literals).toEqual([
2474
+ ]);
2475
+ expect(targetSnippet.isChanged).toBe(true);
2476
+ }
2477
+ });
2478
+
2479
+ // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2480
+ test('should handle .text = format 1', () => {
2481
+ const code = `m_text_info.text = info1.member1 + info2.member2;`;
2482
+ const snippets = extractor.extractStrings(code);
2483
+ {
2484
+ let targetSnippet = snippets[0];
2485
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`info1.member1 + info2.member2`));
2486
+ expect(targetSnippet.originalCode).toBe(`info1.member1 + info2.member2`);
2487
+ expect(targetSnippet.convertedCode).toBe(`info1.member1.TR() + info2.member2.TR()`);
2488
+ expect(targetSnippet.literals).toEqual([
2489
+ ]);
2490
+ expect(targetSnippet.isChanged).toBe(true);
2491
+ }
2492
+ });
2493
+
1029
2494
  });