@fisharmy100/auto-i18n-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/langs.js ADDED
@@ -0,0 +1,464 @@
1
+ export const LANGUAGE_CODES = [
2
+ 'ace_Arab',
3
+ 'ace_Latn',
4
+ 'acm_Arab',
5
+ 'acq_Arab',
6
+ 'aeb_Arab',
7
+ 'afr_Latn',
8
+ 'als_Latn',
9
+ 'amh_Ethi',
10
+ 'apc_Arab',
11
+ 'arb_Arab',
12
+ 'arb_Latn',
13
+ 'arg_Latn',
14
+ 'ars_Arab',
15
+ 'ary_Arab',
16
+ 'arz_Arab',
17
+ 'asm_Beng',
18
+ 'ast_Latn',
19
+ 'awa_Deva',
20
+ 'ayr_Latn',
21
+ 'azb_Arab',
22
+ 'azj_Latn',
23
+ 'bak_Cyrl',
24
+ 'bam_Latn',
25
+ 'ban_Latn',
26
+ 'bel_Cyrl',
27
+ 'bem_Latn',
28
+ 'ben_Beng',
29
+ 'bho_Deva',
30
+ 'bjn_Arab',
31
+ 'bjn_Latn',
32
+ 'bod_Tibt',
33
+ 'bos_Latn',
34
+ 'brx_Deva',
35
+ 'bug_Latn',
36
+ 'bul_Cyrl',
37
+ 'cat_Latn',
38
+ 'ceb_Latn',
39
+ 'ces_Latn',
40
+ 'chv_Cyrl',
41
+ 'cjk_Latn',
42
+ 'ckb_Arab',
43
+ 'cmn_Hans',
44
+ 'cmn_Hant',
45
+ 'crh_Latn',
46
+ 'cym_Latn',
47
+ 'dan_Latn',
48
+ 'dar_Cyrl',
49
+ 'deu_Latn',
50
+ 'dgo_Deva',
51
+ 'dik_Latn',
52
+ 'dyu_Latn',
53
+ 'dzo_Tibt',
54
+ 'ekk_Latn',
55
+ 'ell_Grek',
56
+ 'eng_Latn',
57
+ 'epo_Latn',
58
+ 'eus_Latn',
59
+ 'ewe_Latn',
60
+ 'fao_Latn',
61
+ 'fij_Latn',
62
+ 'fil_Latn',
63
+ 'fin_Latn',
64
+ 'fon_Latn',
65
+ 'fra_Latn',
66
+ 'fur_Latn',
67
+ 'fuv_Latn',
68
+ 'gaz_Latn',
69
+ 'gla_Latn',
70
+ 'gle_Latn',
71
+ 'glg_Latn',
72
+ 'gom_Deva',
73
+ 'gug_Latn',
74
+ 'guj_Gujr',
75
+ 'hat_Latn',
76
+ 'hau_Latn',
77
+ 'heb_Hebr',
78
+ 'hin_Deva',
79
+ 'hne_Deva',
80
+ 'hrv_Latn',
81
+ 'hun_Latn',
82
+ 'hye_Armn',
83
+ 'ibo_Latn',
84
+ 'ilo_Latn',
85
+ 'ind_Latn',
86
+ 'isl_Latn',
87
+ 'ita_Latn',
88
+ 'jav_Latn',
89
+ 'jpn_Jpan',
90
+ 'kaa_Latn',
91
+ 'kab_Latn',
92
+ 'kac_Latn',
93
+ 'kam_Latn',
94
+ 'kan_Knda',
95
+ 'kas_Arab',
96
+ 'kas_Deva',
97
+ 'kat_Geor',
98
+ 'kaz_Cyrl',
99
+ 'kbp_Latn',
100
+ 'kea_Latn',
101
+ 'khk_Cyrl',
102
+ 'khm_Khmr',
103
+ 'kik_Latn',
104
+ 'kin_Latn',
105
+ 'kir_Cyrl',
106
+ 'kmb_Latn',
107
+ 'kmr_Latn',
108
+ 'knc_Arab',
109
+ 'knc_Latn',
110
+ 'kor_Hang',
111
+ 'ktu_Latn',
112
+ 'lao_Laoo',
113
+ 'lij_Latn',
114
+ 'lim_Latn',
115
+ 'lin_Latn',
116
+ 'lit_Latn',
117
+ 'lld_Latn',
118
+ 'lmo_Latn',
119
+ 'ltg_Latn',
120
+ 'ltz_Latn',
121
+ 'lua_Latn',
122
+ 'lug_Latn',
123
+ 'luo_Latn',
124
+ 'lus_Latn',
125
+ 'lvs_Latn',
126
+ 'mag_Deva',
127
+ 'mai_Deva',
128
+ 'mal_Mlym',
129
+ 'mar_Deva',
130
+ 'mfe_Latn',
131
+ 'mhr_Cyrl',
132
+ 'min_Arab',
133
+ 'min_Latn',
134
+ 'mkd_Cyrl',
135
+ 'mlt_Latn',
136
+ 'mni_Beng',
137
+ 'mni_Mtei',
138
+ 'mos_Latn',
139
+ 'mri_Latn',
140
+ 'mya_Mymr',
141
+ 'myv_Cyrl',
142
+ 'nld_Latn',
143
+ 'nno_Latn',
144
+ 'nob_Latn',
145
+ 'npi_Deva',
146
+ 'nqo_Nkoo',
147
+ 'nso_Latn',
148
+ 'nus_Latn',
149
+ 'nya_Latn',
150
+ 'oci_Latn',
151
+ 'ory_Orya',
152
+ 'pag_Latn',
153
+ 'pan_Guru',
154
+ 'pap_Latn',
155
+ 'pbt_Arab',
156
+ 'pes_Arab',
157
+ 'plt_Latn',
158
+ 'pol_Latn',
159
+ 'por_Latn',
160
+ 'prs_Arab',
161
+ 'quy_Latn',
162
+ 'ron_Latn',
163
+ 'run_Latn',
164
+ 'rus_Cyrl',
165
+ 'sag_Latn',
166
+ 'san_Deva',
167
+ 'sat_Olck',
168
+ 'scn_Latn',
169
+ 'shn_Mymr',
170
+ 'sin_Sinh',
171
+ 'slk_Latn',
172
+ 'slv_Latn',
173
+ 'smo_Latn',
174
+ 'sna_Latn',
175
+ 'snd_Arab',
176
+ 'snd_Deva',
177
+ 'som_Latn',
178
+ 'sot_Latn',
179
+ 'spa_Latn',
180
+ 'srd_Latn',
181
+ 'srp_Cyrl',
182
+ 'ssw_Latn',
183
+ 'sun_Latn',
184
+ 'swe_Latn',
185
+ 'swh_Latn',
186
+ 'szl_Latn',
187
+ 'tam_Taml',
188
+ 'taq_Latn',
189
+ 'taq_Tfng',
190
+ 'tat_Cyrl',
191
+ 'tel_Telu',
192
+ 'tgk_Cyrl',
193
+ 'tha_Thai',
194
+ 'tir_Ethi',
195
+ 'tpi_Latn',
196
+ 'tsn_Latn',
197
+ 'tso_Latn',
198
+ 'tuk_Latn',
199
+ 'tum_Latn',
200
+ 'tur_Latn',
201
+ 'twi_Latn',
202
+ 'tyv_Cyrl',
203
+ 'uig_Arab',
204
+ 'ukr_Cyrl',
205
+ 'umb_Latn',
206
+ 'urd_Arab',
207
+ 'uzn_Latn',
208
+ 'uzs_Arab',
209
+ 'vec_Latn',
210
+ 'vie_Latn',
211
+ 'vmw_Latn',
212
+ 'war_Latn',
213
+ 'wol_Latn',
214
+ 'wuu_Hans',
215
+ 'xho_Latn',
216
+ 'ydd_Hebr',
217
+ 'yor_Latn',
218
+ 'yue_Hant',
219
+ 'zgh_Tfng',
220
+ 'zsm_Latn',
221
+ 'zul_Latn',
222
+ ];
223
+ export function stringToLanguageCode(str) {
224
+ if (LANGUAGE_CODES.includes(str)) {
225
+ return str;
226
+ }
227
+ else {
228
+ return null;
229
+ }
230
+ }
231
+ /**
232
+ * Maps NLLB language codes to Azure Translator language codes.
233
+ * Azure uses BCP-47 tags: https://learn.microsoft.com/en-us/azure/ai-services/translator/language-support
234
+ * null = no Azure equivalent or not supported.
235
+ */
236
+ const NLLB_TO_AZURE = {
237
+ ace_Arab: null,
238
+ ace_Latn: null,
239
+ acm_Arab: null,
240
+ acq_Arab: null,
241
+ aeb_Arab: null,
242
+ afr_Latn: 'af',
243
+ als_Latn: 'sq',
244
+ amh_Ethi: 'am',
245
+ apc_Arab: null,
246
+ arb_Arab: 'ar',
247
+ arb_Latn: 'ar',
248
+ arg_Latn: null,
249
+ ars_Arab: 'ar',
250
+ ary_Arab: null,
251
+ arz_Arab: null,
252
+ asm_Beng: 'as',
253
+ ast_Latn: null,
254
+ awa_Deva: null,
255
+ ayr_Latn: null,
256
+ azb_Arab: null,
257
+ azj_Latn: 'az',
258
+ bak_Cyrl: 'ba',
259
+ bam_Latn: null,
260
+ ban_Latn: null,
261
+ bel_Cyrl: null,
262
+ bem_Latn: null,
263
+ ben_Beng: 'bn',
264
+ bho_Deva: 'bho',
265
+ bjn_Arab: null,
266
+ bjn_Latn: null,
267
+ bod_Tibt: 'bo',
268
+ bos_Latn: 'bs',
269
+ brx_Deva: null,
270
+ bug_Latn: null,
271
+ bul_Cyrl: 'bg',
272
+ cat_Latn: 'ca',
273
+ ceb_Latn: 'ceb',
274
+ ces_Latn: 'cs',
275
+ chv_Cyrl: null,
276
+ cjk_Latn: null,
277
+ ckb_Arab: 'ku',
278
+ cmn_Hans: 'zh-Hans',
279
+ cmn_Hant: 'zh-Hant',
280
+ crh_Latn: 'crh',
281
+ cym_Latn: 'cy',
282
+ dan_Latn: 'da',
283
+ dar_Cyrl: null,
284
+ deu_Latn: 'de',
285
+ dgo_Deva: null,
286
+ dik_Latn: null,
287
+ dyu_Latn: null,
288
+ dzo_Tibt: null,
289
+ ekk_Latn: 'et',
290
+ ell_Grek: 'el',
291
+ eng_Latn: 'en',
292
+ epo_Latn: null,
293
+ eus_Latn: 'eu',
294
+ ewe_Latn: null,
295
+ fao_Latn: 'fo',
296
+ fij_Latn: 'fj',
297
+ fil_Latn: 'fil',
298
+ fin_Latn: 'fi',
299
+ fon_Latn: null,
300
+ fra_Latn: 'fr',
301
+ fur_Latn: null,
302
+ fuv_Latn: null,
303
+ gaz_Latn: 'om',
304
+ gla_Latn: 'ga', // Scottish Gaelic → closest is Irish; Azure has 'ga' for Irish but not 'gd' in all regions
305
+ gle_Latn: 'ga',
306
+ glg_Latn: 'gl',
307
+ gom_Deva: null,
308
+ gug_Latn: null,
309
+ guj_Gujr: 'gu',
310
+ hat_Latn: 'ht',
311
+ hau_Latn: 'ha',
312
+ heb_Hebr: 'he',
313
+ hin_Deva: 'hi',
314
+ hne_Deva: null,
315
+ hrv_Latn: 'hr',
316
+ hun_Latn: 'hu',
317
+ hye_Armn: 'hy',
318
+ ibo_Latn: 'ig',
319
+ ilo_Latn: null,
320
+ ind_Latn: 'id',
321
+ isl_Latn: 'is',
322
+ ita_Latn: 'it',
323
+ jav_Latn: 'jv',
324
+ jpn_Jpan: 'ja',
325
+ kaa_Latn: null,
326
+ kab_Latn: null,
327
+ kac_Latn: null,
328
+ kam_Latn: null,
329
+ kan_Knda: 'kn',
330
+ kas_Arab: null,
331
+ kas_Deva: null,
332
+ kat_Geor: 'ka',
333
+ kaz_Cyrl: 'kk',
334
+ kbp_Latn: null,
335
+ kea_Latn: null,
336
+ khk_Cyrl: 'mn-Cyrl',
337
+ khm_Khmr: 'km',
338
+ kik_Latn: null,
339
+ kin_Latn: 'rw',
340
+ kir_Cyrl: 'ky',
341
+ kmb_Latn: null,
342
+ kmr_Latn: 'kmr',
343
+ knc_Arab: null,
344
+ knc_Latn: null,
345
+ kor_Hang: 'ko',
346
+ ktu_Latn: null,
347
+ lao_Laoo: 'lo',
348
+ lij_Latn: null,
349
+ lim_Latn: null,
350
+ lin_Latn: null,
351
+ lit_Latn: 'lt',
352
+ lld_Latn: null,
353
+ lmo_Latn: null,
354
+ ltg_Latn: null,
355
+ ltz_Latn: 'lb',
356
+ lua_Latn: null,
357
+ lug_Latn: null,
358
+ luo_Latn: null,
359
+ lus_Latn: null,
360
+ lvs_Latn: 'lv',
361
+ mag_Deva: null,
362
+ mai_Deva: 'mai',
363
+ mal_Mlym: 'ml',
364
+ mar_Deva: 'mr',
365
+ mfe_Latn: null,
366
+ mhr_Cyrl: null,
367
+ min_Arab: null,
368
+ min_Latn: null,
369
+ mkd_Cyrl: 'mk',
370
+ mlt_Latn: 'mt',
371
+ mni_Beng: 'mni',
372
+ mni_Mtei: null,
373
+ mos_Latn: null,
374
+ mri_Latn: 'mi',
375
+ mya_Mymr: 'my',
376
+ myv_Cyrl: null,
377
+ nld_Latn: 'nl',
378
+ nno_Latn: 'nb', // Azure merges Norwegian Nynorsk/Bokmål under 'nb'
379
+ nob_Latn: 'nb',
380
+ npi_Deva: 'ne',
381
+ nqo_Nkoo: null,
382
+ nso_Latn: null,
383
+ nus_Latn: null,
384
+ nya_Latn: 'ny',
385
+ oci_Latn: null,
386
+ ory_Orya: 'or',
387
+ pag_Latn: null,
388
+ pan_Guru: 'pa',
389
+ pap_Latn: null,
390
+ pbt_Arab: 'ps',
391
+ pes_Arab: 'fa',
392
+ plt_Latn: 'mg',
393
+ pol_Latn: 'pl',
394
+ por_Latn: 'pt',
395
+ prs_Arab: 'prs',
396
+ quy_Latn: null,
397
+ ron_Latn: 'ro',
398
+ run_Latn: null,
399
+ rus_Cyrl: 'ru',
400
+ sag_Latn: null,
401
+ san_Deva: null,
402
+ sat_Olck: null,
403
+ scn_Latn: null,
404
+ shn_Mymr: null,
405
+ sin_Sinh: 'si',
406
+ slk_Latn: 'sk',
407
+ slv_Latn: 'sl',
408
+ smo_Latn: 'sm',
409
+ sna_Latn: null,
410
+ snd_Arab: 'sd',
411
+ snd_Deva: 'sd',
412
+ som_Latn: 'so',
413
+ sot_Latn: 'st',
414
+ spa_Latn: 'es',
415
+ srd_Latn: null,
416
+ srp_Cyrl: 'sr-Cyrl',
417
+ ssw_Latn: null,
418
+ sun_Latn: 'su',
419
+ swe_Latn: 'sv',
420
+ swh_Latn: 'sw',
421
+ szl_Latn: null,
422
+ tam_Taml: 'ta',
423
+ taq_Latn: null,
424
+ taq_Tfng: null,
425
+ tat_Cyrl: 'tt',
426
+ tel_Telu: 'te',
427
+ tgk_Cyrl: 'tg',
428
+ tha_Thai: 'th',
429
+ tir_Ethi: 'ti',
430
+ tpi_Latn: 'to',
431
+ tsn_Latn: 'tn',
432
+ tso_Latn: null,
433
+ tuk_Latn: 'tk',
434
+ tum_Latn: null,
435
+ tur_Latn: 'tr',
436
+ twi_Latn: null,
437
+ tyv_Cyrl: null,
438
+ uig_Arab: 'ug',
439
+ ukr_Cyrl: 'uk',
440
+ umb_Latn: null,
441
+ urd_Arab: 'ur',
442
+ uzn_Latn: 'uz',
443
+ uzs_Arab: null,
444
+ vec_Latn: null,
445
+ vie_Latn: 'vi',
446
+ vmw_Latn: null,
447
+ war_Latn: null,
448
+ wol_Latn: null,
449
+ wuu_Hans: 'wuu',
450
+ xho_Latn: 'xh',
451
+ ydd_Hebr: 'yua', // Yiddish → Azure has 'yua' for Yucatec Maya; true Yiddish is not well-supported
452
+ yor_Latn: 'yo',
453
+ yue_Hant: 'yue',
454
+ zgh_Tfng: null,
455
+ zsm_Latn: 'ms',
456
+ zul_Latn: 'zu',
457
+ };
458
+ /**
459
+ * Converts an NLLB LanguageCode to an Azure Translator BCP-47 language tag.
460
+ * Returns null if there is no Azure equivalent for the given code.
461
+ */
462
+ export function nllbToAzure(code) {
463
+ return NLLB_TO_AZURE[code] ?? null;
464
+ }
@@ -0,0 +1,29 @@
1
+ import ts from "typescript";
2
+ export type FoundTranslation = {
3
+ key: string;
4
+ message: string | string[];
5
+ };
6
+ export type ParsedResult = {
7
+ type: "ok";
8
+ value: Partial<Record<string, FoundTranslation>>;
9
+ } | {
10
+ type: "error";
11
+ value: string[];
12
+ };
13
+ export declare function parseTSFiles(dir: string): Promise<ParsedResult>;
14
+ export declare function formatError(fileName: string, fileContent: string, message: string, start: ts.LineAndCharacter, end: ts.LineAndCharacter): string;
15
+ export declare function getTSFiles(dir: string): Promise<string[]>;
16
+ export type FoundCallsResult = {
17
+ type: "ok";
18
+ translations: {
19
+ translation: FoundTranslation;
20
+ idStart: ts.LineAndCharacter;
21
+ idEnd: ts.LineAndCharacter;
22
+ }[];
23
+ } | {
24
+ type: "error";
25
+ message: string;
26
+ start: ts.LineAndCharacter;
27
+ end: ts.LineAndCharacter;
28
+ };
29
+ export declare function findTCalls(node: ts.Node, file: ts.SourceFile): FoundCallsResult;
package/dist/parser.js ADDED
@@ -0,0 +1,215 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs/promises";
3
+ import ts from "typescript";
4
+ export async function parseTSFiles(dir) {
5
+ const files = await getTSFiles(dir);
6
+ const obj = {};
7
+ const errors = [];
8
+ for (let file of files) {
9
+ const content = await fs.readFile(file, "utf-8");
10
+ const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);
11
+ const results = findTCalls(sourceFile, sourceFile);
12
+ if (results.type === "ok") {
13
+ for (let result of results.translations) {
14
+ if (obj[result.translation.key] !== undefined) {
15
+ errors.push(formatError(file, content, `translation with id '${result.translation.key}' already exists`, result.idStart, result.idEnd));
16
+ }
17
+ else {
18
+ obj[result.translation.key] = result.translation;
19
+ }
20
+ }
21
+ }
22
+ else {
23
+ errors.push(formatError(file, content, results.message, results.start, results.end));
24
+ }
25
+ }
26
+ if (errors.length > 0) {
27
+ return {
28
+ type: "error",
29
+ value: errors,
30
+ };
31
+ }
32
+ else {
33
+ return {
34
+ type: "ok",
35
+ value: obj,
36
+ };
37
+ }
38
+ }
39
+ export function formatError(fileName, fileContent, message, start, end) {
40
+ const lines = fileContent.split(/\r?\n/);
41
+ const errorLine = lines[start.line];
42
+ const markerLine = " ".repeat(start.character) +
43
+ "^".repeat(Math.max(end.character - start.character, 1));
44
+ return `${fileName}:${start.line + 1}:${start.character + 1} - Error: ${message}\n` +
45
+ `${errorLine}\n` +
46
+ `${markerLine}`;
47
+ }
48
+ export async function getTSFiles(dir) {
49
+ const stat = await fs.stat(dir);
50
+ if (stat.isFile()) {
51
+ const ext = path.extname(dir);
52
+ if (ext === ".ts" || ext === ".tsx") {
53
+ return [path.resolve(dir)];
54
+ }
55
+ else {
56
+ return [];
57
+ }
58
+ }
59
+ const entries = await fs.readdir(dir, { withFileTypes: true });
60
+ const files = await Promise.all(entries.map(entry => {
61
+ const fullPath = path.join(dir, entry.name);
62
+ if (entry.isDirectory()) {
63
+ return getTSFiles(fullPath);
64
+ }
65
+ else if (entry.isFile() && (fullPath.endsWith(".ts") || fullPath.endsWith(".tsx"))) {
66
+ return [path.resolve(fullPath)];
67
+ }
68
+ else {
69
+ return [];
70
+ }
71
+ }));
72
+ return files.flat();
73
+ }
74
+ export function findTCalls(node, file) {
75
+ if (ts.isCallExpression(node)) {
76
+ const expression = node.expression;
77
+ if (ts.isIdentifier(expression) && expression.text === "__t") {
78
+ if (node.arguments.length != 2 && node.arguments.length !== 3) {
79
+ return {
80
+ type: "error",
81
+ message: `__t calls must have 2-3 arguments`,
82
+ start: file.getLineAndCharacterOfPosition(expression.getStart()),
83
+ end: file.getLineAndCharacterOfPosition(expression.getEnd())
84
+ };
85
+ }
86
+ const arg1 = node.arguments[0];
87
+ const arg2 = node.arguments[1];
88
+ if (!ts.isStringLiteral(arg1)) {
89
+ return {
90
+ type: "error",
91
+ message: `arg1 is not a string literal`,
92
+ start: file.getLineAndCharacterOfPosition(arg1.getStart()),
93
+ end: file.getLineAndCharacterOfPosition(arg1.getEnd())
94
+ };
95
+ }
96
+ if (!ts.isStringLiteral(arg2)) {
97
+ return {
98
+ type: "error",
99
+ message: `arg2 is not a string literal`,
100
+ start: file.getLineAndCharacterOfPosition(arg2.getStart()),
101
+ end: file.getLineAndCharacterOfPosition(arg2.getEnd())
102
+ };
103
+ }
104
+ return {
105
+ type: "ok",
106
+ translations: [{
107
+ translation: {
108
+ key: arg1.text,
109
+ message: arg2.text,
110
+ },
111
+ idStart: file.getLineAndCharacterOfPosition(arg1.getStart()),
112
+ idEnd: file.getLineAndCharacterOfPosition(arg1.getEnd())
113
+ }],
114
+ };
115
+ }
116
+ else if (ts.isIdentifier(expression) && expression.text === "__tv") {
117
+ if (node.arguments.length != 3) {
118
+ return {
119
+ type: "error",
120
+ message: `__t calls must have 2-3 arguments`,
121
+ start: file.getLineAndCharacterOfPosition(expression.getStart()),
122
+ end: file.getLineAndCharacterOfPosition(expression.getEnd())
123
+ };
124
+ }
125
+ const arg1 = node.arguments[0];
126
+ const arg2 = node.arguments[1];
127
+ if (!ts.isStringLiteral(arg1)) {
128
+ return {
129
+ type: "error",
130
+ message: `arg1 is not a string literal`,
131
+ start: file.getLineAndCharacterOfPosition(arg1.getStart()),
132
+ end: file.getLineAndCharacterOfPosition(arg1.getEnd())
133
+ };
134
+ }
135
+ if (!ts.isArrayLiteralExpression(arg2)) {
136
+ return {
137
+ type: "error",
138
+ message: `arg2 is not a array literal`,
139
+ start: file.getLineAndCharacterOfPosition(arg2.getStart()),
140
+ end: file.getLineAndCharacterOfPosition(arg2.getEnd())
141
+ };
142
+ }
143
+ const variants = [];
144
+ for (let i = 0; i < arg2.elements.length; i++) {
145
+ let variant = arg2.elements[i];
146
+ if (i >= arg2.elements.length - 1) {
147
+ if (!ts.isStringLiteral(variant)) {
148
+ return {
149
+ type: "error",
150
+ message: `The last element of the arg2 array must be an array literal`,
151
+ start: file.getLineAndCharacterOfPosition(arg1.getStart()),
152
+ end: file.getLineAndCharacterOfPosition(arg1.getEnd())
153
+ };
154
+ }
155
+ variants.push(variant.text);
156
+ }
157
+ else {
158
+ if (!ts.isArrayLiteralExpression(variant)) {
159
+ return {
160
+ type: "error",
161
+ message: `This expression must be a array tuple literal`,
162
+ start: file.getLineAndCharacterOfPosition(variant.getStart()),
163
+ end: file.getLineAndCharacterOfPosition(variant.getEnd())
164
+ };
165
+ }
166
+ if (variant.elements.length !== 2) {
167
+ return {
168
+ type: "error",
169
+ message: `This array tuple literal must be of length 2`,
170
+ start: file.getLineAndCharacterOfPosition(variant.getStart()),
171
+ end: file.getLineAndCharacterOfPosition(variant.getEnd())
172
+ };
173
+ }
174
+ const message = variant.elements[0];
175
+ if (!ts.isStringLiteral(message)) {
176
+ return {
177
+ type: "error",
178
+ message: `This expression must be a string literal`,
179
+ start: file.getLineAndCharacterOfPosition(message.getStart()),
180
+ end: file.getLineAndCharacterOfPosition(message.getEnd())
181
+ };
182
+ }
183
+ variants.push(message.text);
184
+ }
185
+ }
186
+ return {
187
+ type: "ok",
188
+ translations: [{
189
+ translation: {
190
+ key: arg1.text,
191
+ message: variants,
192
+ },
193
+ idStart: file.getLineAndCharacterOfPosition(arg1.getStart()),
194
+ idEnd: file.getLineAndCharacterOfPosition(arg1.getEnd())
195
+ }],
196
+ };
197
+ }
198
+ }
199
+ const translations = [];
200
+ const results = node.getChildren().map(n => {
201
+ return findTCalls(n, file);
202
+ });
203
+ for (let result of results) {
204
+ if (result.type === "error") {
205
+ return result;
206
+ }
207
+ else {
208
+ translations.push(...result.translations);
209
+ }
210
+ }
211
+ return {
212
+ type: "ok",
213
+ translations,
214
+ };
215
+ }