@intl-party/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/index.js ADDED
@@ -0,0 +1,885 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ checkCommand: () => checkCommand,
34
+ extractCommand: () => extractCommand,
35
+ generateCommand: () => generateCommand,
36
+ initCommand: () => initCommand,
37
+ loadConfig: () => loadConfig,
38
+ loadTranslations: () => loadTranslations,
39
+ saveConfig: () => saveConfig,
40
+ saveTranslations: () => saveTranslations,
41
+ syncCommand: () => syncCommand,
42
+ validateCommand: () => validateCommand
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/commands/validate.ts
47
+ var import_chalk = __toESM(require("chalk"));
48
+ var import_ora = __toESM(require("ora"));
49
+ var import_fs_extra3 = __toESM(require("fs-extra"));
50
+ var import_core = require("@intl-party/core");
51
+
52
+ // src/utils/config.ts
53
+ var import_fs_extra = __toESM(require("fs-extra"));
54
+ var import_path = __toESM(require("path"));
55
+ var DEFAULT_CONFIG = {
56
+ locales: ["en", "es", "fr"],
57
+ defaultLocale: "en",
58
+ namespaces: ["common"],
59
+ translationPaths: {},
60
+ sourcePatterns: ["src/**/*.{ts,tsx,js,jsx}"],
61
+ outputDir: "./translations",
62
+ validation: {
63
+ strict: false,
64
+ logMissing: true,
65
+ throwOnMissing: false,
66
+ validateFormats: true
67
+ },
68
+ extraction: {
69
+ keyPrefix: "",
70
+ markExtracted: true,
71
+ sortKeys: true,
72
+ includeMetadata: false
73
+ },
74
+ sync: {
75
+ removeUnused: false,
76
+ addMissing: true,
77
+ preserveOrder: true
78
+ }
79
+ };
80
+ async function loadConfig(configPath) {
81
+ const configFiles = [
82
+ configPath,
83
+ "intl-party.config.js",
84
+ "intl-party.config.ts",
85
+ "intl-party.config.json",
86
+ ".intl-party.config.js",
87
+ ".intl-party.config.ts",
88
+ ".intl-party.config.json"
89
+ ].filter(Boolean);
90
+ for (const configFile of configFiles) {
91
+ if (configFile && await import_fs_extra.default.pathExists(configFile)) {
92
+ try {
93
+ let config;
94
+ if (configFile.endsWith(".json")) {
95
+ const content = await import_fs_extra.default.readFile(configFile, "utf-8");
96
+ config = JSON.parse(content);
97
+ } else {
98
+ delete require.cache[import_path.default.resolve(configFile)];
99
+ config = require(import_path.default.resolve(configFile));
100
+ if (config.default) {
101
+ config = config.default;
102
+ }
103
+ }
104
+ return mergeConfig(DEFAULT_CONFIG, config);
105
+ } catch (error) {
106
+ throw new Error(
107
+ `Failed to load config from ${configFile}: ${error instanceof Error ? error.message : error}`
108
+ );
109
+ }
110
+ }
111
+ }
112
+ const packageJsonPath = "package.json";
113
+ if (await import_fs_extra.default.pathExists(packageJsonPath)) {
114
+ try {
115
+ const packageJson = await import_fs_extra.default.readJson(packageJsonPath);
116
+ if (packageJson["intl-party"]) {
117
+ return mergeConfig(DEFAULT_CONFIG, packageJson["intl-party"]);
118
+ }
119
+ } catch {
120
+ }
121
+ }
122
+ const autoDetected = await autoDetectConfig();
123
+ return mergeConfig(DEFAULT_CONFIG, autoDetected);
124
+ }
125
+ async function autoDetectConfig() {
126
+ const config = {};
127
+ const commonPaths = [
128
+ "translations",
129
+ "locales",
130
+ "i18n",
131
+ "public/locales",
132
+ "src/locales",
133
+ "src/translations",
134
+ "assets/locales"
135
+ ];
136
+ for (const basePath of commonPaths) {
137
+ if (await import_fs_extra.default.pathExists(basePath)) {
138
+ try {
139
+ const entries = await import_fs_extra.default.readdir(basePath);
140
+ const locales = entries.filter(
141
+ (entry) => import_fs_extra.default.statSync(import_path.default.join(basePath, entry)).isDirectory()
142
+ );
143
+ if (locales.length > 0) {
144
+ config.locales = locales;
145
+ config.translationPaths = {};
146
+ const firstLocaleDir = import_path.default.join(basePath, locales[0]);
147
+ const namespaceFiles = await import_fs_extra.default.readdir(firstLocaleDir);
148
+ const namespaces = namespaceFiles.filter((file) => file.endsWith(".json")).map((file) => import_path.default.basename(file, ".json"));
149
+ if (namespaces.length > 0) {
150
+ config.namespaces = namespaces;
151
+ for (const locale of locales) {
152
+ config.translationPaths[locale] = {};
153
+ for (const namespace of namespaces) {
154
+ config.translationPaths[locale][namespace] = import_path.default.join(
155
+ basePath,
156
+ locale,
157
+ `${namespace}.json`
158
+ );
159
+ }
160
+ }
161
+ }
162
+ break;
163
+ }
164
+ } catch {
165
+ }
166
+ }
167
+ }
168
+ return config;
169
+ }
170
+ function mergeConfig(defaultConfig, userConfig) {
171
+ return {
172
+ ...defaultConfig,
173
+ ...userConfig,
174
+ validation: {
175
+ ...defaultConfig.validation,
176
+ ...userConfig.validation
177
+ },
178
+ extraction: {
179
+ ...defaultConfig.extraction,
180
+ ...userConfig.extraction
181
+ },
182
+ sync: {
183
+ ...defaultConfig.sync,
184
+ ...userConfig.sync
185
+ },
186
+ translationPaths: {
187
+ ...defaultConfig.translationPaths,
188
+ ...userConfig.translationPaths
189
+ }
190
+ };
191
+ }
192
+ async function saveConfig(config, configPath = "intl-party.config.json") {
193
+ await import_fs_extra.default.writeFile(configPath, JSON.stringify(config, null, 2));
194
+ }
195
+
196
+ // src/utils/translations.ts
197
+ var import_fs_extra2 = __toESM(require("fs-extra"));
198
+ var import_path2 = __toESM(require("path"));
199
+ async function loadTranslations(translationPaths, locales, namespaces) {
200
+ const translations = {};
201
+ for (const locale of locales) {
202
+ translations[locale] = {};
203
+ for (const namespace of namespaces) {
204
+ const translationPath = translationPaths[locale]?.[namespace];
205
+ if (translationPath && await import_fs_extra2.default.pathExists(translationPath)) {
206
+ try {
207
+ const content = await import_fs_extra2.default.readJson(translationPath);
208
+ translations[locale][namespace] = content;
209
+ } catch (error) {
210
+ console.warn(
211
+ `Failed to load ${locale}/${namespace} from ${translationPath}`
212
+ );
213
+ translations[locale][namespace] = {};
214
+ }
215
+ } else {
216
+ translations[locale][namespace] = {};
217
+ }
218
+ }
219
+ }
220
+ return translations;
221
+ }
222
+ async function saveTranslations(translations, translationPaths) {
223
+ for (const [locale, localeTranslations] of Object.entries(translations)) {
224
+ for (const [namespace, namespaceTranslations] of Object.entries(
225
+ localeTranslations
226
+ )) {
227
+ const translationPath = translationPaths[locale]?.[namespace];
228
+ if (translationPath) {
229
+ await import_fs_extra2.default.ensureDir(import_path2.default.dirname(translationPath));
230
+ await import_fs_extra2.default.writeJson(translationPath, namespaceTranslations, {
231
+ spaces: 2
232
+ });
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ // src/commands/validate.ts
239
+ async function validateCommand(options) {
240
+ const spinner = (0, import_ora.default)("Loading configuration...").start();
241
+ try {
242
+ const config = await loadConfig(options.config);
243
+ spinner.succeed("Configuration loaded");
244
+ const targetLocales = options.locales || config.locales;
245
+ const targetNamespaces = options.namespaces || config.namespaces;
246
+ spinner.start("Loading translations...");
247
+ const translations = await loadTranslations(
248
+ config.translationPaths,
249
+ targetLocales,
250
+ targetNamespaces
251
+ );
252
+ spinner.succeed("Translations loaded");
253
+ spinner.start("Validating translations...");
254
+ const validationConfig = {
255
+ strict: options.strict || config.validation?.strict || false,
256
+ logMissing: options.verbose || false,
257
+ throwOnMissing: false,
258
+ validateFormats: true
259
+ };
260
+ const result = (0, import_core.validateTranslations)(
261
+ translations,
262
+ targetLocales,
263
+ targetNamespaces,
264
+ validationConfig
265
+ );
266
+ spinner.stop();
267
+ await outputResults(result, options);
268
+ if (!result.valid) {
269
+ process.exit(1);
270
+ }
271
+ } catch (error) {
272
+ spinner.fail("Validation failed");
273
+ console.error(
274
+ import_chalk.default.red("Error:"),
275
+ error instanceof Error ? error.message : error
276
+ );
277
+ process.exit(1);
278
+ }
279
+ }
280
+ async function outputResults(result, options) {
281
+ if (options.format === "json") {
282
+ const output = JSON.stringify(result, null, 2);
283
+ if (options.output) {
284
+ await import_fs_extra3.default.writeFile(options.output, output);
285
+ console.log(import_chalk.default.green("\u2713"), `Results written to ${options.output}`);
286
+ } else {
287
+ console.log(output);
288
+ }
289
+ return;
290
+ }
291
+ if (options.format === "junit") {
292
+ const junitXml = generateJUnitXML(result);
293
+ if (options.output) {
294
+ await import_fs_extra3.default.writeFile(options.output, junitXml);
295
+ console.log(
296
+ import_chalk.default.green("\u2713"),
297
+ `JUnit report written to ${options.output}`
298
+ );
299
+ } else {
300
+ console.log(junitXml);
301
+ }
302
+ return;
303
+ }
304
+ if (result.valid) {
305
+ console.log(import_chalk.default.green("\u2713 All translations are valid!"));
306
+ if (result.warnings.length > 0) {
307
+ console.log(import_chalk.default.yellow(`
308
+ \u26A0 ${result.warnings.length} warning(s):`));
309
+ result.warnings.forEach((warning, index) => {
310
+ console.log(
311
+ ` ${index + 1}. ${import_chalk.default.yellow(warning.type)}: ${warning.message}`
312
+ );
313
+ if (options.verbose) {
314
+ console.log(
315
+ ` Locale: ${warning.locale}, Namespace: ${warning.namespace}, Key: ${warning.key}`
316
+ );
317
+ }
318
+ });
319
+ }
320
+ } else {
321
+ console.log(
322
+ import_chalk.default.red(`\u2717 Validation failed with ${result.errors.length} error(s):`)
323
+ );
324
+ result.errors.forEach((error, index) => {
325
+ console.log(` ${index + 1}. ${import_chalk.default.red(error.type)}: ${error.message}`);
326
+ if (options.verbose) {
327
+ console.log(
328
+ ` Locale: ${error.locale}, Namespace: ${error.namespace}, Key: ${error.key}`
329
+ );
330
+ }
331
+ });
332
+ if (result.warnings.length > 0) {
333
+ console.log(import_chalk.default.yellow(`
334
+ \u26A0 ${result.warnings.length} warning(s):`));
335
+ result.warnings.forEach((warning, index) => {
336
+ console.log(
337
+ ` ${index + 1}. ${import_chalk.default.yellow(warning.type)}: ${warning.message}`
338
+ );
339
+ if (options.verbose) {
340
+ console.log(
341
+ ` Locale: ${warning.locale}, Namespace: ${warning.namespace}, Key: ${warning.key}`
342
+ );
343
+ }
344
+ });
345
+ }
346
+ }
347
+ const totalIssues = result.errors.length + result.warnings.length;
348
+ if (totalIssues > 0) {
349
+ console.log(
350
+ `
351
+ Summary: ${result.errors.length} errors, ${result.warnings.length} warnings`
352
+ );
353
+ }
354
+ }
355
+ function generateJUnitXML(result) {
356
+ const totalTests = 1;
357
+ const failures = result.valid ? 0 : 1;
358
+ const errors = result.errors.length;
359
+ let xml = `<?xml version="1.0" encoding="UTF-8"?>
360
+ `;
361
+ xml += `<testsuites name="intl-party-validation" tests="${totalTests}" failures="${failures}" errors="${errors}">
362
+ `;
363
+ xml += ` <testsuite name="translation-validation" tests="${totalTests}" failures="${failures}" errors="${errors}">
364
+ `;
365
+ if (result.valid) {
366
+ xml += ` <testcase name="validation" classname="translations" />
367
+ `;
368
+ } else {
369
+ xml += ` <testcase name="validation" classname="translations">
370
+ `;
371
+ xml += ` <failure message="Translation validation failed">
372
+ `;
373
+ xml += ` <![CDATA[
374
+ `;
375
+ result.errors.forEach((error) => {
376
+ xml += `${error.type}: ${error.message}
377
+ `;
378
+ xml += ` Locale: ${error.locale}, Namespace: ${error.namespace}, Key: ${error.key}
379
+
380
+ `;
381
+ });
382
+ xml += ` ]]>
383
+ `;
384
+ xml += ` </failure>
385
+ `;
386
+ xml += ` </testcase>
387
+ `;
388
+ }
389
+ xml += ` </testsuite>
390
+ `;
391
+ xml += `</testsuites>
392
+ `;
393
+ return xml;
394
+ }
395
+
396
+ // src/commands/extract.ts
397
+ var import_chalk2 = __toESM(require("chalk"));
398
+ var import_ora2 = __toESM(require("ora"));
399
+ var import_glob = require("glob");
400
+ var import_fs_extra4 = __toESM(require("fs-extra"));
401
+ var import_path3 = __toESM(require("path"));
402
+ async function extractCommand(options) {
403
+ const spinner = (0, import_ora2.default)("Extracting translation keys...").start();
404
+ try {
405
+ const sourcePatterns = options.source || ["src/**/*.{ts,tsx,js,jsx}"];
406
+ const outputDir = options.output || "./translations";
407
+ const files = await (0, import_glob.glob)(sourcePatterns);
408
+ spinner.succeed(`Found ${files.length} source files`);
409
+ const extractedKeys = /* @__PURE__ */ new Set();
410
+ for (const file of files) {
411
+ const content = await import_fs_extra4.default.readFile(file, "utf-8");
412
+ const keys = extractKeysFromContent(content);
413
+ keys.forEach((key) => extractedKeys.add(key));
414
+ }
415
+ spinner.succeed(`Extracted ${extractedKeys.size} unique translation keys`);
416
+ if (options.dryRun) {
417
+ console.log("\nExtracted keys:");
418
+ Array.from(extractedKeys).sort().forEach((key) => {
419
+ console.log(` ${import_chalk2.default.cyan(key)}`);
420
+ });
421
+ return;
422
+ }
423
+ await writeExtractedKeys(Array.from(extractedKeys), outputDir, options);
424
+ console.log(import_chalk2.default.green(`\u2713 Translation keys extracted to ${outputDir}`));
425
+ } catch (error) {
426
+ spinner.fail("Extraction failed");
427
+ console.error(
428
+ import_chalk2.default.red("Error:"),
429
+ error instanceof Error ? error.message : error
430
+ );
431
+ process.exit(1);
432
+ }
433
+ }
434
+ function extractKeysFromContent(content) {
435
+ const keys = [];
436
+ const patterns = [
437
+ /t\(['"`]([^'"`]+)['"`]\)/g,
438
+ // t('key')
439
+ /useTranslations\(\)\(['"`]([^'"`]+)['"`]\)/g,
440
+ // useTranslations()('key')
441
+ /i18nKey=['"`]([^'"`]+)['"`]/g,
442
+ // i18nKey="key"
443
+ /\{\s*t\(['"`]([^'"`]+)['"`]\)\s*\}/g
444
+ // { t('key') }
445
+ ];
446
+ for (const pattern of patterns) {
447
+ let match;
448
+ while ((match = pattern.exec(content)) !== null) {
449
+ keys.push(match[1]);
450
+ }
451
+ }
452
+ return keys;
453
+ }
454
+ async function writeExtractedKeys(keys, outputDir, options) {
455
+ await import_fs_extra4.default.ensureDir(outputDir);
456
+ const namespaces = { common: [] };
457
+ for (const key of keys) {
458
+ const parts = key.split(".");
459
+ if (parts.length > 1) {
460
+ const namespace = parts[0];
461
+ const keyWithoutNamespace = parts.slice(1).join(".");
462
+ if (!namespaces[namespace]) {
463
+ namespaces[namespace] = [];
464
+ }
465
+ namespaces[namespace].push(keyWithoutNamespace);
466
+ } else {
467
+ namespaces.common.push(key);
468
+ }
469
+ }
470
+ for (const [namespace, namespaceKeys] of Object.entries(namespaces)) {
471
+ if (namespaceKeys.length === 0) continue;
472
+ const filePath = import_path3.default.join(outputDir, "en", `${namespace}.json`);
473
+ await import_fs_extra4.default.ensureDir(import_path3.default.dirname(filePath));
474
+ let translations = {};
475
+ if (options.update && await import_fs_extra4.default.pathExists(filePath)) {
476
+ try {
477
+ translations = await import_fs_extra4.default.readJson(filePath);
478
+ } catch {
479
+ }
480
+ }
481
+ for (const key of namespaceKeys) {
482
+ if (!translations[key]) {
483
+ translations[key] = key;
484
+ }
485
+ }
486
+ await import_fs_extra4.default.writeJson(filePath, translations, { spaces: 2 });
487
+ }
488
+ }
489
+
490
+ // src/commands/sync.ts
491
+ var import_chalk3 = __toESM(require("chalk"));
492
+ async function syncCommand(options) {
493
+ console.log(import_chalk3.default.blue("Sync command not yet implemented"));
494
+ console.log("Options:", options);
495
+ }
496
+
497
+ // src/commands/init.ts
498
+ var import_chalk4 = __toESM(require("chalk"));
499
+ var import_ora3 = __toESM(require("ora"));
500
+ var import_fs_extra5 = __toESM(require("fs-extra"));
501
+ var import_path4 = __toESM(require("path"));
502
+ var import_inquirer = __toESM(require("inquirer"));
503
+ async function initCommand(options) {
504
+ const spinner = (0, import_ora3.default)("Initializing IntlParty configuration...").start();
505
+ try {
506
+ const configPath = "intl-party.config.json";
507
+ if (await import_fs_extra5.default.pathExists(configPath) && !options.force) {
508
+ spinner.stop();
509
+ const { overwrite } = await import_inquirer.default.prompt([
510
+ {
511
+ type: "confirm",
512
+ name: "overwrite",
513
+ message: "Configuration file already exists. Overwrite?",
514
+ default: false
515
+ }
516
+ ]);
517
+ if (!overwrite) {
518
+ console.log(import_chalk4.default.yellow("Initialization cancelled."));
519
+ return;
520
+ }
521
+ }
522
+ spinner.start("Setting up configuration...");
523
+ spinner.stop();
524
+ const answers = await import_inquirer.default.prompt([
525
+ {
526
+ type: "input",
527
+ name: "defaultLocale",
528
+ message: "Default locale:",
529
+ default: "en"
530
+ },
531
+ {
532
+ type: "input",
533
+ name: "locales",
534
+ message: "Supported locales (comma-separated):",
535
+ default: "en,es,fr",
536
+ filter: (input) => input.split(",").map((l) => l.trim())
537
+ },
538
+ {
539
+ type: "input",
540
+ name: "namespaces",
541
+ message: "Translation namespaces (comma-separated):",
542
+ default: "common",
543
+ filter: (input) => input.split(",").map((n) => n.trim())
544
+ },
545
+ {
546
+ type: "input",
547
+ name: "translationsDir",
548
+ message: "Translations directory:",
549
+ default: "./translations"
550
+ },
551
+ {
552
+ type: "input",
553
+ name: "sourceDir",
554
+ message: "Source directory:",
555
+ default: "./src"
556
+ }
557
+ ]);
558
+ const config = {
559
+ locales: answers.locales,
560
+ defaultLocale: answers.defaultLocale,
561
+ namespaces: answers.namespaces,
562
+ translationPaths: {},
563
+ sourcePatterns: [`${answers.sourceDir}/**/*.{ts,tsx,js,jsx}`],
564
+ outputDir: answers.translationsDir,
565
+ validation: {
566
+ strict: false,
567
+ logMissing: true,
568
+ throwOnMissing: false,
569
+ validateFormats: true
570
+ },
571
+ extraction: {
572
+ keyPrefix: "",
573
+ markExtracted: true,
574
+ sortKeys: true,
575
+ includeMetadata: false
576
+ },
577
+ sync: {
578
+ removeUnused: false,
579
+ addMissing: true,
580
+ preserveOrder: true
581
+ }
582
+ };
583
+ for (const locale of answers.locales) {
584
+ config.translationPaths[locale] = {};
585
+ for (const namespace of answers.namespaces) {
586
+ config.translationPaths[locale][namespace] = import_path4.default.join(
587
+ answers.translationsDir,
588
+ locale,
589
+ `${namespace}.json`
590
+ );
591
+ }
592
+ }
593
+ spinner.start("Creating directory structure...");
594
+ await import_fs_extra5.default.ensureDir(answers.translationsDir);
595
+ for (const locale of answers.locales) {
596
+ const localeDir = import_path4.default.join(answers.translationsDir, locale);
597
+ await import_fs_extra5.default.ensureDir(localeDir);
598
+ for (const namespace of answers.namespaces) {
599
+ const filePath = import_path4.default.join(localeDir, `${namespace}.json`);
600
+ if (!await import_fs_extra5.default.pathExists(filePath)) {
601
+ await import_fs_extra5.default.writeJson(filePath, {}, { spaces: 2 });
602
+ }
603
+ }
604
+ }
605
+ await saveConfig(config, configPath);
606
+ if (options.template) {
607
+ await createTemplateFiles(options.template, answers);
608
+ }
609
+ spinner.succeed("IntlParty configuration initialized successfully!");
610
+ console.log(import_chalk4.default.green("\n\u2713 Configuration created:"), configPath);
611
+ console.log(
612
+ import_chalk4.default.green("\u2713 Translation directories created:"),
613
+ answers.translationsDir
614
+ );
615
+ if (options.template) {
616
+ console.log(import_chalk4.default.green("\u2713 Template files created"));
617
+ }
618
+ console.log(import_chalk4.default.blue("\nNext steps:"));
619
+ console.log("1. Add translations to your JSON files");
620
+ console.log(
621
+ "2. Run",
622
+ import_chalk4.default.cyan("intl-party extract"),
623
+ "to extract keys from your code"
624
+ );
625
+ console.log(
626
+ "3. Run",
627
+ import_chalk4.default.cyan("intl-party validate"),
628
+ "to check your translations"
629
+ );
630
+ } catch (error) {
631
+ spinner.fail("Initialization failed");
632
+ console.error(
633
+ import_chalk4.default.red("Error:"),
634
+ error instanceof Error ? error.message : error
635
+ );
636
+ process.exit(1);
637
+ }
638
+ }
639
+ async function createTemplateFiles(template, config) {
640
+ switch (template) {
641
+ case "nextjs":
642
+ await createNextJSTemplate(config);
643
+ break;
644
+ case "react":
645
+ await createReactTemplate(config);
646
+ break;
647
+ case "vanilla":
648
+ await createVanillaTemplate(config);
649
+ break;
650
+ default:
651
+ console.log(import_chalk4.default.yellow(`Unknown template: ${template}`));
652
+ }
653
+ }
654
+ async function createNextJSTemplate(config) {
655
+ const middlewareContent = `import { createI18nMiddleware } from '@intl-party/nextjs/middleware';
656
+
657
+ export default createI18nMiddleware({
658
+ locales: ${JSON.stringify(config.locales)},
659
+ defaultLocale: '${config.defaultLocale}',
660
+ localePrefix: 'as-needed'
661
+ });
662
+
663
+ export const config = {
664
+ matcher: [
665
+ '/((?!api|_next|_vercel|favicon.ico).*)'
666
+ ]
667
+ };
668
+ `;
669
+ const layoutContent = `import { AppI18nProvider } from '@intl-party/nextjs/app';
670
+
671
+ export default function RootLayout({
672
+ children,
673
+ params: { locale }
674
+ }: {
675
+ children: React.ReactNode;
676
+ params: { locale: string };
677
+ }) {
678
+ return (
679
+ <html lang={locale}>
680
+ <body>
681
+ <AppI18nProvider locale={locale} config={{
682
+ locales: ${JSON.stringify(config.locales)},
683
+ defaultLocale: '${config.defaultLocale}',
684
+ namespaces: ${JSON.stringify(config.namespaces)}
685
+ }}>
686
+ {children}
687
+ </AppI18nProvider>
688
+ </body>
689
+ </html>
690
+ );
691
+ }
692
+ `;
693
+ await import_fs_extra5.default.writeFile("middleware.ts", middlewareContent);
694
+ await import_fs_extra5.default.ensureDir("app/[locale]");
695
+ await import_fs_extra5.default.writeFile("app/[locale]/layout.tsx", layoutContent);
696
+ }
697
+ async function createReactTemplate(config) {
698
+ const appContent = `import { I18nProvider } from '@intl-party/react';
699
+ import { createI18n } from '@intl-party/core';
700
+
701
+ const i18n = createI18n({
702
+ locales: ${JSON.stringify(config.locales)},
703
+ defaultLocale: '${config.defaultLocale}',
704
+ namespaces: ${JSON.stringify(config.namespaces)}
705
+ });
706
+
707
+ function App() {
708
+ return (
709
+ <I18nProvider i18n={i18n}>
710
+ <div className="App">
711
+ <h1>IntlParty React App</h1>
712
+ {/* Your app content */}
713
+ </div>
714
+ </I18nProvider>
715
+ );
716
+ }
717
+
718
+ export default App;
719
+ `;
720
+ await import_fs_extra5.default.writeFile("src/App.tsx", appContent);
721
+ }
722
+ async function createVanillaTemplate(config) {
723
+ const indexContent = `import { createI18n } from '@intl-party/core';
724
+
725
+ const i18n = createI18n({
726
+ locales: ${JSON.stringify(config.locales)},
727
+ defaultLocale: '${config.defaultLocale}',
728
+ namespaces: ${JSON.stringify(config.namespaces)}
729
+ });
730
+
731
+ // Load translations
732
+ // Add your translation loading logic here
733
+
734
+ // Use translations
735
+ console.log(i18n.t('welcome'));
736
+ `;
737
+ await import_fs_extra5.default.ensureDir("src");
738
+ await import_fs_extra5.default.writeFile("src/index.ts", indexContent);
739
+ }
740
+
741
+ // src/commands/check.ts
742
+ var import_chalk5 = __toESM(require("chalk"));
743
+ var import_ora4 = __toESM(require("ora"));
744
+ var import_core2 = require("@intl-party/core");
745
+ async function checkCommand(options) {
746
+ const spinner = (0, import_ora4.default)("Loading configuration...").start();
747
+ try {
748
+ const config = await loadConfig(options.config);
749
+ spinner.succeed("Configuration loaded");
750
+ spinner.start("Loading translations...");
751
+ const translations = await loadTranslations(
752
+ config.translationPaths,
753
+ config.locales,
754
+ config.namespaces
755
+ );
756
+ spinner.succeed("Translations loaded");
757
+ const issues = [];
758
+ if (options.missing !== false) {
759
+ spinner.start("Checking for missing translations...");
760
+ const validationResult = (0, import_core2.validateTranslations)(
761
+ translations,
762
+ config.locales,
763
+ config.namespaces,
764
+ { strict: true }
765
+ );
766
+ validationResult.errors.forEach((error) => {
767
+ if (error.type === "missing_key" || error.type === "missing_namespace") {
768
+ issues.push({
769
+ type: "missing",
770
+ severity: "error",
771
+ message: error.message,
772
+ locale: error.locale,
773
+ namespace: error.namespace,
774
+ key: error.key
775
+ });
776
+ }
777
+ });
778
+ spinner.succeed(
779
+ `Missing translations check completed (${issues.length} issues found)`
780
+ );
781
+ }
782
+ if (options.formatErrors !== false) {
783
+ spinner.start("Checking for format errors...");
784
+ const validationResult = (0, import_core2.validateTranslations)(
785
+ translations,
786
+ config.locales,
787
+ config.namespaces,
788
+ { validateFormats: true }
789
+ );
790
+ validationResult.errors.forEach((error) => {
791
+ if (error.type === "invalid_format") {
792
+ issues.push({
793
+ type: "format",
794
+ severity: "error",
795
+ message: error.message,
796
+ locale: error.locale,
797
+ namespace: error.namespace,
798
+ key: error.key
799
+ });
800
+ }
801
+ });
802
+ spinner.succeed("Format errors check completed");
803
+ }
804
+ if (options.unused) {
805
+ spinner.start("Checking for unused keys...");
806
+ spinner.succeed("Unused keys check completed");
807
+ }
808
+ if (options.duplicates) {
809
+ spinner.start("Checking for duplicate keys...");
810
+ spinner.succeed("Duplicate keys check completed");
811
+ }
812
+ if (issues.length === 0) {
813
+ console.log(import_chalk5.default.green("\u2713 No issues found!"));
814
+ return;
815
+ }
816
+ console.log(import_chalk5.default.red(`
817
+ \u2717 Found ${issues.length} issue(s):
818
+ `));
819
+ const groupedIssues = issues.reduce(
820
+ (acc, issue) => {
821
+ if (!acc[issue.type]) {
822
+ acc[issue.type] = [];
823
+ }
824
+ acc[issue.type].push(issue);
825
+ return acc;
826
+ },
827
+ {}
828
+ );
829
+ for (const [type, typeIssues] of Object.entries(groupedIssues)) {
830
+ console.log(
831
+ import_chalk5.default.bold(`${type.toUpperCase()} ISSUES (${typeIssues.length}):`)
832
+ );
833
+ typeIssues.forEach((issue, index) => {
834
+ const icon = issue.severity === "error" ? import_chalk5.default.red("\u2717") : import_chalk5.default.yellow("\u26A0");
835
+ const location = issue.locale && issue.namespace ? import_chalk5.default.gray(
836
+ `[${issue.locale}/${issue.namespace}${issue.key ? `/${issue.key}` : ""}]`
837
+ ) : "";
838
+ console.log(` ${index + 1}. ${icon} ${issue.message} ${location}`);
839
+ });
840
+ console.log();
841
+ }
842
+ if (options.fix) {
843
+ spinner.start("Attempting to fix issues...");
844
+ let fixedCount = 0;
845
+ if (fixedCount > 0) {
846
+ spinner.succeed(`Fixed ${fixedCount} issue(s)`);
847
+ } else {
848
+ spinner.info("No issues could be automatically fixed");
849
+ }
850
+ }
851
+ const errorCount = issues.filter(
852
+ (issue) => issue.severity === "error"
853
+ ).length;
854
+ if (errorCount > 0) {
855
+ process.exit(1);
856
+ }
857
+ } catch (error) {
858
+ spinner.fail("Check failed");
859
+ console.error(
860
+ import_chalk5.default.red("Error:"),
861
+ error instanceof Error ? error.message : error
862
+ );
863
+ process.exit(1);
864
+ }
865
+ }
866
+
867
+ // src/commands/generate.ts
868
+ var import_chalk6 = __toESM(require("chalk"));
869
+ async function generateCommand(options) {
870
+ console.log(import_chalk6.default.blue("Generate command not yet implemented"));
871
+ console.log("Options:", options);
872
+ }
873
+ // Annotate the CommonJS export names for ESM import in node:
874
+ 0 && (module.exports = {
875
+ checkCommand,
876
+ extractCommand,
877
+ generateCommand,
878
+ initCommand,
879
+ loadConfig,
880
+ loadTranslations,
881
+ saveConfig,
882
+ saveTranslations,
883
+ syncCommand,
884
+ validateCommand
885
+ });