poly-lexis 0.3.0 → 0.3.2
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/cli/translations.js +1 -1
- package/dist/cli/translations.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/scripts/generate-schema.js +379 -0
- package/dist/scripts/generate-schema.js.map +1 -0
- package/dist/scripts/verify-translations.js.map +1 -1
- package/dist/translations/core/translations-config.schema.json +132 -142
- package/package.json +5 -3
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// src/scripts/generate-schema.ts
|
|
2
|
+
import { writeFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
// src/translations/core/schema.ts
|
|
7
|
+
var TRANSLATION_PROVIDERS = ["deepl", "google"];
|
|
8
|
+
var DEEPL_LANGUAGES = [
|
|
9
|
+
"ar",
|
|
10
|
+
// Arabic
|
|
11
|
+
"bg",
|
|
12
|
+
// Bulgarian
|
|
13
|
+
"cs",
|
|
14
|
+
// Czech
|
|
15
|
+
"da",
|
|
16
|
+
// Danish
|
|
17
|
+
"de",
|
|
18
|
+
// German
|
|
19
|
+
"el",
|
|
20
|
+
// Greek
|
|
21
|
+
"en",
|
|
22
|
+
// English (unspecified)
|
|
23
|
+
"en_gb",
|
|
24
|
+
// English (British)
|
|
25
|
+
"en_us",
|
|
26
|
+
// English (American)
|
|
27
|
+
"es",
|
|
28
|
+
// Spanish
|
|
29
|
+
"es_419",
|
|
30
|
+
// Spanish (Latin American)
|
|
31
|
+
"et",
|
|
32
|
+
// Estonian
|
|
33
|
+
"fi",
|
|
34
|
+
// Finnish
|
|
35
|
+
"fr",
|
|
36
|
+
// French
|
|
37
|
+
"he",
|
|
38
|
+
// Hebrew (next-gen models only)
|
|
39
|
+
"hu",
|
|
40
|
+
// Hungarian
|
|
41
|
+
"id",
|
|
42
|
+
// Indonesian
|
|
43
|
+
"it",
|
|
44
|
+
// Italian
|
|
45
|
+
"ja",
|
|
46
|
+
// Japanese
|
|
47
|
+
"ko",
|
|
48
|
+
// Korean
|
|
49
|
+
"lt",
|
|
50
|
+
// Lithuanian
|
|
51
|
+
"lv",
|
|
52
|
+
// Latvian
|
|
53
|
+
"nb",
|
|
54
|
+
// Norwegian Bokmål
|
|
55
|
+
"nl",
|
|
56
|
+
// Dutch
|
|
57
|
+
"pl",
|
|
58
|
+
// Polish
|
|
59
|
+
"pt",
|
|
60
|
+
// Portuguese (unspecified)
|
|
61
|
+
"pt_br",
|
|
62
|
+
// Portuguese (Brazilian)
|
|
63
|
+
"pt_pt",
|
|
64
|
+
// Portuguese (excluding Brazilian)
|
|
65
|
+
"ro",
|
|
66
|
+
// Romanian
|
|
67
|
+
"ru",
|
|
68
|
+
// Russian
|
|
69
|
+
"sk",
|
|
70
|
+
// Slovak
|
|
71
|
+
"sl",
|
|
72
|
+
// Slovenian
|
|
73
|
+
"sv",
|
|
74
|
+
// Swedish
|
|
75
|
+
"th",
|
|
76
|
+
// Thai (next-gen models only)
|
|
77
|
+
"tr",
|
|
78
|
+
// Turkish
|
|
79
|
+
"uk",
|
|
80
|
+
// Ukrainian
|
|
81
|
+
"vi",
|
|
82
|
+
// Vietnamese (next-gen models only)
|
|
83
|
+
"zh",
|
|
84
|
+
// Chinese (unspecified)
|
|
85
|
+
"zh_hans",
|
|
86
|
+
// Chinese (simplified)
|
|
87
|
+
"zh_hant"
|
|
88
|
+
// Chinese (traditional)
|
|
89
|
+
];
|
|
90
|
+
var GOOGLE_LANGUAGES = [
|
|
91
|
+
"af",
|
|
92
|
+
// Afrikaans
|
|
93
|
+
"sq",
|
|
94
|
+
// Albanian
|
|
95
|
+
"am",
|
|
96
|
+
// Amharic
|
|
97
|
+
"ar",
|
|
98
|
+
// Arabic
|
|
99
|
+
"hy",
|
|
100
|
+
// Armenian
|
|
101
|
+
"az",
|
|
102
|
+
// Azerbaijani
|
|
103
|
+
"eu",
|
|
104
|
+
// Basque
|
|
105
|
+
"be",
|
|
106
|
+
// Belarusian
|
|
107
|
+
"bn",
|
|
108
|
+
// Bengali
|
|
109
|
+
"bs",
|
|
110
|
+
// Bosnian
|
|
111
|
+
"bg",
|
|
112
|
+
// Bulgarian
|
|
113
|
+
"ca",
|
|
114
|
+
// Catalan
|
|
115
|
+
"ceb",
|
|
116
|
+
// Cebuano
|
|
117
|
+
"zh",
|
|
118
|
+
// Chinese (Simplified)
|
|
119
|
+
"zh_cn",
|
|
120
|
+
// Chinese (Simplified)
|
|
121
|
+
"zh_tw",
|
|
122
|
+
// Chinese (Traditional)
|
|
123
|
+
"co",
|
|
124
|
+
// Corsican
|
|
125
|
+
"hr",
|
|
126
|
+
// Croatian
|
|
127
|
+
"cs",
|
|
128
|
+
// Czech
|
|
129
|
+
"da",
|
|
130
|
+
// Danish
|
|
131
|
+
"nl",
|
|
132
|
+
// Dutch
|
|
133
|
+
"en",
|
|
134
|
+
// English
|
|
135
|
+
"eo",
|
|
136
|
+
// Esperanto
|
|
137
|
+
"et",
|
|
138
|
+
// Estonian
|
|
139
|
+
"fi",
|
|
140
|
+
// Finnish
|
|
141
|
+
"fr",
|
|
142
|
+
// French
|
|
143
|
+
"fy",
|
|
144
|
+
// Frisian
|
|
145
|
+
"gl",
|
|
146
|
+
// Galician
|
|
147
|
+
"ka",
|
|
148
|
+
// Georgian
|
|
149
|
+
"de",
|
|
150
|
+
// German
|
|
151
|
+
"el",
|
|
152
|
+
// Greek
|
|
153
|
+
"gu",
|
|
154
|
+
// Gujarati
|
|
155
|
+
"ht",
|
|
156
|
+
// Haitian Creole
|
|
157
|
+
"ha",
|
|
158
|
+
// Hausa
|
|
159
|
+
"haw",
|
|
160
|
+
// Hawaiian
|
|
161
|
+
"he",
|
|
162
|
+
// Hebrew
|
|
163
|
+
"hi",
|
|
164
|
+
// Hindi
|
|
165
|
+
"hmn",
|
|
166
|
+
// Hmong
|
|
167
|
+
"hu",
|
|
168
|
+
// Hungarian
|
|
169
|
+
"is",
|
|
170
|
+
// Icelandic
|
|
171
|
+
"ig",
|
|
172
|
+
// Igbo
|
|
173
|
+
"id",
|
|
174
|
+
// Indonesian
|
|
175
|
+
"ga",
|
|
176
|
+
// Irish
|
|
177
|
+
"it",
|
|
178
|
+
// Italian
|
|
179
|
+
"ja",
|
|
180
|
+
// Japanese
|
|
181
|
+
"jv",
|
|
182
|
+
// Javanese
|
|
183
|
+
"kn",
|
|
184
|
+
// Kannada
|
|
185
|
+
"kk",
|
|
186
|
+
// Kazakh
|
|
187
|
+
"km",
|
|
188
|
+
// Khmer
|
|
189
|
+
"rw",
|
|
190
|
+
// Kinyarwanda
|
|
191
|
+
"ko",
|
|
192
|
+
// Korean
|
|
193
|
+
"ku",
|
|
194
|
+
// Kurdish
|
|
195
|
+
"ky",
|
|
196
|
+
// Kyrgyz
|
|
197
|
+
"lo",
|
|
198
|
+
// Lao
|
|
199
|
+
"la",
|
|
200
|
+
// Latin
|
|
201
|
+
"lv",
|
|
202
|
+
// Latvian
|
|
203
|
+
"lt",
|
|
204
|
+
// Lithuanian
|
|
205
|
+
"lb",
|
|
206
|
+
// Luxembourgish
|
|
207
|
+
"mk",
|
|
208
|
+
// Macedonian
|
|
209
|
+
"mg",
|
|
210
|
+
// Malagasy
|
|
211
|
+
"ms",
|
|
212
|
+
// Malay
|
|
213
|
+
"ml",
|
|
214
|
+
// Malayalam
|
|
215
|
+
"mt",
|
|
216
|
+
// Maltese
|
|
217
|
+
"mi",
|
|
218
|
+
// Maori
|
|
219
|
+
"mr",
|
|
220
|
+
// Marathi
|
|
221
|
+
"mn",
|
|
222
|
+
// Mongolian
|
|
223
|
+
"my",
|
|
224
|
+
// Myanmar (Burmese)
|
|
225
|
+
"ne",
|
|
226
|
+
// Nepali
|
|
227
|
+
"no",
|
|
228
|
+
// Norwegian
|
|
229
|
+
"ny",
|
|
230
|
+
// Nyanja (Chichewa)
|
|
231
|
+
"or",
|
|
232
|
+
// Odia (Oriya)
|
|
233
|
+
"ps",
|
|
234
|
+
// Pashto
|
|
235
|
+
"fa",
|
|
236
|
+
// Persian
|
|
237
|
+
"pl",
|
|
238
|
+
// Polish
|
|
239
|
+
"pt",
|
|
240
|
+
// Portuguese
|
|
241
|
+
"pt_br",
|
|
242
|
+
// Portuguese (Brazil)
|
|
243
|
+
"pa",
|
|
244
|
+
// Punjabi
|
|
245
|
+
"ro",
|
|
246
|
+
// Romanian
|
|
247
|
+
"ru",
|
|
248
|
+
// Russian
|
|
249
|
+
"sm",
|
|
250
|
+
// Samoan
|
|
251
|
+
"gd",
|
|
252
|
+
// Scots Gaelic
|
|
253
|
+
"sr",
|
|
254
|
+
// Serbian
|
|
255
|
+
"st",
|
|
256
|
+
// Sesotho
|
|
257
|
+
"sn",
|
|
258
|
+
// Shona
|
|
259
|
+
"sd",
|
|
260
|
+
// Sindhi
|
|
261
|
+
"si",
|
|
262
|
+
// Sinhala (Sinhalese)
|
|
263
|
+
"sk",
|
|
264
|
+
// Slovak
|
|
265
|
+
"sl",
|
|
266
|
+
// Slovenian
|
|
267
|
+
"so",
|
|
268
|
+
// Somali
|
|
269
|
+
"es",
|
|
270
|
+
// Spanish
|
|
271
|
+
"su",
|
|
272
|
+
// Sundanese
|
|
273
|
+
"sw",
|
|
274
|
+
// Swahili
|
|
275
|
+
"sv",
|
|
276
|
+
// Swedish
|
|
277
|
+
"tl",
|
|
278
|
+
// Tagalog (Filipino)
|
|
279
|
+
"tg",
|
|
280
|
+
// Tajik
|
|
281
|
+
"ta",
|
|
282
|
+
// Tamil
|
|
283
|
+
"tt",
|
|
284
|
+
// Tatar
|
|
285
|
+
"te",
|
|
286
|
+
// Telugu
|
|
287
|
+
"th",
|
|
288
|
+
// Thai
|
|
289
|
+
"tr",
|
|
290
|
+
// Turkish
|
|
291
|
+
"tk",
|
|
292
|
+
// Turkmen
|
|
293
|
+
"uk",
|
|
294
|
+
// Ukrainian
|
|
295
|
+
"ur",
|
|
296
|
+
// Urdu
|
|
297
|
+
"ug",
|
|
298
|
+
// Uyghur
|
|
299
|
+
"uz",
|
|
300
|
+
// Uzbek
|
|
301
|
+
"vi",
|
|
302
|
+
// Vietnamese
|
|
303
|
+
"cy",
|
|
304
|
+
// Welsh
|
|
305
|
+
"xh",
|
|
306
|
+
// Xhosa
|
|
307
|
+
"yi",
|
|
308
|
+
// Yiddish
|
|
309
|
+
"yo",
|
|
310
|
+
// Yoruba
|
|
311
|
+
"zu"
|
|
312
|
+
// Zulu
|
|
313
|
+
];
|
|
314
|
+
var SUPPORTED_LANGUAGES = Array.from(
|
|
315
|
+
/* @__PURE__ */ new Set([...DEEPL_LANGUAGES, ...GOOGLE_LANGUAGES])
|
|
316
|
+
).sort();
|
|
317
|
+
var TRANSLATION_CONFIG_SCHEMA = {
|
|
318
|
+
title: "Translation Configuration",
|
|
319
|
+
description: "Configuration for the translation management system",
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
$schema: {
|
|
323
|
+
type: "string",
|
|
324
|
+
description: "JSON Schema reference"
|
|
325
|
+
},
|
|
326
|
+
translationsPath: {
|
|
327
|
+
type: "string",
|
|
328
|
+
description: "Path to the translations directory relative to project root",
|
|
329
|
+
default: "public/static/locales",
|
|
330
|
+
examples: ["public/static/locales", "src/locales", "locales"]
|
|
331
|
+
},
|
|
332
|
+
languages: {
|
|
333
|
+
type: "array",
|
|
334
|
+
description: "List of language codes to support",
|
|
335
|
+
items: {
|
|
336
|
+
type: "string",
|
|
337
|
+
enum: SUPPORTED_LANGUAGES
|
|
338
|
+
},
|
|
339
|
+
minItems: 1,
|
|
340
|
+
uniqueItems: true,
|
|
341
|
+
default: ["en"]
|
|
342
|
+
},
|
|
343
|
+
sourceLanguage: {
|
|
344
|
+
type: "string",
|
|
345
|
+
description: 'Source language for translations (usually "en")',
|
|
346
|
+
enum: SUPPORTED_LANGUAGES,
|
|
347
|
+
default: "en"
|
|
348
|
+
},
|
|
349
|
+
typesOutputPath: {
|
|
350
|
+
type: "string",
|
|
351
|
+
description: "Path to output TypeScript types file",
|
|
352
|
+
default: "src/types/i18nTypes.ts",
|
|
353
|
+
examples: ["src/types/i18nTypes.ts", "src/types/translations.ts"]
|
|
354
|
+
},
|
|
355
|
+
provider: {
|
|
356
|
+
type: "string",
|
|
357
|
+
description: "Translation provider to use (deepl or google)",
|
|
358
|
+
enum: TRANSLATION_PROVIDERS,
|
|
359
|
+
default: "deepl"
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
required: ["translationsPath", "languages", "sourceLanguage"],
|
|
363
|
+
additionalProperties: false
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// src/scripts/generate-schema.ts
|
|
367
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
368
|
+
var __dirname2 = dirname(__filename2);
|
|
369
|
+
var projectRoot = join(__dirname2, "../..");
|
|
370
|
+
var SCHEMA_OUTPUT_PATH = join(projectRoot, "src/translations/core/translations-config.schema.json");
|
|
371
|
+
var schemaWithMeta = {
|
|
372
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
373
|
+
$id: "https://raw.githubusercontent.com/designedhead/lexis/main/src/translations/core/translations-config.schema.json",
|
|
374
|
+
...TRANSLATION_CONFIG_SCHEMA
|
|
375
|
+
};
|
|
376
|
+
writeFileSync(SCHEMA_OUTPUT_PATH, `${JSON.stringify(schemaWithMeta, null, 2)}
|
|
377
|
+
`);
|
|
378
|
+
console.log("\u2705 Generated translations-config.schema.json");
|
|
379
|
+
//# sourceMappingURL=generate-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/scripts/generate-schema.ts","../../src/translations/core/schema.ts"],"sourcesContent":["/**\n * Generate JSON schema file from TypeScript schema definition\n * This ensures the JSON schema stays in sync with the TypeScript source\n */\nimport { writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { TRANSLATION_CONFIG_SCHEMA } from '../translations/core/schema.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// Write to src directory, not dist\n// When compiled to dist/scripts/, we need to go up 2 levels to get to project root\nconst projectRoot = join(__dirname, '../..');\nconst SCHEMA_OUTPUT_PATH = join(projectRoot, 'src/translations/core/translations-config.schema.json');\n\nconst schemaWithMeta = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n $id: 'https://raw.githubusercontent.com/designedhead/lexis/main/src/translations/core/translations-config.schema.json',\n ...TRANSLATION_CONFIG_SCHEMA\n};\n\nwriteFileSync(SCHEMA_OUTPUT_PATH, `${JSON.stringify(schemaWithMeta, null, 2)}\\n`);\n\nconsole.log('✅ Generated translations-config.schema.json');\n","/**\n * Translation provider types\n */\nexport const TRANSLATION_PROVIDERS = ['deepl', 'google'] as const;\nexport type TranslationProviderType = (typeof TRANSLATION_PROVIDERS)[number];\n\n/**\n * DeepL supported target languages\n * Based on DeepL API v2 documentation\n */\nexport const DEEPL_LANGUAGES = [\n 'ar', // Arabic\n 'bg', // Bulgarian\n 'cs', // Czech\n 'da', // Danish\n 'de', // German\n 'el', // Greek\n 'en', // English (unspecified)\n 'en_gb', // English (British)\n 'en_us', // English (American)\n 'es', // Spanish\n 'es_419', // Spanish (Latin American)\n 'et', // Estonian\n 'fi', // Finnish\n 'fr', // French\n 'he', // Hebrew (next-gen models only)\n 'hu', // Hungarian\n 'id', // Indonesian\n 'it', // Italian\n 'ja', // Japanese\n 'ko', // Korean\n 'lt', // Lithuanian\n 'lv', // Latvian\n 'nb', // Norwegian Bokmål\n 'nl', // Dutch\n 'pl', // Polish\n 'pt', // Portuguese (unspecified)\n 'pt_br', // Portuguese (Brazilian)\n 'pt_pt', // Portuguese (excluding Brazilian)\n 'ro', // Romanian\n 'ru', // Russian\n 'sk', // Slovak\n 'sl', // Slovenian\n 'sv', // Swedish\n 'th', // Thai (next-gen models only)\n 'tr', // Turkish\n 'uk', // Ukrainian\n 'vi', // Vietnamese (next-gen models only)\n 'zh', // Chinese (unspecified)\n 'zh_hans', // Chinese (simplified)\n 'zh_hant' // Chinese (traditional)\n] as const;\n\n/**\n * Google Translate supported languages\n * Based on Google Cloud Translation API v2\n * Reference: https://docs.cloud.google.com/translate/docs/languages\n */\nexport const GOOGLE_LANGUAGES = [\n 'af', // Afrikaans\n 'sq', // Albanian\n 'am', // Amharic\n 'ar', // Arabic\n 'hy', // Armenian\n 'az', // Azerbaijani\n 'eu', // Basque\n 'be', // Belarusian\n 'bn', // Bengali\n 'bs', // Bosnian\n 'bg', // Bulgarian\n 'ca', // Catalan\n 'ceb', // Cebuano\n 'zh', // Chinese (Simplified)\n 'zh_cn', // Chinese (Simplified)\n 'zh_tw', // Chinese (Traditional)\n 'co', // Corsican\n 'hr', // Croatian\n 'cs', // Czech\n 'da', // Danish\n 'nl', // Dutch\n 'en', // English\n 'eo', // Esperanto\n 'et', // Estonian\n 'fi', // Finnish\n 'fr', // French\n 'fy', // Frisian\n 'gl', // Galician\n 'ka', // Georgian\n 'de', // German\n 'el', // Greek\n 'gu', // Gujarati\n 'ht', // Haitian Creole\n 'ha', // Hausa\n 'haw', // Hawaiian\n 'he', // Hebrew\n 'hi', // Hindi\n 'hmn', // Hmong\n 'hu', // Hungarian\n 'is', // Icelandic\n 'ig', // Igbo\n 'id', // Indonesian\n 'ga', // Irish\n 'it', // Italian\n 'ja', // Japanese\n 'jv', // Javanese\n 'kn', // Kannada\n 'kk', // Kazakh\n 'km', // Khmer\n 'rw', // Kinyarwanda\n 'ko', // Korean\n 'ku', // Kurdish\n 'ky', // Kyrgyz\n 'lo', // Lao\n 'la', // Latin\n 'lv', // Latvian\n 'lt', // Lithuanian\n 'lb', // Luxembourgish\n 'mk', // Macedonian\n 'mg', // Malagasy\n 'ms', // Malay\n 'ml', // Malayalam\n 'mt', // Maltese\n 'mi', // Maori\n 'mr', // Marathi\n 'mn', // Mongolian\n 'my', // Myanmar (Burmese)\n 'ne', // Nepali\n 'no', // Norwegian\n 'ny', // Nyanja (Chichewa)\n 'or', // Odia (Oriya)\n 'ps', // Pashto\n 'fa', // Persian\n 'pl', // Polish\n 'pt', // Portuguese\n 'pt_br', // Portuguese (Brazil)\n 'pa', // Punjabi\n 'ro', // Romanian\n 'ru', // Russian\n 'sm', // Samoan\n 'gd', // Scots Gaelic\n 'sr', // Serbian\n 'st', // Sesotho\n 'sn', // Shona\n 'sd', // Sindhi\n 'si', // Sinhala (Sinhalese)\n 'sk', // Slovak\n 'sl', // Slovenian\n 'so', // Somali\n 'es', // Spanish\n 'su', // Sundanese\n 'sw', // Swahili\n 'sv', // Swedish\n 'tl', // Tagalog (Filipino)\n 'tg', // Tajik\n 'ta', // Tamil\n 'tt', // Tatar\n 'te', // Telugu\n 'th', // Thai\n 'tr', // Turkish\n 'tk', // Turkmen\n 'uk', // Ukrainian\n 'ur', // Urdu\n 'ug', // Uyghur\n 'uz', // Uzbek\n 'vi', // Vietnamese\n 'cy', // Welsh\n 'xh', // Xhosa\n 'yi', // Yiddish\n 'yo', // Yoruba\n 'zu' // Zulu\n] as const;\n\n/**\n * All supported language codes (union of DeepL and Google Translate)\n */\nexport const SUPPORTED_LANGUAGES = Array.from(\n new Set([...DEEPL_LANGUAGES, ...GOOGLE_LANGUAGES])\n).sort() as readonly string[];\n\nexport type DeepLLanguage = (typeof DEEPL_LANGUAGES)[number];\nexport type GoogleLanguage = (typeof GOOGLE_LANGUAGES)[number];\nexport type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\n/**\n * JSON Schema for .translationsrc.json\n */\nexport const TRANSLATION_CONFIG_SCHEMA = {\n title: 'Translation Configuration',\n description: 'Configuration for the translation management system',\n type: 'object',\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema reference'\n },\n translationsPath: {\n type: 'string',\n description: 'Path to the translations directory relative to project root',\n default: 'public/static/locales',\n examples: ['public/static/locales', 'src/locales', 'locales']\n },\n languages: {\n type: 'array',\n description: 'List of language codes to support',\n items: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES\n },\n minItems: 1,\n uniqueItems: true,\n default: ['en']\n },\n sourceLanguage: {\n type: 'string',\n description: 'Source language for translations (usually \"en\")',\n enum: SUPPORTED_LANGUAGES,\n default: 'en'\n },\n typesOutputPath: {\n type: 'string',\n description: 'Path to output TypeScript types file',\n default: 'src/types/i18nTypes.ts',\n examples: ['src/types/i18nTypes.ts', 'src/types/translations.ts']\n },\n provider: {\n type: 'string',\n description: 'Translation provider to use (deepl or google)',\n enum: TRANSLATION_PROVIDERS,\n default: 'deepl'\n }\n },\n required: ['translationsPath', 'languages', 'sourceLanguage'],\n additionalProperties: false\n};\n\n/**\n * Validate if a language code is supported\n */\nexport function isValidLanguage(lang: string): lang is SupportedLanguage {\n return SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);\n}\n\n/**\n * Validate if a language is supported by DeepL\n */\nexport function isValidDeepLLanguage(lang: string): lang is DeepLLanguage {\n return DEEPL_LANGUAGES.includes(lang as DeepLLanguage);\n}\n\n/**\n * Validate if a language is supported by Google Translate\n */\nexport function isValidGoogleLanguage(lang: string): lang is GoogleLanguage {\n return GOOGLE_LANGUAGES.includes(lang as GoogleLanguage);\n}\n\n/**\n * Validate if a language is supported by a specific provider\n */\nexport function isValidLanguageForProvider(lang: string, provider: TranslationProviderType): boolean {\n switch (provider) {\n case 'deepl':\n return isValidDeepLLanguage(lang);\n case 'google':\n return isValidGoogleLanguage(lang);\n default:\n return false;\n }\n}\n\n/**\n * Validate languages array\n */\nexport function validateLanguages(languages: string[]): {\n valid: boolean;\n invalid: string[];\n} {\n const invalid = languages.filter((lang) => !isValidLanguage(lang));\n return {\n valid: invalid.length === 0,\n invalid\n };\n}\n\n/**\n * Validate languages array for a specific provider\n */\nexport function validateLanguagesForProvider(\n languages: string[],\n provider: TranslationProviderType\n): {\n valid: boolean;\n invalid: string[];\n} {\n const invalid = languages.filter((lang) => !isValidLanguageForProvider(lang, provider));\n return {\n valid: invalid.length === 0,\n invalid\n };\n}\n\n/**\n * Get supported languages for a specific provider\n */\nexport function getSupportedLanguages(provider: TranslationProviderType): readonly string[] {\n switch (provider) {\n case 'deepl':\n return DEEPL_LANGUAGES;\n case 'google':\n return GOOGLE_LANGUAGES;\n default:\n return [];\n }\n}\n"],"mappings":";AAIA,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;;;ACHvB,IAAM,wBAAwB,CAAC,SAAS,QAAQ;AAOhD,IAAM,kBAAkB;AAAA,EAC7B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAOO,IAAM,mBAAmB;AAAA,EAC9B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAKO,IAAM,sBAAsB,MAAM;AAAA,EACvC,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AACnD,EAAE,KAAK;AASA,IAAM,4BAA4B;AAAA,EACvC,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,MACT,UAAU,CAAC,yBAAyB,eAAe,SAAS;AAAA,IAC9D;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV,aAAa;AAAA,MACb,SAAS,CAAC,IAAI;AAAA,IAChB;AAAA,IACA,gBAAgB;AAAA,MACd,MAAM;AAAA,MACN,aAAa;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,MACT,UAAU,CAAC,0BAA0B,2BAA2B;AAAA,IAClE;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAU,CAAC,oBAAoB,aAAa,gBAAgB;AAAA,EAC5D,sBAAsB;AACxB;;;ADhOA,IAAMA,cAAa,cAAc,YAAY,GAAG;AAChD,IAAMC,aAAY,QAAQD,WAAU;AAIpC,IAAM,cAAc,KAAKC,YAAW,OAAO;AAC3C,IAAM,qBAAqB,KAAK,aAAa,uDAAuD;AAEpG,IAAM,iBAAiB;AAAA,EACrB,SAAS;AAAA,EACT,KAAK;AAAA,EACL,GAAG;AACL;AAEA,cAAc,oBAAoB,GAAG,KAAK,UAAU,gBAAgB,MAAM,CAAC,CAAC;AAAA,CAAI;AAEhF,QAAQ,IAAI,kDAA6C;","names":["__filename","__dirname"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/translations/cli/validate.ts","../../src/translations/utils/utils.ts","../../src/translations/cli/init.ts","../../src/translations/core/schema.ts","../../src/translations/core/types.ts","../../src/scripts/verify-translations.ts"],"sourcesContent":["import * as path from 'node:path';\nimport type { MissingTranslation, ValidationResult } from '../core/types.js';\nimport { getAvailableLanguages, getNamespaces, readTranslations } from '../utils/utils.js';\nimport { loadConfig } from './init.js';\n\n/**\n * Validate all translations against the source language\n * Checks for missing keys and empty values\n */\nexport function validateTranslations(projectRoot: string = process.cwd()): ValidationResult {\n const config = loadConfig(projectRoot);\n const translationsPath = path.join(projectRoot, config.translationsPath);\n const sourceLanguage = config.sourceLanguage;\n\n const missing: MissingTranslation[] = [];\n const empty: MissingTranslation[] = [];\n\n // Read source translations\n const sourceTranslations = readTranslations(translationsPath, sourceLanguage);\n const sourceNamespaces = getNamespaces(translationsPath, sourceLanguage);\n\n // Get all available languages\n const languages = getAvailableLanguages(translationsPath).filter((lang) => lang !== sourceLanguage);\n\n console.log('=====');\n console.log('Validating translations');\n console.log('=====');\n console.log(`Source language: ${sourceLanguage}`);\n console.log(`Target languages: ${languages.join(', ')}`);\n console.log(`Namespaces: ${sourceNamespaces.join(', ')}`);\n console.log('=====');\n\n // Validate each language\n for (const language of languages) {\n const targetTranslations = readTranslations(translationsPath, language);\n\n // Check each namespace\n for (const namespace of sourceNamespaces) {\n const sourceKeys = sourceTranslations[namespace] || {};\n const targetKeys = targetTranslations[namespace] || {};\n\n // Check for missing or empty translations\n for (const [key, sourceValue] of Object.entries(sourceKeys)) {\n const targetValue = targetKeys[key];\n\n // Missing key in target language\n if (targetValue === undefined) {\n missing.push({\n namespace,\n key,\n language,\n sourceValue\n });\n }\n // Empty value in target language\n else if (typeof targetValue === 'string' && targetValue.trim() === '') {\n empty.push({\n namespace,\n key,\n language,\n sourceValue\n });\n }\n }\n }\n }\n\n const valid = missing.length === 0 && empty.length === 0;\n\n if (valid) {\n console.log('✓ All translations are valid!');\n } else {\n if (missing.length > 0) {\n console.log(`\\n⚠ Found ${missing.length} missing translations:`);\n for (const item of missing.slice(0, 10)) {\n console.log(` ${item.language}/${item.namespace}.json -> ${item.key}`);\n }\n if (missing.length > 10) {\n console.log(` ... and ${missing.length - 10} more`);\n }\n }\n\n if (empty.length > 0) {\n console.log(`\\n⚠ Found ${empty.length} empty translations:`);\n for (const item of empty.slice(0, 10)) {\n console.log(` ${item.language}/${item.namespace}.json -> ${item.key}`);\n }\n if (empty.length > 10) {\n console.log(` ... and ${empty.length - 10} more`);\n }\n }\n }\n\n console.log('=====');\n\n return { valid, missing, empty };\n}\n\n/**\n * Get all missing or empty translations for a specific language\n */\nexport function getMissingForLanguage(\n projectRoot: string,\n language: string\n): Array<MissingTranslation & { type: 'missing' | 'empty' }> {\n const result = validateTranslations(projectRoot);\n const items = [\n ...result.missing.filter((m) => m.language === language).map((m) => ({ ...m, type: 'missing' as const })),\n ...result.empty.filter((e) => e.language === language).map((e) => ({ ...e, type: 'empty' as const }))\n ];\n\n return items;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { TranslationFile, TranslationFiles } from '../core/types.js';\n\n/**\n * Read all translation files for a specific language\n */\nexport function readTranslations(translationsPath: string, language: string): TranslationFiles {\n const langPath = path.join(translationsPath, language);\n\n if (!fs.existsSync(langPath)) {\n return {};\n }\n\n const files = fs.readdirSync(langPath).filter((f) => f.endsWith('.json'));\n const translations: TranslationFiles = {};\n\n for (const file of files) {\n const namespace = path.basename(file, '.json');\n const filePath = path.join(langPath, file);\n const content = fs.readFileSync(filePath, 'utf-8');\n translations[namespace] = JSON.parse(content) as TranslationFile;\n }\n\n return translations;\n}\n\n/**\n * Write translation file for a specific language and namespace\n */\nexport function writeTranslation(\n translationsPath: string,\n language: string,\n namespace: string,\n translations: TranslationFile\n): void {\n const langPath = path.join(translationsPath, language);\n\n if (!fs.existsSync(langPath)) {\n fs.mkdirSync(langPath, { recursive: true });\n }\n\n const filePath = path.join(langPath, `${namespace}.json`);\n fs.writeFileSync(filePath, `${JSON.stringify(translations, null, 2)}\\n`, 'utf-8');\n}\n\n/**\n * Get all available languages from the translations directory\n */\nexport function getAvailableLanguages(translationsPath: string): string[] {\n if (!fs.existsSync(translationsPath)) {\n return [];\n }\n\n return fs\n .readdirSync(translationsPath, { withFileTypes: true })\n .filter((dirent) => dirent.isDirectory())\n .map((dirent) => dirent.name);\n}\n\n/**\n * Get all namespaces for a specific language\n */\nexport function getNamespaces(translationsPath: string, language: string): string[] {\n const langPath = path.join(translationsPath, language);\n\n if (!fs.existsSync(langPath)) {\n return [];\n }\n\n return fs\n .readdirSync(langPath)\n .filter((f) => f.endsWith('.json'))\n .map((f) => path.basename(f, '.json'));\n}\n\n/**\n * Check if a string contains interpolation variables (e.g., {{variable}})\n */\nexport function hasInterpolation(text: string): boolean {\n return /\\{\\{[^}]+\\}\\}/g.test(text);\n}\n\n/**\n * Extract interpolation variable names from a string\n */\nexport function extractVariables(text: string): string[] {\n const matches = text.match(/\\{\\{([^}]+)\\}\\}/g);\n if (!matches) return [];\n return matches.map((match) => match.replace(/\\{\\{|\\}\\}/g, '').trim());\n}\n\n/**\n * Validate that translated text has the same variables as source text\n */\nexport function validateVariables(sourceText: string, translatedText: string): boolean {\n const sourceVars = extractVariables(sourceText).sort();\n const translatedVars = extractVariables(translatedText).sort();\n\n if (sourceVars.length !== translatedVars.length) {\n return false;\n }\n\n return sourceVars.every((v, i) => v === translatedVars[i]);\n}\n\n/**\n * Sort object keys alphabetically\n */\nexport function sortKeys<T extends Record<string, unknown>>(obj: T): T {\n const sorted = {} as T;\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n sorted[key as keyof T] = obj[key as keyof T];\n }\n return sorted;\n}\n\n/**\n * Ensure translations path exists and has proper structure\n */\nexport function ensureTranslationsStructure(translationsPath: string, languages: string[]): void {\n if (!fs.existsSync(translationsPath)) {\n fs.mkdirSync(translationsPath, { recursive: true });\n }\n\n for (const lang of languages) {\n const langPath = path.join(translationsPath, lang);\n if (!fs.existsSync(langPath)) {\n fs.mkdirSync(langPath, { recursive: true });\n }\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { validateLanguages } from '../core/schema.js';\nimport type { TranslationConfig } from '../core/types.js';\nimport { DEFAULT_CONFIG, DEFAULT_LANGUAGES } from '../core/types.js';\nimport { ensureTranslationsStructure, getAvailableLanguages } from '../utils/utils.js';\n\n/**\n * Detect existing translation structure in common locations\n */\nexport function detectExistingTranslations(projectRoot: string): {\n path: string | null;\n languages: string[];\n} {\n const possiblePaths = ['public/static/locales', 'public/locales', 'src/locales', 'locales', 'i18n', 'translations'];\n\n for (const possiblePath of possiblePaths) {\n const fullPath = path.join(projectRoot, possiblePath);\n if (fs.existsSync(fullPath)) {\n const languages = getAvailableLanguages(fullPath);\n if (languages.length > 0) {\n return { path: possiblePath, languages };\n }\n }\n }\n\n return { path: null, languages: [] };\n}\n\n/**\n * Initialize translation structure for a project\n */\nexport function initTranslations(projectRoot: string, config: TranslationConfig = {}): void {\n console.log('=====');\n console.log('Initializing translation structure');\n console.log('=====');\n\n // Detect existing translations\n const existing = detectExistingTranslations(projectRoot);\n\n let finalConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Use detected path and languages if found\n if (existing.path && existing.languages.length > 0) {\n console.log(`✓ Detected existing translations at: ${existing.path}`);\n console.log(`✓ Found languages: ${existing.languages.join(', ')}`);\n\n // Validate detected languages\n const validation = validateLanguages(existing.languages);\n if (!validation.valid) {\n console.log(`⚠️ Warning: Invalid language codes found: ${validation.invalid.join(', ')}`);\n console.log('These languages will be skipped.');\n }\n\n const validLanguages = existing.languages.filter((lang) => !validation.invalid.includes(lang));\n\n finalConfig = {\n ...finalConfig,\n translationsPath: existing.path,\n languages: validLanguages.length > 0 ? validLanguages : finalConfig.languages\n };\n }\n\n const translationsPath = path.join(projectRoot, finalConfig.translationsPath);\n const languages = finalConfig.languages.length > 0 ? finalConfig.languages : [...DEFAULT_LANGUAGES];\n\n console.log(`Project root: ${projectRoot}`);\n console.log(`Translations path: ${translationsPath}`);\n console.log(`Languages: ${languages.join(', ')}`);\n console.log('=====');\n\n // Validate languages\n const validation = validateLanguages(languages);\n if (!validation.valid) {\n throw new Error(`Invalid language codes: ${validation.invalid.join(', ')}`);\n }\n\n // Create directory structure\n ensureTranslationsStructure(translationsPath, languages);\n\n // Create sample translation files for English (source language)\n const sourceLanguage = finalConfig.sourceLanguage;\n const sourcePath = path.join(translationsPath, sourceLanguage);\n\n // Create a sample common.json if it doesn't exist\n const commonPath = path.join(sourcePath, 'common.json');\n if (!fs.existsSync(commonPath)) {\n const sampleTranslations = {\n LOADING: 'Loading',\n SAVE: 'Save',\n CANCEL: 'Cancel',\n SUBMIT: 'Submit',\n ERROR: 'Error',\n SUCCESS: 'Success'\n };\n\n fs.writeFileSync(commonPath, `${JSON.stringify(sampleTranslations, null, 2)}\\n`, 'utf-8');\n console.log(`Created sample file: ${commonPath}`);\n }\n\n // Create empty translation files for other languages\n for (const lang of languages) {\n if (lang === sourceLanguage) continue;\n\n const langCommonPath = path.join(translationsPath, lang, 'common.json');\n if (!fs.existsSync(langCommonPath)) {\n fs.writeFileSync(langCommonPath, '{}\\n', 'utf-8');\n console.log(`Created empty file: ${langCommonPath}`);\n }\n }\n\n // Create config file with schema reference\n const configPath = path.join(projectRoot, '.translationsrc.json');\n if (!fs.existsSync(configPath)) {\n const configContent = {\n $schema: './node_modules/@repo/env-scripts/dist/translations/core/translations-config.schema.json',\n translationsPath: finalConfig.translationsPath,\n languages,\n sourceLanguage,\n typesOutputPath: finalConfig.typesOutputPath\n };\n\n fs.writeFileSync(configPath, `${JSON.stringify(configContent, null, 2)}\\n`, 'utf-8');\n console.log(`Created config file: ${configPath}`);\n }\n\n console.log('=====');\n console.log('Translation structure initialized successfully!');\n console.log('=====');\n}\n\n/**\n * Load translation configuration from .translationsrc.json\n */\nexport function loadConfig(projectRoot: string): Required<TranslationConfig> {\n const configPath = path.join(projectRoot, '.translationsrc.json');\n\n if (!fs.existsSync(configPath)) {\n // Try to detect existing translations\n const existing = detectExistingTranslations(projectRoot);\n if (existing.path && existing.languages.length > 0) {\n console.log(`ℹ️ No config found, but detected translations at ${existing.path}`);\n return {\n ...DEFAULT_CONFIG,\n translationsPath: existing.path,\n languages: existing.languages\n };\n }\n return DEFAULT_CONFIG;\n }\n\n const configContent = fs.readFileSync(configPath, 'utf-8');\n const config = JSON.parse(configContent) as TranslationConfig;\n\n // Validate languages in config\n if (config.languages) {\n const validation = validateLanguages(config.languages);\n if (!validation.valid) {\n console.warn(`⚠️ Warning: Invalid language codes in config: ${validation.invalid.join(', ')}`);\n console.warn('Please update .translationsrc.json with valid language codes.');\n }\n }\n\n return { ...DEFAULT_CONFIG, ...config };\n}\n","/**\n * Translation provider types\n */\nexport const TRANSLATION_PROVIDERS = ['deepl', 'google'] as const;\nexport type TranslationProviderType = (typeof TRANSLATION_PROVIDERS)[number];\n\n/**\n * DeepL supported target languages\n * Based on DeepL API v2 documentation\n */\nexport const DEEPL_LANGUAGES = [\n 'ar', // Arabic\n 'bg', // Bulgarian\n 'cs', // Czech\n 'da', // Danish\n 'de', // German\n 'el', // Greek\n 'en', // English (unspecified)\n 'en_gb', // English (British)\n 'en_us', // English (American)\n 'es', // Spanish\n 'es_419', // Spanish (Latin American)\n 'et', // Estonian\n 'fi', // Finnish\n 'fr', // French\n 'he', // Hebrew (next-gen models only)\n 'hu', // Hungarian\n 'id', // Indonesian\n 'it', // Italian\n 'ja', // Japanese\n 'ko', // Korean\n 'lt', // Lithuanian\n 'lv', // Latvian\n 'nb', // Norwegian Bokmål\n 'nl', // Dutch\n 'pl', // Polish\n 'pt', // Portuguese (unspecified)\n 'pt_br', // Portuguese (Brazilian)\n 'pt_pt', // Portuguese (excluding Brazilian)\n 'ro', // Romanian\n 'ru', // Russian\n 'sk', // Slovak\n 'sl', // Slovenian\n 'sv', // Swedish\n 'th', // Thai (next-gen models only)\n 'tr', // Turkish\n 'uk', // Ukrainian\n 'vi', // Vietnamese (next-gen models only)\n 'zh', // Chinese (unspecified)\n 'zh_hans', // Chinese (simplified)\n 'zh_hant' // Chinese (traditional)\n] as const;\n\n/**\n * Google Translate supported languages\n * Based on Google Cloud Translation API v2\n * Reference: https://docs.cloud.google.com/translate/docs/languages\n */\nexport const GOOGLE_LANGUAGES = [\n 'af', // Afrikaans\n 'sq', // Albanian\n 'am', // Amharic\n 'ar', // Arabic\n 'hy', // Armenian\n 'az', // Azerbaijani\n 'eu', // Basque\n 'be', // Belarusian\n 'bn', // Bengali\n 'bs', // Bosnian\n 'bg', // Bulgarian\n 'ca', // Catalan\n 'ceb', // Cebuano\n 'zh', // Chinese (Simplified)\n 'zh_cn', // Chinese (Simplified)\n 'zh_tw', // Chinese (Traditional)\n 'co', // Corsican\n 'hr', // Croatian\n 'cs', // Czech\n 'da', // Danish\n 'nl', // Dutch\n 'en', // English\n 'eo', // Esperanto\n 'et', // Estonian\n 'fi', // Finnish\n 'fr', // French\n 'fy', // Frisian\n 'gl', // Galician\n 'ka', // Georgian\n 'de', // German\n 'el', // Greek\n 'gu', // Gujarati\n 'ht', // Haitian Creole\n 'ha', // Hausa\n 'haw', // Hawaiian\n 'he', // Hebrew\n 'hi', // Hindi\n 'hmn', // Hmong\n 'hu', // Hungarian\n 'is', // Icelandic\n 'ig', // Igbo\n 'id', // Indonesian\n 'ga', // Irish\n 'it', // Italian\n 'ja', // Japanese\n 'jv', // Javanese\n 'kn', // Kannada\n 'kk', // Kazakh\n 'km', // Khmer\n 'rw', // Kinyarwanda\n 'ko', // Korean\n 'ku', // Kurdish\n 'ky', // Kyrgyz\n 'lo', // Lao\n 'la', // Latin\n 'lv', // Latvian\n 'lt', // Lithuanian\n 'lb', // Luxembourgish\n 'mk', // Macedonian\n 'mg', // Malagasy\n 'ms', // Malay\n 'ml', // Malayalam\n 'mt', // Maltese\n 'mi', // Maori\n 'mr', // Marathi\n 'mn', // Mongolian\n 'my', // Myanmar (Burmese)\n 'ne', // Nepali\n 'no', // Norwegian\n 'ny', // Nyanja (Chichewa)\n 'or', // Odia (Oriya)\n 'ps', // Pashto\n 'fa', // Persian\n 'pl', // Polish\n 'pt', // Portuguese\n 'pt_br', // Portuguese (Brazil)\n 'pa', // Punjabi\n 'ro', // Romanian\n 'ru', // Russian\n 'sm', // Samoan\n 'gd', // Scots Gaelic\n 'sr', // Serbian\n 'st', // Sesotho\n 'sn', // Shona\n 'sd', // Sindhi\n 'si', // Sinhala (Sinhalese)\n 'sk', // Slovak\n 'sl', // Slovenian\n 'so', // Somali\n 'es', // Spanish\n 'su', // Sundanese\n 'sw', // Swahili\n 'sv', // Swedish\n 'tl', // Tagalog (Filipino)\n 'tg', // Tajik\n 'ta', // Tamil\n 'tt', // Tatar\n 'te', // Telugu\n 'th', // Thai\n 'tr', // Turkish\n 'tk', // Turkmen\n 'uk', // Ukrainian\n 'ur', // Urdu\n 'ug', // Uyghur\n 'uz', // Uzbek\n 'vi', // Vietnamese\n 'cy', // Welsh\n 'xh', // Xhosa\n 'yi', // Yiddish\n 'yo', // Yoruba\n 'zu' // Zulu\n] as const;\n\n/**\n * All supported language codes (union of DeepL and Google Translate)\n */\nexport const SUPPORTED_LANGUAGES = Array.from(\n new Set([...DEEPL_LANGUAGES, ...GOOGLE_LANGUAGES])\n).sort() as readonly string[];\n\nexport type DeepLLanguage = (typeof DEEPL_LANGUAGES)[number];\nexport type GoogleLanguage = (typeof GOOGLE_LANGUAGES)[number];\nexport type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\n/**\n * JSON Schema for .translationsrc.json\n */\nexport const TRANSLATION_CONFIG_SCHEMA = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n title: 'Translation Configuration',\n description: 'Configuration for the translation management system',\n type: 'object',\n properties: {\n translationsPath: {\n type: 'string',\n description: 'Path to the translations directory relative to project root',\n default: 'public/static/locales'\n },\n languages: {\n type: 'array',\n description: 'List of language codes to support',\n items: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES\n },\n minItems: 1,\n uniqueItems: true\n },\n sourceLanguage: {\n type: 'string',\n description: 'Source language for translations (usually \"en\")',\n enum: SUPPORTED_LANGUAGES,\n default: 'en'\n },\n typesOutputPath: {\n type: 'string',\n description: 'Path to output TypeScript types file',\n default: 'src/types/i18nTypes.ts'\n },\n provider: {\n type: 'string',\n description: 'Translation provider to use (deepl or google)',\n enum: TRANSLATION_PROVIDERS,\n default: 'deepl'\n }\n },\n required: ['translationsPath', 'languages', 'sourceLanguage'],\n additionalProperties: false\n};\n\n/**\n * Validate if a language code is supported\n */\nexport function isValidLanguage(lang: string): lang is SupportedLanguage {\n return SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);\n}\n\n/**\n * Validate if a language is supported by DeepL\n */\nexport function isValidDeepLLanguage(lang: string): lang is DeepLLanguage {\n return DEEPL_LANGUAGES.includes(lang as DeepLLanguage);\n}\n\n/**\n * Validate if a language is supported by Google Translate\n */\nexport function isValidGoogleLanguage(lang: string): lang is GoogleLanguage {\n return GOOGLE_LANGUAGES.includes(lang as GoogleLanguage);\n}\n\n/**\n * Validate if a language is supported by a specific provider\n */\nexport function isValidLanguageForProvider(\n lang: string,\n provider: TranslationProviderType\n): boolean {\n switch (provider) {\n case 'deepl':\n return isValidDeepLLanguage(lang);\n case 'google':\n return isValidGoogleLanguage(lang);\n default:\n return false;\n }\n}\n\n/**\n * Validate languages array\n */\nexport function validateLanguages(languages: string[]): {\n valid: boolean;\n invalid: string[];\n} {\n const invalid = languages.filter((lang) => !isValidLanguage(lang));\n return {\n valid: invalid.length === 0,\n invalid\n };\n}\n\n/**\n * Validate languages array for a specific provider\n */\nexport function validateLanguagesForProvider(\n languages: string[],\n provider: TranslationProviderType\n): {\n valid: boolean;\n invalid: string[];\n} {\n const invalid = languages.filter((lang) => !isValidLanguageForProvider(lang, provider));\n return {\n valid: invalid.length === 0,\n invalid\n };\n}\n\n/**\n * Get supported languages for a specific provider\n */\nexport function getSupportedLanguages(provider: TranslationProviderType): readonly string[] {\n switch (provider) {\n case 'deepl':\n return DEEPL_LANGUAGES;\n case 'google':\n return GOOGLE_LANGUAGES;\n default:\n return [];\n }\n}\n","export interface TranslationConfig {\n /** Path to the translations directory (default: public/static/locales) */\n translationsPath?: string;\n /** Languages to support (default: ['en']) */\n languages?: string[];\n /** Source language for translations (default: 'en') */\n sourceLanguage?: string;\n /** Path to output i18n types (default: src/types/i18nTypes.ts) */\n typesOutputPath?: string;\n}\n\nexport interface TranslationEntry {\n namespace: string;\n key: string;\n value: string;\n}\n\nexport interface TranslationFile {\n [key: string]: string;\n}\n\nexport interface TranslationFiles {\n [namespace: string]: TranslationFile;\n}\n\nexport interface MissingTranslation {\n namespace: string;\n key: string;\n language: string;\n sourceValue: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n missing: MissingTranslation[];\n empty: MissingTranslation[];\n}\n\nexport const DEFAULT_CONFIG: Required<TranslationConfig> = {\n translationsPath: 'public/static/locales',\n languages: ['en'],\n sourceLanguage: 'en',\n typesOutputPath: 'src/types/i18nTypes.ts'\n};\n\nexport const DEFAULT_LANGUAGES = [\n 'en',\n 'fr',\n 'it',\n 'pl',\n 'es',\n 'pt',\n 'de',\n 'de_at',\n 'nl',\n 'sv',\n 'hu',\n 'cs',\n 'ja',\n 'zh_hk'\n] as const;\n","#!/usr/bin/env node\nimport { validateTranslations } from '../translations/cli/validate.js';\n\n/**\n * Script to verify that all translations have no missing keys\n * Exits with error code 1 if there are any missing or empty translations\n * This is useful for CI/CD pipelines to ensure translation completeness\n */\nfunction verifyTranslations(): void {\n const projectRoot = process.cwd();\n\n try {\n const result = validateTranslations(projectRoot);\n\n if (!result.valid) {\n console.error('\\n❌ Translation verification failed!');\n console.error(\n `Found ${result.missing.length} missing translations and ${result.empty.length} empty translations.`\n );\n console.error('Please fix these issues before proceeding.\\n');\n process.exit(1);\n }\n\n console.log('\\n✅ All translations verified successfully!');\n process.exit(0);\n } catch (error) {\n console.error('\\n❌ Error during translation verification:');\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n\nverifyTranslations();\n"],"mappings":";;;;AAAA,YAAYA,WAAU;;;ACAtB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAMf,SAAS,iBAAiB,kBAA0B,UAAoC;AAC7F,QAAM,WAAgB,UAAK,kBAAkB,QAAQ;AAErD,MAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAW,eAAY,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AACxE,QAAM,eAAiC,CAAC;AAExC,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAiB,cAAS,MAAM,OAAO;AAC7C,UAAM,WAAgB,UAAK,UAAU,IAAI;AACzC,UAAM,UAAa,gBAAa,UAAU,OAAO;AACjD,iBAAa,SAAS,IAAI,KAAK,MAAM,OAAO;AAAA,EAC9C;AAEA,SAAO;AACT;AAwBO,SAAS,sBAAsB,kBAAoC;AACxE,MAAI,CAAI,cAAW,gBAAgB,GAAG;AACpC,WAAO,CAAC;AAAA,EACV;AAEA,SACG,eAAY,kBAAkB,EAAE,eAAe,KAAK,CAAC,EACrD,OAAO,CAAC,WAAW,OAAO,YAAY,CAAC,EACvC,IAAI,CAAC,WAAW,OAAO,IAAI;AAChC;AAKO,SAAS,cAAc,kBAA0B,UAA4B;AAClF,QAAM,WAAgB,UAAK,kBAAkB,QAAQ;AAErD,MAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,WAAO,CAAC;AAAA,EACV;AAEA,SACG,eAAY,QAAQ,EACpB,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,EACjC,IAAI,CAAC,MAAW,cAAS,GAAG,OAAO,CAAC;AACzC;;;AC1EA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;;;ACSf,IAAM,kBAAkB;AAAA,EAC7B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAOO,IAAM,mBAAmB;AAAA,EAC9B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAA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","names":["path","fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/translations/cli/validate.ts","../../src/translations/utils/utils.ts","../../src/translations/cli/init.ts","../../src/translations/core/schema.ts","../../src/translations/core/types.ts","../../src/scripts/verify-translations.ts"],"sourcesContent":["import * as path from 'node:path';\nimport type { MissingTranslation, ValidationResult } from '../core/types.js';\nimport { getAvailableLanguages, getNamespaces, readTranslations } from '../utils/utils.js';\nimport { loadConfig } from './init.js';\n\n/**\n * Validate all translations against the source language\n * Checks for missing keys and empty values\n */\nexport function validateTranslations(projectRoot: string = process.cwd()): ValidationResult {\n const config = loadConfig(projectRoot);\n const translationsPath = path.join(projectRoot, config.translationsPath);\n const sourceLanguage = config.sourceLanguage;\n\n const missing: MissingTranslation[] = [];\n const empty: MissingTranslation[] = [];\n\n // Read source translations\n const sourceTranslations = readTranslations(translationsPath, sourceLanguage);\n const sourceNamespaces = getNamespaces(translationsPath, sourceLanguage);\n\n // Get all available languages\n const languages = getAvailableLanguages(translationsPath).filter((lang) => lang !== sourceLanguage);\n\n console.log('=====');\n console.log('Validating translations');\n console.log('=====');\n console.log(`Source language: ${sourceLanguage}`);\n console.log(`Target languages: ${languages.join(', ')}`);\n console.log(`Namespaces: ${sourceNamespaces.join(', ')}`);\n console.log('=====');\n\n // Validate each language\n for (const language of languages) {\n const targetTranslations = readTranslations(translationsPath, language);\n\n // Check each namespace\n for (const namespace of sourceNamespaces) {\n const sourceKeys = sourceTranslations[namespace] || {};\n const targetKeys = targetTranslations[namespace] || {};\n\n // Check for missing or empty translations\n for (const [key, sourceValue] of Object.entries(sourceKeys)) {\n const targetValue = targetKeys[key];\n\n // Missing key in target language\n if (targetValue === undefined) {\n missing.push({\n namespace,\n key,\n language,\n sourceValue\n });\n }\n // Empty value in target language\n else if (typeof targetValue === 'string' && targetValue.trim() === '') {\n empty.push({\n namespace,\n key,\n language,\n sourceValue\n });\n }\n }\n }\n }\n\n const valid = missing.length === 0 && empty.length === 0;\n\n if (valid) {\n console.log('✓ All translations are valid!');\n } else {\n if (missing.length > 0) {\n console.log(`\\n⚠ Found ${missing.length} missing translations:`);\n for (const item of missing.slice(0, 10)) {\n console.log(` ${item.language}/${item.namespace}.json -> ${item.key}`);\n }\n if (missing.length > 10) {\n console.log(` ... and ${missing.length - 10} more`);\n }\n }\n\n if (empty.length > 0) {\n console.log(`\\n⚠ Found ${empty.length} empty translations:`);\n for (const item of empty.slice(0, 10)) {\n console.log(` ${item.language}/${item.namespace}.json -> ${item.key}`);\n }\n if (empty.length > 10) {\n console.log(` ... and ${empty.length - 10} more`);\n }\n }\n }\n\n console.log('=====');\n\n return { valid, missing, empty };\n}\n\n/**\n * Get all missing or empty translations for a specific language\n */\nexport function getMissingForLanguage(\n projectRoot: string,\n language: string\n): Array<MissingTranslation & { type: 'missing' | 'empty' }> {\n const result = validateTranslations(projectRoot);\n const items = [\n ...result.missing.filter((m) => m.language === language).map((m) => ({ ...m, type: 'missing' as const })),\n ...result.empty.filter((e) => e.language === language).map((e) => ({ ...e, type: 'empty' as const }))\n ];\n\n return items;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { TranslationFile, TranslationFiles } from '../core/types.js';\n\n/**\n * Read all translation files for a specific language\n */\nexport function readTranslations(translationsPath: string, language: string): TranslationFiles {\n const langPath = path.join(translationsPath, language);\n\n if (!fs.existsSync(langPath)) {\n return {};\n }\n\n const files = fs.readdirSync(langPath).filter((f) => f.endsWith('.json'));\n const translations: TranslationFiles = {};\n\n for (const file of files) {\n const namespace = path.basename(file, '.json');\n const filePath = path.join(langPath, file);\n const content = fs.readFileSync(filePath, 'utf-8');\n translations[namespace] = JSON.parse(content) as TranslationFile;\n }\n\n return translations;\n}\n\n/**\n * Write translation file for a specific language and namespace\n */\nexport function writeTranslation(\n translationsPath: string,\n language: string,\n namespace: string,\n translations: TranslationFile\n): void {\n const langPath = path.join(translationsPath, language);\n\n if (!fs.existsSync(langPath)) {\n fs.mkdirSync(langPath, { recursive: true });\n }\n\n const filePath = path.join(langPath, `${namespace}.json`);\n fs.writeFileSync(filePath, `${JSON.stringify(translations, null, 2)}\\n`, 'utf-8');\n}\n\n/**\n * Get all available languages from the translations directory\n */\nexport function getAvailableLanguages(translationsPath: string): string[] {\n if (!fs.existsSync(translationsPath)) {\n return [];\n }\n\n return fs\n .readdirSync(translationsPath, { withFileTypes: true })\n .filter((dirent) => dirent.isDirectory())\n .map((dirent) => dirent.name);\n}\n\n/**\n * Get all namespaces for a specific language\n */\nexport function getNamespaces(translationsPath: string, language: string): string[] {\n const langPath = path.join(translationsPath, language);\n\n if (!fs.existsSync(langPath)) {\n return [];\n }\n\n return fs\n .readdirSync(langPath)\n .filter((f) => f.endsWith('.json'))\n .map((f) => path.basename(f, '.json'));\n}\n\n/**\n * Check if a string contains interpolation variables (e.g., {{variable}})\n */\nexport function hasInterpolation(text: string): boolean {\n return /\\{\\{[^}]+\\}\\}/g.test(text);\n}\n\n/**\n * Extract interpolation variable names from a string\n */\nexport function extractVariables(text: string): string[] {\n const matches = text.match(/\\{\\{([^}]+)\\}\\}/g);\n if (!matches) return [];\n return matches.map((match) => match.replace(/\\{\\{|\\}\\}/g, '').trim());\n}\n\n/**\n * Validate that translated text has the same variables as source text\n */\nexport function validateVariables(sourceText: string, translatedText: string): boolean {\n const sourceVars = extractVariables(sourceText).sort();\n const translatedVars = extractVariables(translatedText).sort();\n\n if (sourceVars.length !== translatedVars.length) {\n return false;\n }\n\n return sourceVars.every((v, i) => v === translatedVars[i]);\n}\n\n/**\n * Sort object keys alphabetically\n */\nexport function sortKeys<T extends Record<string, unknown>>(obj: T): T {\n const sorted = {} as T;\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n sorted[key as keyof T] = obj[key as keyof T];\n }\n return sorted;\n}\n\n/**\n * Ensure translations path exists and has proper structure\n */\nexport function ensureTranslationsStructure(translationsPath: string, languages: string[]): void {\n if (!fs.existsSync(translationsPath)) {\n fs.mkdirSync(translationsPath, { recursive: true });\n }\n\n for (const lang of languages) {\n const langPath = path.join(translationsPath, lang);\n if (!fs.existsSync(langPath)) {\n fs.mkdirSync(langPath, { recursive: true });\n }\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { validateLanguages } from '../core/schema.js';\nimport type { TranslationConfig } from '../core/types.js';\nimport { DEFAULT_CONFIG, DEFAULT_LANGUAGES } from '../core/types.js';\nimport { ensureTranslationsStructure, getAvailableLanguages } from '../utils/utils.js';\n\n/**\n * Detect existing translation structure in common locations\n */\nexport function detectExistingTranslations(projectRoot: string): {\n path: string | null;\n languages: string[];\n} {\n const possiblePaths = ['public/static/locales', 'public/locales', 'src/locales', 'locales', 'i18n', 'translations'];\n\n for (const possiblePath of possiblePaths) {\n const fullPath = path.join(projectRoot, possiblePath);\n if (fs.existsSync(fullPath)) {\n const languages = getAvailableLanguages(fullPath);\n if (languages.length > 0) {\n return { path: possiblePath, languages };\n }\n }\n }\n\n return { path: null, languages: [] };\n}\n\n/**\n * Initialize translation structure for a project\n */\nexport function initTranslations(projectRoot: string, config: TranslationConfig = {}): void {\n console.log('=====');\n console.log('Initializing translation structure');\n console.log('=====');\n\n // Detect existing translations\n const existing = detectExistingTranslations(projectRoot);\n\n let finalConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Use detected path and languages if found\n if (existing.path && existing.languages.length > 0) {\n console.log(`✓ Detected existing translations at: ${existing.path}`);\n console.log(`✓ Found languages: ${existing.languages.join(', ')}`);\n\n // Validate detected languages\n const validation = validateLanguages(existing.languages);\n if (!validation.valid) {\n console.log(`⚠️ Warning: Invalid language codes found: ${validation.invalid.join(', ')}`);\n console.log('These languages will be skipped.');\n }\n\n const validLanguages = existing.languages.filter((lang) => !validation.invalid.includes(lang));\n\n finalConfig = {\n ...finalConfig,\n translationsPath: existing.path,\n languages: validLanguages.length > 0 ? validLanguages : finalConfig.languages\n };\n }\n\n const translationsPath = path.join(projectRoot, finalConfig.translationsPath);\n const languages = finalConfig.languages.length > 0 ? finalConfig.languages : [...DEFAULT_LANGUAGES];\n\n console.log(`Project root: ${projectRoot}`);\n console.log(`Translations path: ${translationsPath}`);\n console.log(`Languages: ${languages.join(', ')}`);\n console.log('=====');\n\n // Validate languages\n const validation = validateLanguages(languages);\n if (!validation.valid) {\n throw new Error(`Invalid language codes: ${validation.invalid.join(', ')}`);\n }\n\n // Create directory structure\n ensureTranslationsStructure(translationsPath, languages);\n\n // Create sample translation files for English (source language)\n const sourceLanguage = finalConfig.sourceLanguage;\n const sourcePath = path.join(translationsPath, sourceLanguage);\n\n // Create a sample common.json if it doesn't exist\n const commonPath = path.join(sourcePath, 'common.json');\n if (!fs.existsSync(commonPath)) {\n const sampleTranslations = {\n LOADING: 'Loading',\n SAVE: 'Save',\n CANCEL: 'Cancel',\n SUBMIT: 'Submit',\n ERROR: 'Error',\n SUCCESS: 'Success'\n };\n\n fs.writeFileSync(commonPath, `${JSON.stringify(sampleTranslations, null, 2)}\\n`, 'utf-8');\n console.log(`Created sample file: ${commonPath}`);\n }\n\n // Create empty translation files for other languages\n for (const lang of languages) {\n if (lang === sourceLanguage) continue;\n\n const langCommonPath = path.join(translationsPath, lang, 'common.json');\n if (!fs.existsSync(langCommonPath)) {\n fs.writeFileSync(langCommonPath, '{}\\n', 'utf-8');\n console.log(`Created empty file: ${langCommonPath}`);\n }\n }\n\n // Create config file with schema reference\n const configPath = path.join(projectRoot, '.translationsrc.json');\n if (!fs.existsSync(configPath)) {\n const configContent = {\n $schema: './node_modules/poly-lexis/dist/translations/core/translations-config.schema.json',\n translationsPath: finalConfig.translationsPath,\n languages,\n sourceLanguage,\n typesOutputPath: finalConfig.typesOutputPath\n };\n\n fs.writeFileSync(configPath, `${JSON.stringify(configContent, null, 2)}\\n`, 'utf-8');\n console.log(`Created config file: ${configPath}`);\n }\n\n console.log('=====');\n console.log('Translation structure initialized successfully!');\n console.log('=====');\n}\n\n/**\n * Load translation configuration from .translationsrc.json\n */\nexport function loadConfig(projectRoot: string): Required<TranslationConfig> {\n const configPath = path.join(projectRoot, '.translationsrc.json');\n\n if (!fs.existsSync(configPath)) {\n // Try to detect existing translations\n const existing = detectExistingTranslations(projectRoot);\n if (existing.path && existing.languages.length > 0) {\n console.log(`ℹ️ No config found, but detected translations at ${existing.path}`);\n return {\n ...DEFAULT_CONFIG,\n translationsPath: existing.path,\n languages: existing.languages\n };\n }\n return DEFAULT_CONFIG;\n }\n\n const configContent = fs.readFileSync(configPath, 'utf-8');\n const config = JSON.parse(configContent) as TranslationConfig;\n\n // Validate languages in config\n if (config.languages) {\n const validation = validateLanguages(config.languages);\n if (!validation.valid) {\n console.warn(`⚠️ Warning: Invalid language codes in config: ${validation.invalid.join(', ')}`);\n console.warn('Please update .translationsrc.json with valid language codes.');\n }\n }\n\n return { ...DEFAULT_CONFIG, ...config };\n}\n","/**\n * Translation provider types\n */\nexport const TRANSLATION_PROVIDERS = ['deepl', 'google'] as const;\nexport type TranslationProviderType = (typeof TRANSLATION_PROVIDERS)[number];\n\n/**\n * DeepL supported target languages\n * Based on DeepL API v2 documentation\n */\nexport const DEEPL_LANGUAGES = [\n 'ar', // Arabic\n 'bg', // Bulgarian\n 'cs', // Czech\n 'da', // Danish\n 'de', // German\n 'el', // Greek\n 'en', // English (unspecified)\n 'en_gb', // English (British)\n 'en_us', // English (American)\n 'es', // Spanish\n 'es_419', // Spanish (Latin American)\n 'et', // Estonian\n 'fi', // Finnish\n 'fr', // French\n 'he', // Hebrew (next-gen models only)\n 'hu', // Hungarian\n 'id', // Indonesian\n 'it', // Italian\n 'ja', // Japanese\n 'ko', // Korean\n 'lt', // Lithuanian\n 'lv', // Latvian\n 'nb', // Norwegian Bokmål\n 'nl', // Dutch\n 'pl', // Polish\n 'pt', // Portuguese (unspecified)\n 'pt_br', // Portuguese (Brazilian)\n 'pt_pt', // Portuguese (excluding Brazilian)\n 'ro', // Romanian\n 'ru', // Russian\n 'sk', // Slovak\n 'sl', // Slovenian\n 'sv', // Swedish\n 'th', // Thai (next-gen models only)\n 'tr', // Turkish\n 'uk', // Ukrainian\n 'vi', // Vietnamese (next-gen models only)\n 'zh', // Chinese (unspecified)\n 'zh_hans', // Chinese (simplified)\n 'zh_hant' // Chinese (traditional)\n] as const;\n\n/**\n * Google Translate supported languages\n * Based on Google Cloud Translation API v2\n * Reference: https://docs.cloud.google.com/translate/docs/languages\n */\nexport const GOOGLE_LANGUAGES = [\n 'af', // Afrikaans\n 'sq', // Albanian\n 'am', // Amharic\n 'ar', // Arabic\n 'hy', // Armenian\n 'az', // Azerbaijani\n 'eu', // Basque\n 'be', // Belarusian\n 'bn', // Bengali\n 'bs', // Bosnian\n 'bg', // Bulgarian\n 'ca', // Catalan\n 'ceb', // Cebuano\n 'zh', // Chinese (Simplified)\n 'zh_cn', // Chinese (Simplified)\n 'zh_tw', // Chinese (Traditional)\n 'co', // Corsican\n 'hr', // Croatian\n 'cs', // Czech\n 'da', // Danish\n 'nl', // Dutch\n 'en', // English\n 'eo', // Esperanto\n 'et', // Estonian\n 'fi', // Finnish\n 'fr', // French\n 'fy', // Frisian\n 'gl', // Galician\n 'ka', // Georgian\n 'de', // German\n 'el', // Greek\n 'gu', // Gujarati\n 'ht', // Haitian Creole\n 'ha', // Hausa\n 'haw', // Hawaiian\n 'he', // Hebrew\n 'hi', // Hindi\n 'hmn', // Hmong\n 'hu', // Hungarian\n 'is', // Icelandic\n 'ig', // Igbo\n 'id', // Indonesian\n 'ga', // Irish\n 'it', // Italian\n 'ja', // Japanese\n 'jv', // Javanese\n 'kn', // Kannada\n 'kk', // Kazakh\n 'km', // Khmer\n 'rw', // Kinyarwanda\n 'ko', // Korean\n 'ku', // Kurdish\n 'ky', // Kyrgyz\n 'lo', // Lao\n 'la', // Latin\n 'lv', // Latvian\n 'lt', // Lithuanian\n 'lb', // Luxembourgish\n 'mk', // Macedonian\n 'mg', // Malagasy\n 'ms', // Malay\n 'ml', // Malayalam\n 'mt', // Maltese\n 'mi', // Maori\n 'mr', // Marathi\n 'mn', // Mongolian\n 'my', // Myanmar (Burmese)\n 'ne', // Nepali\n 'no', // Norwegian\n 'ny', // Nyanja (Chichewa)\n 'or', // Odia (Oriya)\n 'ps', // Pashto\n 'fa', // Persian\n 'pl', // Polish\n 'pt', // Portuguese\n 'pt_br', // Portuguese (Brazil)\n 'pa', // Punjabi\n 'ro', // Romanian\n 'ru', // Russian\n 'sm', // Samoan\n 'gd', // Scots Gaelic\n 'sr', // Serbian\n 'st', // Sesotho\n 'sn', // Shona\n 'sd', // Sindhi\n 'si', // Sinhala (Sinhalese)\n 'sk', // Slovak\n 'sl', // Slovenian\n 'so', // Somali\n 'es', // Spanish\n 'su', // Sundanese\n 'sw', // Swahili\n 'sv', // Swedish\n 'tl', // Tagalog (Filipino)\n 'tg', // Tajik\n 'ta', // Tamil\n 'tt', // Tatar\n 'te', // Telugu\n 'th', // Thai\n 'tr', // Turkish\n 'tk', // Turkmen\n 'uk', // Ukrainian\n 'ur', // Urdu\n 'ug', // Uyghur\n 'uz', // Uzbek\n 'vi', // Vietnamese\n 'cy', // Welsh\n 'xh', // Xhosa\n 'yi', // Yiddish\n 'yo', // Yoruba\n 'zu' // Zulu\n] as const;\n\n/**\n * All supported language codes (union of DeepL and Google Translate)\n */\nexport const SUPPORTED_LANGUAGES = Array.from(\n new Set([...DEEPL_LANGUAGES, ...GOOGLE_LANGUAGES])\n).sort() as readonly string[];\n\nexport type DeepLLanguage = (typeof DEEPL_LANGUAGES)[number];\nexport type GoogleLanguage = (typeof GOOGLE_LANGUAGES)[number];\nexport type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\n/**\n * JSON Schema for .translationsrc.json\n */\nexport const TRANSLATION_CONFIG_SCHEMA = {\n title: 'Translation Configuration',\n description: 'Configuration for the translation management system',\n type: 'object',\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema reference'\n },\n translationsPath: {\n type: 'string',\n description: 'Path to the translations directory relative to project root',\n default: 'public/static/locales',\n examples: ['public/static/locales', 'src/locales', 'locales']\n },\n languages: {\n type: 'array',\n description: 'List of language codes to support',\n items: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES\n },\n minItems: 1,\n uniqueItems: true,\n default: ['en']\n },\n sourceLanguage: {\n type: 'string',\n description: 'Source language for translations (usually \"en\")',\n enum: SUPPORTED_LANGUAGES,\n default: 'en'\n },\n typesOutputPath: {\n type: 'string',\n description: 'Path to output TypeScript types file',\n default: 'src/types/i18nTypes.ts',\n examples: ['src/types/i18nTypes.ts', 'src/types/translations.ts']\n },\n provider: {\n type: 'string',\n description: 'Translation provider to use (deepl or google)',\n enum: TRANSLATION_PROVIDERS,\n default: 'deepl'\n }\n },\n required: ['translationsPath', 'languages', 'sourceLanguage'],\n additionalProperties: false\n};\n\n/**\n * Validate if a language code is supported\n */\nexport function isValidLanguage(lang: string): lang is SupportedLanguage {\n return SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);\n}\n\n/**\n * Validate if a language is supported by DeepL\n */\nexport function isValidDeepLLanguage(lang: string): lang is DeepLLanguage {\n return DEEPL_LANGUAGES.includes(lang as DeepLLanguage);\n}\n\n/**\n * Validate if a language is supported by Google Translate\n */\nexport function isValidGoogleLanguage(lang: string): lang is GoogleLanguage {\n return GOOGLE_LANGUAGES.includes(lang as GoogleLanguage);\n}\n\n/**\n * Validate if a language is supported by a specific provider\n */\nexport function isValidLanguageForProvider(lang: string, provider: TranslationProviderType): boolean {\n switch (provider) {\n case 'deepl':\n return isValidDeepLLanguage(lang);\n case 'google':\n return isValidGoogleLanguage(lang);\n default:\n return false;\n }\n}\n\n/**\n * Validate languages array\n */\nexport function validateLanguages(languages: string[]): {\n valid: boolean;\n invalid: string[];\n} {\n const invalid = languages.filter((lang) => !isValidLanguage(lang));\n return {\n valid: invalid.length === 0,\n invalid\n };\n}\n\n/**\n * Validate languages array for a specific provider\n */\nexport function validateLanguagesForProvider(\n languages: string[],\n provider: TranslationProviderType\n): {\n valid: boolean;\n invalid: string[];\n} {\n const invalid = languages.filter((lang) => !isValidLanguageForProvider(lang, provider));\n return {\n valid: invalid.length === 0,\n invalid\n };\n}\n\n/**\n * Get supported languages for a specific provider\n */\nexport function getSupportedLanguages(provider: TranslationProviderType): readonly string[] {\n switch (provider) {\n case 'deepl':\n return DEEPL_LANGUAGES;\n case 'google':\n return GOOGLE_LANGUAGES;\n default:\n return [];\n }\n}\n","export interface TranslationConfig {\n /** Path to the translations directory (default: public/static/locales) */\n translationsPath?: string;\n /** Languages to support (default: ['en']) */\n languages?: string[];\n /** Source language for translations (default: 'en') */\n sourceLanguage?: string;\n /** Path to output i18n types (default: src/types/i18nTypes.ts) */\n typesOutputPath?: string;\n}\n\nexport interface TranslationEntry {\n namespace: string;\n key: string;\n value: string;\n}\n\nexport interface TranslationFile {\n [key: string]: string;\n}\n\nexport interface TranslationFiles {\n [namespace: string]: TranslationFile;\n}\n\nexport interface MissingTranslation {\n namespace: string;\n key: string;\n language: string;\n sourceValue: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n missing: MissingTranslation[];\n empty: MissingTranslation[];\n}\n\nexport const DEFAULT_CONFIG: Required<TranslationConfig> = {\n translationsPath: 'public/static/locales',\n languages: ['en'],\n sourceLanguage: 'en',\n typesOutputPath: 'src/types/i18nTypes.ts'\n};\n\nexport const DEFAULT_LANGUAGES = [\n 'en',\n 'fr',\n 'it',\n 'pl',\n 'es',\n 'pt',\n 'de',\n 'de_at',\n 'nl',\n 'sv',\n 'hu',\n 'cs',\n 'ja',\n 'zh_hk'\n] as const;\n","#!/usr/bin/env node\nimport { validateTranslations } from '../translations/cli/validate.js';\n\n/**\n * Script to verify that all translations have no missing keys\n * Exits with error code 1 if there are any missing or empty translations\n * This is useful for CI/CD pipelines to ensure translation completeness\n */\nfunction verifyTranslations(): void {\n const projectRoot = process.cwd();\n\n try {\n const result = validateTranslations(projectRoot);\n\n if (!result.valid) {\n console.error('\\n❌ Translation verification failed!');\n console.error(\n `Found ${result.missing.length} missing translations and ${result.empty.length} empty translations.`\n );\n console.error('Please fix these issues before proceeding.\\n');\n process.exit(1);\n }\n\n console.log('\\n✅ All translations verified successfully!');\n process.exit(0);\n } catch (error) {\n console.error('\\n❌ Error during translation verification:');\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n\nverifyTranslations();\n"],"mappings":";;;;AAAA,YAAYA,WAAU;;;ACAtB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAMf,SAAS,iBAAiB,kBAA0B,UAAoC;AAC7F,QAAM,WAAgB,UAAK,kBAAkB,QAAQ;AAErD,MAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAW,eAAY,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AACxE,QAAM,eAAiC,CAAC;AAExC,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAiB,cAAS,MAAM,OAAO;AAC7C,UAAM,WAAgB,UAAK,UAAU,IAAI;AACzC,UAAM,UAAa,gBAAa,UAAU,OAAO;AACjD,iBAAa,SAAS,IAAI,KAAK,MAAM,OAAO;AAAA,EAC9C;AAEA,SAAO;AACT;AAwBO,SAAS,sBAAsB,kBAAoC;AACxE,MAAI,CAAI,cAAW,gBAAgB,GAAG;AACpC,WAAO,CAAC;AAAA,EACV;AAEA,SACG,eAAY,kBAAkB,EAAE,eAAe,KAAK,CAAC,EACrD,OAAO,CAAC,WAAW,OAAO,YAAY,CAAC,EACvC,IAAI,CAAC,WAAW,OAAO,IAAI;AAChC;AAKO,SAAS,cAAc,kBAA0B,UAA4B;AAClF,QAAM,WAAgB,UAAK,kBAAkB,QAAQ;AAErD,MAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,WAAO,CAAC;AAAA,EACV;AAEA,SACG,eAAY,QAAQ,EACpB,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,EACjC,IAAI,CAAC,MAAW,cAAS,GAAG,OAAO,CAAC;AACzC;;;AC1EA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;;;ACSf,IAAM,kBAAkB;AAAA,EAC7B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAOO,IAAM,mBAAmB;AAAA,EAC9B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAA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","names":["path","fs","path"]}
|