@djangocfg/i18n 2.1.542 → 2.1.544

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.
@@ -68,11 +68,11 @@ function readLocaleFile(config, locale) {
68
68
  if (config.fileExtension === ".json") {
69
69
  return { content, exportName: "" };
70
70
  }
71
- const exportMatch = content.match(/export const (\w+)/);
72
- if (!exportMatch) {
71
+ const [, exportName] = content.match(/export const (\w+)/) ?? [];
72
+ if (!exportName) {
73
73
  throw new Error(`Could not find export in ${locale}${config.fileExtension}`);
74
74
  }
75
- return { content, exportName: exportMatch[1] };
75
+ return { content, exportName };
76
76
  }
77
77
  function writeLocaleFile(config, locale, content) {
78
78
  const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);
@@ -101,6 +101,61 @@ function getValueAtPath(obj, keyPath) {
101
101
  }
102
102
  return current;
103
103
  }
104
+ function setValueAtPath(obj, keyPath, value) {
105
+ const keys = keyPath.split(".").filter(Boolean);
106
+ const leaf = keys.pop();
107
+ if (leaf === void 0) return;
108
+ let current = obj;
109
+ for (const key of keys) {
110
+ const next = current[key];
111
+ current = isPlainRecord(next) ? next : current[key] = {};
112
+ }
113
+ current[leaf] = value;
114
+ }
115
+ function isPlainRecord(value) {
116
+ return typeof value === "object" && value !== null && !Array.isArray(value);
117
+ }
118
+ var BARE_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
119
+ function toObjectLiteral(value, indent) {
120
+ if (!isPlainRecord(value)) return JSON.stringify(value ?? null);
121
+ const inner = indent + " ";
122
+ const body = Object.entries(value).map(([key, child]) => {
123
+ const name = BARE_KEY.test(key) ? key : JSON.stringify(key);
124
+ return `${inner}${name}: ${toObjectLiteral(child, inner)},`;
125
+ }).join("\n");
126
+ return body ? `{
127
+ ${body}
128
+ ${indent}}` : "{}";
129
+ }
130
+ function renderTsLocale(originalContent, exportName, translations) {
131
+ const declaration = new RegExp(`(export\\s+const\\s+${exportName}\\b[^=]*=\\s*)`);
132
+ const match = originalContent.match(declaration);
133
+ if (!match || match.index === void 0) {
134
+ throw new Error(`Could not find "export const ${exportName}" to update`);
135
+ }
136
+ const bodyStart = match.index + match[0].length;
137
+ if (originalContent[bodyStart] !== "{") {
138
+ throw new Error(`Expected an object literal after "export const ${exportName}"`);
139
+ }
140
+ const bodyEnd = matchBrace(originalContent, bodyStart);
141
+ return originalContent.slice(0, bodyStart) + toObjectLiteral(translations, "") + originalContent.slice(bodyEnd + 1);
142
+ }
143
+ function matchBrace(source, start) {
144
+ let depth = 0;
145
+ let quote = null;
146
+ for (let i = start; i < source.length; i++) {
147
+ const char = source[i];
148
+ if (quote) {
149
+ if (char === "\\") i++;
150
+ else if (char === quote) quote = null;
151
+ continue;
152
+ }
153
+ if (char === '"' || char === "'" || char === "`") quote = char;
154
+ else if (char === "{") depth++;
155
+ else if (char === "}" && --depth === 0) return i;
156
+ }
157
+ throw new Error("Unbalanced braces in locale file");
158
+ }
104
159
 
105
160
  // src/cli/commands/list.ts
106
161
  var listCommand = defineCommand({
@@ -292,7 +347,6 @@ var addCommand = defineCommand({
292
347
  consola.info(`Adding key: ${keyPath}`);
293
348
  consola.info(`Directory: ${localesDir}`);
294
349
  consola.log("");
295
- const keyParts = keyPath.split(".");
296
350
  let updated = 0;
297
351
  let skipped = 0;
298
352
  for (const locale of config.locales) {
@@ -312,11 +366,11 @@ var addCommand = defineCommand({
312
366
  const { content, exportName } = readLocaleFile(config, locale);
313
367
  if (config.fileExtension === ".json") {
314
368
  const json = JSON.parse(content);
315
- setNestedValue(json, keyParts, translation);
369
+ setValueAtPath(json, keyPath, translation);
316
370
  writeLocaleFile(config, locale, JSON.stringify(json, null, 2));
317
371
  } else {
318
- const newContent = insertKeyIntoTS(content, exportName, keyParts, translation);
319
- writeLocaleFile(config, locale, newContent);
372
+ setValueAtPath(existing, keyPath, translation);
373
+ writeLocaleFile(config, locale, renderTsLocale(content, exportName, existing));
320
374
  }
321
375
  consola.success(` ${locale}: Added "${translation}"`);
322
376
  updated++;
@@ -331,62 +385,6 @@ var addCommand = defineCommand({
331
385
  }
332
386
  }
333
387
  });
334
- function setNestedValue(obj, keys, value) {
335
- let current = obj;
336
- for (let i = 0; i < keys.length - 1; i++) {
337
- const key = keys[i];
338
- if (!(key in current) || typeof current[key] !== "object") {
339
- current[key] = {};
340
- }
341
- current = current[key];
342
- }
343
- current[keys[keys.length - 1]] = value;
344
- }
345
- function insertKeyIntoTS(content, exportName, keyParts, value) {
346
- const firstKey = keyParts[0];
347
- const restKeys = keyParts.slice(1);
348
- const topLevelRegex = new RegExp(`(${firstKey}:\\s*\\{)`, "g");
349
- const hasTopLevel = topLevelRegex.test(content);
350
- if (!hasTopLevel) {
351
- const closingBrace = content.lastIndexOf("}");
352
- const indent2 = " ";
353
- const newEntry = buildNestedEntry(keyParts, value, indent2);
354
- return content.slice(0, closingBrace) + "\n" + newEntry + "\n" + content.slice(closingBrace);
355
- }
356
- let insertPoint = content.indexOf(`${firstKey}:`);
357
- if (insertPoint === -1) return content;
358
- let braceCount = 0;
359
- let foundStart = false;
360
- let insertIndex = insertPoint;
361
- for (let i = insertPoint; i < content.length; i++) {
362
- if (content[i] === "{") {
363
- braceCount++;
364
- foundStart = true;
365
- } else if (content[i] === "}") {
366
- braceCount--;
367
- if (foundStart && braceCount === 0) {
368
- insertIndex = i;
369
- break;
370
- }
371
- }
372
- }
373
- const indent = " ";
374
- const entry = restKeys.length === 0 ? `${indent}${keyParts[keyParts.length - 1]}: '${escapeQuotes(value)}',` : buildNestedEntry(restKeys, value, indent);
375
- return content.slice(0, insertIndex) + "\n" + entry + "\n " + content.slice(insertIndex);
376
- }
377
- function buildNestedEntry(keys, value, baseIndent) {
378
- if (keys.length === 1) {
379
- return `${baseIndent}${keys[0]}: '${escapeQuotes(value)}',`;
380
- }
381
- const [first, ...rest] = keys;
382
- const nestedContent = buildNestedEntry(rest, value, baseIndent + " ");
383
- return `${baseIndent}${first}: {
384
- ${nestedContent}
385
- ${baseIndent}},`;
386
- }
387
- function escapeQuotes(value) {
388
- return value.replace(/'/g, "\\'");
389
- }
390
388
 
391
389
  // src/cli/index.ts
392
390
  var main = defineCommand({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/utils/locales.ts","../../src/cli/commands/list.ts","../../src/cli/commands/check.ts","../../src/cli/commands/add.ts","../../src/cli/index.ts"],"names":["path2","consola","defineCommand","path3","path4","indent"],"mappings":";;;;;;;AAUA,IAAM,IAAA,GAAO,UAAA,CAAW,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AAWhC,SAAS,mBAAmB,GAAA,EAA2B;AAC5D,EAAA,IAAI,CAAI,EAAA,CAAA,UAAA,CAAW,GAAG,CAAA,EAAG;AACvB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qBAAA,EAAwB,GAAG,CAAA,CAAE,CAAA;AAAA,EAC/C;AAEA,EAAA,MAAM,KAAA,GAAW,eAAY,GAAG,CAAA;AAGhC,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,QAAA,CAAS,KAAK,CAAA,IAAK,CAAA,KAAM,UAAU,CAAA;AACzE,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC,CAAA;AAEzD,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,MAAA,GAAS,CAAA,GAAI,KAAA,GAAQ,OAAA;AACnD,EAAA,MAAM,WAAA,GAAc,aAAA,KAAkB,KAAA,GAAQ,OAAA,GAAU,SAAA;AAExD,EAAA,MAAM,OAAA,GAAU,WAAA,CACb,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,OAAA,CAAQ,aAAA,EAAe,EAAE,CAAC,CAAA,CACvC,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AACd,IAAA,IAAI,CAAA,KAAM,MAAM,OAAO,EAAA;AACvB,IAAA,IAAI,CAAA,KAAM,MAAM,OAAO,CAAA;AACvB,IAAA,OAAO,CAAA,CAAE,cAAc,CAAC,CAAA;AAAA,EAC1B,CAAC,CAAA;AAEH,EAAA,OAAO,EAAE,GAAA,EAAK,aAAA,EAAe,OAAA,EAAQ;AACvC;AAKO,SAAS,oBAAA,GAA+B;AAE7C,EAAA,IAAI,OAAA,GAAU,QAAQ,GAAA,EAAI;AAE1B,EAAA,OAAO,YAAY,GAAA,EAAK;AACtB,IAAA,MAAM,WAAA,GAAmB,IAAA,CAAA,IAAA,CAAK,OAAA,EAAS,cAAc,CAAA;AACrD,IAAA,IAAO,EAAA,CAAA,UAAA,CAAW,WAAW,CAAA,EAAG;AAC9B,MAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAS,EAAA,CAAA,YAAA,CAAa,WAAA,EAAa,OAAO,CAAC,CAAA;AAC5D,MAAA,IAAI,GAAA,CAAI,SAAS,iBAAA,EAAmB;AAClC,QAAA,OAAY,IAAA,CAAA,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,SAAS,CAAA;AAAA,MAC5C;AAAA,IACF;AACA,IAAA,OAAA,GAAe,aAAQ,OAAO,CAAA;AAAA,EAChC;AAGA,EAAA,OAAY,IAAA,CAAA,OAAA,CAAQ,WAAW,eAAe,CAAA;AAChD;AAKO,SAAS,cAAc,MAAA,EAAwB;AACpD,EAAA,IAAI,MAAA,KAAW,SAAS,OAAO,MAAA;AAC/B,EAAA,OAAO,MAAA;AACT;AAKA,eAAsB,UAAA,CACpB,QACA,MAAA,EACkC;AAClC,EAAA,MAAM,QAAA,GAAgB,UAAK,MAAA,CAAO,GAAA,EAAK,GAAG,MAAM,CAAA,EAAG,MAAA,CAAO,aAAa,CAAA,CAAE,CAAA;AAEzE,EAAA,IAAI,CAAI,EAAA,CAAA,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC5B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACtD;AAEA,EAAA,IAAI,MAAA,CAAO,kBAAkB,OAAA,EAAS;AACpC,IAAA,MAAM,OAAA,GAAa,EAAA,CAAA,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA;AACjD,IAAA,OAAO,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,EAC3B;AAGA,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA;AACzC,IAAA,MAAM,UAAA,GAAa,cAAc,MAAM,CAAA;AACvC,IAAA,MAAM,CAAA,GAAI,MAAA;AACV,IAAA,OAAQ,EAAE,UAAU,CAAA,IAAK,CAAA,CAAE,SAAS,KAAK,EAAC;AAAA,EAC5C,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,eAAA,EAAkB,MAAM,CAAA,CAAA,CAAA,EAAK,KAAK,CAAA;AAChD,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAKO,SAAS,cAAA,CACd,QACA,MAAA,EACyC;AACzC,EAAA,MAAM,QAAA,GAAgB,UAAK,MAAA,CAAO,GAAA,EAAK,GAAG,MAAM,CAAA,EAAG,MAAA,CAAO,aAAa,CAAA,CAAE,CAAA;AAEzE,EAAA,IAAI,CAAI,EAAA,CAAA,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC5B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,OAAA,GAAa,EAAA,CAAA,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA;AAEjD,EAAA,IAAI,MAAA,CAAO,kBAAkB,OAAA,EAAS;AACpC,IAAA,OAAO,EAAE,OAAA,EAAS,UAAA,EAAY,EAAA,EAAG;AAAA,EACnC;AAEA,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,CAAM,oBAAoB,CAAA;AACtD,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4B,MAAM,CAAA,EAAG,MAAA,CAAO,aAAa,CAAA,CAAE,CAAA;AAAA,EAC7E;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,UAAA,EAAY,WAAA,CAAY,CAAC,CAAA,EAAE;AAC/C;AAKO,SAAS,eAAA,CACd,MAAA,EACA,MAAA,EACA,OAAA,EACM;AACN,EAAA,MAAM,QAAA,GAAgB,UAAK,MAAA,CAAO,GAAA,EAAK,GAAG,MAAM,CAAA,EAAG,MAAA,CAAO,aAAa,CAAA,CAAE,CAAA;AACzE,EAAG,EAAA,CAAA,aAAA,CAAc,QAAA,EAAU,OAAA,EAAS,OAAO,CAAA;AAC7C;AAKO,SAAS,UAAA,CACd,GAAA,EACA,MAAA,GAAS,EAAA,EACgC;AACzC,EAAA,MAAM,OAAgD,EAAC;AAEvD,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,MAAM,WAAW,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAE/C,IAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxE,MAAA,IAAA,CAAK,IAAA,CAAK,GAAG,UAAA,CAAW,KAAA,EAAkC,QAAQ,CAAC,CAAA;AAAA,IACrE,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,QAAA,EAAU,OAAO,CAAA;AAAA,IACrC;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,cAAA,CAAe,KAA8B,OAAA,EAA0B;AACrF,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AAC9B,EAAA,IAAI,OAAA,GAAmB,GAAA;AAEvB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,OAAO,OAAA,KAAY,QAAA,EAAU;AACnD,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAW,QAAoC,GAAG,CAAA;AAAA,EACpD;AAEA,EAAA,OAAO,OAAA;AACT;;;ACxKO,IAAM,cAAc,aAAA,CAAc;AAAA,EACvC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,MAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,OAAA,EAAS;AAAA,MACP,IAAA,EAAM,YAAA;AAAA,MACN,WAAA,EAAa,2BAAA;AAAA,MACb,QAAA,EAAU;AAAA,KACZ;AAAA,IACA,GAAA,EAAK;AAAA,MACH,IAAA,EAAM,QAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa;AAAA,KACf;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,QAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa,iCAAA;AAAA,MACb,OAAA,EAAS;AAAA,KACX;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,SAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa,aAAA;AAAA,MACb,OAAA,EAAS;AAAA;AACX,GACF;AAAA,EACA,MAAM,GAAA,CAAI,EAAE,IAAA,EAAK,EAAG;AAClB,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,GACfA,IAAA,CAAA,OAAA,CAAQ,OAAA,CAAQ,KAAI,EAAG,IAAA,CAAK,GAAG,CAAA,GACpC,oBAAA,EAAqB;AAEzB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,mBAAmB,UAAU,CAAA;AAC5C,MAAA,MAAM,MAAA,GAAS,KAAK,MAAA,IAAU,IAAA;AAE9B,MAAA,IAAI,CAAC,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,MAAM,CAAA,EAAG;AACpC,QAAAC,OAAAA,CAAQ,KAAA,CAAM,CAAA,QAAA,EAAW,MAAM,CAAA,wBAAA,EAA2B,OAAO,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AACrF,QAAA;AAAA,MACF;AAEA,MAAAA,QAAQ,IAAA,CAAK,CAAA,QAAA,EAAW,MAAM,CAAA,MAAA,EAAS,UAAU,CAAA,CAAE,CAAA;AACnD,MAAA,MAAM,YAAA,GAAe,MAAM,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AACpD,MAAA,MAAM,OAAA,GAAU,WAAW,YAAY,CAAA;AAGvC,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,EAAS,WAAA,EAAY;AAC1C,MAAA,MAAM,QAAA,GAAW,UACb,OAAA,CAAQ,MAAA;AAAA,QACN,CAAC,CAAA,KACC,CAAA,CAAE,IAAA,CAAK,WAAA,GAAc,QAAA,CAAS,OAAO,CAAA,IACrC,MAAA,CAAO,EAAE,KAAK,CAAA,CAAE,WAAA,EAAY,CAAE,SAAS,OAAO;AAAA,OAClD,GACA,OAAA;AAGJ,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AACd,MAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,MAAA,EAAS,QAAA,CAAS,MAAM,CAAA,KAAA,EAAQ,OAAA,GAAU,CAAA,WAAA,EAAc,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAE,CAAA;AACtF,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AAEd,MAAA,KAAA,MAAW,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,MAAW,QAAA,EAAU;AAC/C,QAAA,IAAI,KAAK,MAAA,EAAQ;AACf,UAAAA,OAAAA,CAAQ,IAAI,CAAA,EAAA,EAAK,OAAO,KAAK,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,CAAE,CAAA;AAAA,QACtD,CAAA,MAAO;AACL,UAAAA,OAAAA,CAAQ,GAAA,CAAI,CAAA,EAAA,EAAK,OAAO,CAAA,CAAE,CAAA;AAAA,QAC5B;AAAA,MACF;AAEA,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AACd,MAAAA,OAAAA,CAAQ,KAAK,CAAA,OAAA,EAAU,QAAA,CAAS,MAAM,CAAA,GAAA,EAAM,OAAA,CAAQ,MAAM,CAAA,KAAA,CAAO,CAAA;AAAA,IACnE,SAAS,KAAA,EAAO;AACd,MAAAA,OAAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,IACrB;AAAA,EACF;AACF,CAAC,CAAA;AC3EM,IAAM,eAAeC,aAAAA,CAAc;AAAA,EACxC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,GAAA,EAAK;AAAA,MACH,IAAA,EAAM,QAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa;AAAA,KACf;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,QAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa,gCAAA;AAAA,MACb,OAAA,EAAS;AAAA,KACX;AAAA,IACA,GAAA,EAAK;AAAA,MACH,IAAA,EAAM,SAAA;AAAA,MACN,WAAA,EAAa,sBAAA;AAAA,MACb,OAAA,EAAS;AAAA;AACX,GACF;AAAA,EACA,MAAM,GAAA,CAAI,EAAE,IAAA,EAAK,EAAG;AAClB,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,GACfC,IAAA,CAAA,OAAA,CAAQ,OAAA,CAAQ,KAAI,EAAG,IAAA,CAAK,GAAG,CAAA,GACpC,oBAAA,EAAqB;AAEzB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,mBAAmB,UAAU,CAAA;AAC5C,MAAA,MAAM,UAAA,GAAa,KAAK,IAAA,IAAQ,IAAA;AAEhC,MAAA,IAAI,CAAC,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA,EAAG;AACxC,QAAAF,OAAAA,CAAQ,KAAA,CAAM,CAAA,aAAA,EAAgB,UAAU,CAAA,YAAA,CAAc,CAAA;AACtD,QAAA;AAAA,MACF;AAEA,MAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,oBAAA,EAAuB,UAAU,CAAA,CAAE,CAAA;AAChD,MAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,aAAA,EAAgB,UAAU,CAAA,CAAE,CAAA;AACzC,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AAGd,MAAA,MAAM,gBAAA,GAAmB,MAAM,UAAA,CAAW,MAAA,EAAQ,UAAU,CAAA;AAC5D,MAAA,MAAM,QAAA,GAAW,WAAW,gBAAgB,CAAA;AAE5C,MAAA,IAAI,YAAA,GAAe,CAAA;AACnB,MAAA,MAAM,kBAA4C,EAAC;AAGnD,MAAA,KAAA,MAAW,MAAA,IAAU,OAAO,OAAA,EAAS;AACnC,QAAA,IAAI,WAAW,UAAA,EAAY;AAE3B,QAAA,MAAM,YAAA,GAAe,MAAM,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AACpD,QAAA,MAAM,UAAoB,EAAC;AAE3B,QAAA,KAAA,MAAW,EAAE,IAAA,EAAM,OAAA,EAAQ,IAAK,QAAA,EAAU;AACxC,UAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,YAAA,EAAc,OAAO,CAAA;AAClD,UAAA,IAAI,UAAU,KAAA,CAAA,EAAW;AACvB,YAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,UACtB;AAAA,QACF;AAEA,QAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,UAAA,eAAA,CAAgB,MAAM,CAAA,GAAI,OAAA;AAC1B,UAAA,YAAA,IAAgB,OAAA,CAAQ,MAAA;AAAA,QAC1B;AAAA,MACF;AAGA,MAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,QAAAA,OAAAA,CAAQ,QAAQ,4BAA4B,CAAA;AAC5C,QAAA;AAAA,MACF;AAEA,MAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,MAAA,EAAS,YAAY,CAAA,aAAA,CAAe,CAAA;AACjD,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AAEd,MAAA,KAAA,MAAW,CAAC,MAAA,EAAQ,OAAO,KAAK,MAAA,CAAO,OAAA,CAAQ,eAAe,CAAA,EAAG;AAC/D,QAAAA,QAAQ,GAAA,CAAI,CAAA,EAAG,MAAM,CAAA,EAAA,EAAK,OAAA,CAAQ,MAAM,CAAA,UAAA,CAAY,CAAA;AACpD,QAAA,KAAA,MAAW,GAAA,IAAO,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,EAAG;AACtC,UAAAA,OAAAA,CAAQ,GAAA,CAAI,CAAA,IAAA,EAAO,GAAG,CAAA,CAAE,CAAA;AAAA,QAC1B;AACA,QAAA,IAAI,OAAA,CAAQ,SAAS,EAAA,EAAI;AACvB,UAAAA,QAAQ,GAAA,CAAI,CAAA,UAAA,EAAa,OAAA,CAAQ,MAAA,GAAS,EAAE,CAAA,KAAA,CAAO,CAAA;AAAA,QACrD;AACA,QAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AAAA,MAChB;AAGA,MAAAA,QAAQ,GAAA,CAAI;AAAA,QACV,KAAA,EAAO,SAAA;AAAA,QACP,OAAA,EAAS;AAAA,UACP,CAAA,MAAA,EAAS,UAAU,CAAA,EAAA,EAAK,QAAA,CAAS,MAAM,CAAA,MAAA,CAAA;AAAA,UACvC,CAAA,SAAA,EAAY,MAAA,CAAO,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA,QAAA,CAAA;AAAA,UACrC,YAAY,YAAY,CAAA,MAAA;AAAA,SAC1B,CAAE,KAAK,IAAI;AAAA,OACZ,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAAA,OAAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,IACrB;AAAA,EACF;AACF,CAAC,CAAA;ACpGM,IAAM,aAAaC,aAAAA,CAAc;AAAA,EACtC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,KAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,GAAA,EAAK;AAAA,MACH,IAAA,EAAM,YAAA;AAAA,MACN,WAAA,EAAa,oCAAA;AAAA,MACb,QAAA,EAAU;AAAA,KACZ;AAAA,IACA,YAAA,EAAc;AAAA,MACZ,IAAA,EAAM,YAAA;AAAA,MACN,WAAA,EAAa,CAAA,2FAAA,CAAA;AAAA,MACb,QAAA,EAAU;AAAA,KACZ;AAAA,IACA,GAAA,EAAK;AAAA,MACH,IAAA,EAAM,QAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa;AAAA,KACf;AAAA,IACA,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,SAAA;AAAA,MACN,KAAA,EAAO,GAAA;AAAA,MACP,WAAA,EAAa,yBAAA;AAAA,MACb,OAAA,EAAS;AAAA;AACX,GACF;AAAA,EACA,MAAM,GAAA,CAAI,EAAE,IAAA,EAAK,EAAG;AAClB,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,GACfE,IAAA,CAAA,OAAA,CAAQ,OAAA,CAAQ,KAAI,EAAG,IAAA,CAAK,GAAG,CAAA,GACpC,oBAAA,EAAqB;AAEzB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,mBAAmB,UAAU,CAAA;AAC5C,MAAA,MAAM,UAAU,IAAA,CAAK,GAAA;AAGrB,MAAA,IAAI,YAAA;AACJ,MAAA,IAAI;AACF,QAAA,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,YAAY,CAAA;AAAA,MAC7C,CAAA,CAAA,MAAQ;AACN,QAAAH,OAAAA,CAAQ,MAAM,+BAA+B,CAAA;AAC7C,QAAAA,OAAAA,CAAQ,IAAI,CAAA,8DAAA,CAAyC,CAAA;AACrD,QAAA;AAAA,MACF;AAEA,MAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,YAAA,EAAe,OAAO,CAAA,CAAE,CAAA;AACrC,MAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,WAAA,EAAc,UAAU,CAAA,CAAE,CAAA;AACvC,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AAEd,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AAClC,MAAA,IAAI,OAAA,GAAU,CAAA;AACd,MAAA,IAAI,OAAA,GAAU,CAAA;AAEd,MAAA,KAAA,MAAW,MAAA,IAAU,OAAO,OAAA,EAAS;AACnC,QAAA,MAAM,WAAA,GAAc,aAAa,MAAM,CAAA;AAEvC,QAAA,IAAI,CAAC,WAAA,EAAa;AAChB,UAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,EAAA,EAAK,MAAM,CAAA,mCAAA,CAAqC,CAAA;AAC7D,UAAA,OAAA,EAAA;AACA,UAAA;AAAA,QACF;AAGA,QAAA,MAAM,QAAA,GAAW,MAAM,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AAChD,QAAA,MAAM,aAAA,GAAgB,cAAA,CAAe,QAAA,EAAU,OAAO,CAAA;AAEtD,QAAA,IAAI,aAAA,KAAkB,KAAA,CAAA,IAAa,CAAC,IAAA,CAAK,KAAA,EAAO;AAC9C,UAAAA,OAAAA,CAAQ,IAAA,CAAK,CAAA,EAAA,EAAK,MAAM,CAAA,sCAAA,CAAwC,CAAA;AAChE,UAAA,OAAA,EAAA;AACA,UAAA;AAAA,QACF;AAGA,QAAA,MAAM,EAAE,OAAA,EAAS,UAAA,EAAW,GAAI,cAAA,CAAe,QAAQ,MAAM,CAAA;AAE7D,QAAA,IAAI,MAAA,CAAO,kBAAkB,OAAA,EAAS;AAEpC,UAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAC/B,UAAA,cAAA,CAAe,IAAA,EAAM,UAAU,WAAW,CAAA;AAC1C,UAAA,eAAA,CAAgB,QAAQ,MAAA,EAAQ,IAAA,CAAK,UAAU,IAAA,EAAM,IAAA,EAAM,CAAC,CAAC,CAAA;AAAA,QAC/D,CAAA,MAAO;AAEL,UAAA,MAAM,UAAA,GAAa,eAAA,CAAgB,OAAA,EAAS,UAAA,EAAY,UAAU,WAAW,CAAA;AAC7E,UAAA,eAAA,CAAgB,MAAA,EAAQ,QAAQ,UAAU,CAAA;AAAA,QAC5C;AAEA,QAAAA,QAAQ,OAAA,CAAQ,CAAA,EAAA,EAAK,MAAM,CAAA,SAAA,EAAY,WAAW,CAAA,CAAA,CAAG,CAAA;AACrD,QAAA,OAAA,EAAA;AAAA,MACF;AAEA,MAAAA,OAAAA,CAAQ,IAAI,EAAE,CAAA;AACd,MAAAA,QAAQ,GAAA,CAAI;AAAA,QACV,KAAA,EAAO,QAAA;AAAA,QACP,OAAA,EAAS,CAAA,SAAA,EAAY,OAAO,CAAA,WAAA,EAAc,OAAO,CAAA;AAAA,OAClD,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAAA,OAAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,IACrB;AAAA,EACF;AACF,CAAC,CAAA;AAKD,SAAS,cAAA,CAAe,GAAA,EAA8B,IAAA,EAAgB,KAAA,EAAqB;AACzF,EAAA,IAAI,OAAA,GAAU,GAAA;AACd,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,MAAA,GAAS,GAAG,CAAA,EAAA,EAAK;AACxC,IAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,IAAA,IAAI,EAAE,GAAA,IAAO,OAAA,CAAA,IAAY,OAAO,OAAA,CAAQ,GAAG,MAAM,QAAA,EAAU;AACzD,MAAA,OAAA,CAAQ,GAAG,IAAI,EAAC;AAAA,IAClB;AACA,IAAA,OAAA,GAAU,QAAQ,GAAG,CAAA;AAAA,EACvB;AACA,EAAA,OAAA,CAAQ,IAAA,CAAK,IAAA,CAAK,MAAA,GAAS,CAAC,CAAC,CAAA,GAAI,KAAA;AACnC;AAKA,SAAS,eAAA,CACP,OAAA,EACA,UAAA,EACA,QAAA,EACA,KAAA,EACQ;AAER,EAAA,MAAM,QAAA,GAAW,SAAS,CAAC,CAAA;AAC3B,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA;AAGjC,EAAA,MAAM,gBAAgB,IAAI,MAAA,CAAO,CAAA,CAAA,EAAI,QAAQ,aAAa,GAAG,CAAA;AAC7D,EAAA,MAAM,WAAA,GAAc,aAAA,CAAc,IAAA,CAAK,OAAO,CAAA;AAE9C,EAAA,IAAI,CAAC,WAAA,EAAa;AAEhB,IAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,WAAA,CAAY,GAAG,CAAA;AAC5C,IAAA,MAAMI,OAAAA,GAAS,IAAA;AACf,IAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,QAAA,EAAU,KAAA,EAAOA,OAAM,CAAA;AACzD,IAAA,OACE,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,YAAY,CAAA,GAAI,OAAO,QAAA,GAAW,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,YAAY,CAAA;AAAA,EAExF;AAIA,EAAA,IAAI,WAAA,GAAc,OAAA,CAAQ,OAAA,CAAQ,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAG,CAAA;AAChD,EAAA,IAAI,WAAA,KAAgB,IAAI,OAAO,OAAA;AAG/B,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,UAAA,GAAa,KAAA;AACjB,EAAA,IAAI,WAAA,GAAc,WAAA;AAElB,EAAA,KAAA,IAAS,CAAA,GAAI,WAAA,EAAa,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACjD,IAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,KAAM,GAAA,EAAK;AACtB,MAAA,UAAA,EAAA;AACA,MAAA,UAAA,GAAa,IAAA;AAAA,IACf,CAAA,MAAA,IAAW,OAAA,CAAQ,CAAC,CAAA,KAAM,GAAA,EAAK;AAC7B,MAAA,UAAA,EAAA;AACA,MAAA,IAAI,UAAA,IAAc,eAAe,CAAA,EAAG;AAElC,QAAA,WAAA,GAAc,CAAA;AACd,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,MAAA,GAAS,MAAA;AACf,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,KAAW,CAAA,GAC9B,GAAG,MAAM,CAAA,EAAG,SAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAC,CAAA,GAAA,EAAM,aAAa,KAAK,CAAC,OAClE,gBAAA,CAAiB,QAAA,EAAU,OAAO,MAAM,CAAA;AAE5C,EAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,WAAW,CAAA,GAAI,OAAO,KAAA,GAAQ,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAA;AAC1F;AAKA,SAAS,gBAAA,CAAiB,IAAA,EAAgB,KAAA,EAAe,UAAA,EAA4B;AACnF,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,IAAA,OAAO,CAAA,EAAG,UAAU,CAAA,EAAG,IAAA,CAAK,CAAC,CAAC,CAAA,GAAA,EAAM,YAAA,CAAa,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,EACzD;AAEA,EAAA,MAAM,CAAC,KAAA,EAAO,GAAG,IAAI,CAAA,GAAI,IAAA;AACzB,EAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,IAAA,EAAM,KAAA,EAAO,aAAa,IAAI,CAAA;AACrE,EAAA,OAAO,CAAA,EAAG,UAAU,CAAA,EAAG,KAAK,CAAA;AAAA,EAAQ,aAAa;AAAA,EAAK,UAAU,CAAA,EAAA,CAAA;AAClE;AAKA,SAAS,aAAa,KAAA,EAAuB;AAC3C,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAA;AAClC;;;AC/LA,IAAM,OAAOH,aAAAA,CAAc;AAAA,EACzB,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,OAAA;AAAA,IACT,WAAA,EAAa;AAAA,GACf;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,WAAA;AAAA,IACN,KAAA,EAAO,YAAA;AAAA,IACP,GAAA,EAAK;AAAA,GACP;AAAA,EACA,KAAA,GAAQ;AACN,IAAAD,OAAAA,CAAQ,IAAI,UAAU,CAAA;AAAA,EACxB;AACF,CAAC,CAAA;AAED,OAAA,CAAQ,IAAI,CAAA","file":"index.mjs","sourcesContent":["/**\n * Locale file utilities for CLI\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { consola } from 'consola';\nimport { createJiti } from 'jiti';\n\n// Create jiti instance for loading TypeScript files at runtime\nconst jiti = createJiti(import.meta.url);\n\nexport interface LocaleConfig {\n dir: string;\n fileExtension: '.ts' | '.json';\n locales: string[];\n}\n\n/**\n * Detect locale configuration from directory\n */\nexport function detectLocaleConfig(dir: string): LocaleConfig {\n if (!fs.existsSync(dir)) {\n throw new Error(`Directory not found: ${dir}`);\n }\n\n const files = fs.readdirSync(dir);\n\n // Check for .ts or .json files\n const tsFiles = files.filter((f) => f.endsWith('.ts') && f !== 'index.ts');\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const fileExtension = tsFiles.length > 0 ? '.ts' : '.json';\n const localeFiles = fileExtension === '.ts' ? tsFiles : jsonFiles;\n\n const locales = localeFiles\n .map((f) => f.replace(fileExtension, ''))\n .sort((a, b) => {\n if (a === 'en') return -1;\n if (b === 'en') return 1;\n return a.localeCompare(b);\n });\n\n return { dir, fileExtension, locales };\n}\n\n/**\n * Get default locales directory (package locales)\n */\nexport function getDefaultLocalesDir(): string {\n // Try to find package root\n let current = process.cwd();\n\n while (current !== '/') {\n const packageJson = path.join(current, 'package.json');\n if (fs.existsSync(packageJson)) {\n const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf-8'));\n if (pkg.name === '@djangocfg/i18n') {\n return path.join(current, 'src', 'locales');\n }\n }\n current = path.dirname(current);\n }\n\n // Fallback: relative to CLI file\n return path.resolve(__dirname, '../../locales');\n}\n\n/**\n * Get export name for locale (handles pt-BR -> ptBR)\n */\nexport function getExportName(locale: string): string {\n if (locale === 'pt-BR') return 'ptBR';\n return locale;\n}\n\n/**\n * Load locale translations\n */\nexport async function loadLocale(\n config: LocaleConfig,\n locale: string\n): Promise<Record<string, unknown>> {\n const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);\n\n if (!fs.existsSync(filePath)) {\n throw new Error(`Locale file not found: ${filePath}`);\n }\n\n if (config.fileExtension === '.json') {\n const content = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(content);\n }\n\n // TypeScript file - use jiti for runtime TypeScript loading\n try {\n const module = await jiti.import(filePath);\n const exportName = getExportName(locale);\n const m = module as Record<string, unknown>\n return (m[exportName] ?? m['default'] ?? {}) as Record<string, unknown>\n } catch (error) {\n consola.error(`Failed to load ${locale}:`, error);\n return {};\n }\n}\n\n/**\n * Read locale file content (for editing)\n */\nexport function readLocaleFile(\n config: LocaleConfig,\n locale: string\n): { content: string; exportName: string } {\n const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);\n\n if (!fs.existsSync(filePath)) {\n throw new Error(`Locale file not found: ${filePath}`);\n }\n\n const content = fs.readFileSync(filePath, 'utf-8');\n\n if (config.fileExtension === '.json') {\n return { content, exportName: '' };\n }\n\n const exportMatch = content.match(/export const (\\w+)/);\n if (!exportMatch) {\n throw new Error(`Could not find export in ${locale}${config.fileExtension}`);\n }\n\n return { content, exportName: exportMatch[1] };\n}\n\n/**\n * Write locale file\n */\nexport function writeLocaleFile(\n config: LocaleConfig,\n locale: string,\n content: string\n): void {\n const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);\n fs.writeFileSync(filePath, content, 'utf-8');\n}\n\n/**\n * Get all keys from object as dot-notation paths\n */\nexport function getAllKeys(\n obj: Record<string, unknown>,\n prefix = ''\n): Array<{ path: string; value: unknown }> {\n const keys: Array<{ path: string; value: unknown }> = [];\n\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix ? `${prefix}.${key}` : key;\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n keys.push(...getAllKeys(value as Record<string, unknown>, fullPath));\n } else {\n keys.push({ path: fullPath, value });\n }\n }\n\n return keys;\n}\n\n/**\n * Get value at nested path\n */\nexport function getValueAtPath(obj: Record<string, unknown>, keyPath: string): unknown {\n const keys = keyPath.split('.');\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current === null || typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\n/**\n * Set value at nested path\n */\nexport function setValueAtPath(\n obj: Record<string, unknown>,\n keyPath: string,\n value: unknown\n): void {\n const keys = keyPath.split('.');\n let current = obj;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n\n current[keys[keys.length - 1]] = value;\n}\n","/**\n * List/search translation keys\n */\n\nimport { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport * as path from 'node:path';\nimport {\n detectLocaleConfig,\n getDefaultLocalesDir,\n loadLocale,\n getAllKeys,\n} from '../utils';\n\nexport const listCommand = defineCommand({\n meta: {\n name: 'list',\n description: 'List and search translation keys',\n },\n args: {\n pattern: {\n type: 'positional',\n description: 'Search pattern (optional)',\n required: false,\n },\n dir: {\n type: 'string',\n alias: 'd',\n description: 'Locales directory',\n },\n locale: {\n type: 'string',\n alias: 'l',\n description: 'Locale to inspect (default: en)',\n default: 'en',\n },\n values: {\n type: 'boolean',\n alias: 'v',\n description: 'Show values',\n default: false,\n },\n },\n async run({ args }) {\n const localesDir = args.dir\n ? path.resolve(process.cwd(), args.dir)\n : getDefaultLocalesDir();\n\n try {\n const config = detectLocaleConfig(localesDir);\n const locale = args.locale || 'en';\n\n if (!config.locales.includes(locale)) {\n consola.error(`Locale \"${locale}\" not found. Available: ${config.locales.join(', ')}`);\n return;\n }\n\n consola.info(`Loading ${locale} from ${localesDir}`);\n const translations = await loadLocale(config, locale);\n const allKeys = getAllKeys(translations);\n\n // Filter by pattern\n const pattern = args.pattern?.toLowerCase();\n const filtered = pattern\n ? allKeys.filter(\n (k) =>\n k.path.toLowerCase().includes(pattern) ||\n String(k.value).toLowerCase().includes(pattern)\n )\n : allKeys;\n\n // Display\n consola.log('');\n consola.info(`Found ${filtered.length} keys${pattern ? ` matching \"${pattern}\"` : ''}`);\n consola.log('');\n\n for (const { path: keyPath, value } of filtered) {\n if (args.values) {\n consola.log(` ${keyPath}: ${JSON.stringify(value)}`);\n } else {\n consola.log(` ${keyPath}`);\n }\n }\n\n consola.log('');\n consola.info(`Total: ${filtered.length} / ${allKeys.length} keys`);\n } catch (error) {\n consola.error(error);\n }\n },\n});\n","/**\n * Check for missing translation keys\n */\n\nimport { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport * as path from 'node:path';\nimport {\n detectLocaleConfig,\n getDefaultLocalesDir,\n loadLocale,\n getAllKeys,\n getValueAtPath,\n} from '../utils';\n\nexport const checkCommand = defineCommand({\n meta: {\n name: 'check',\n description: 'Find missing translation keys across locales',\n },\n args: {\n dir: {\n type: 'string',\n alias: 'd',\n description: 'Locales directory',\n },\n base: {\n type: 'string',\n alias: 'b',\n description: 'Base locale to compare against',\n default: 'en',\n },\n fix: {\n type: 'boolean',\n description: 'Show suggested fixes',\n default: false,\n },\n },\n async run({ args }) {\n const localesDir = args.dir\n ? path.resolve(process.cwd(), args.dir)\n : getDefaultLocalesDir();\n\n try {\n const config = detectLocaleConfig(localesDir);\n const baseLocale = args.base || 'en';\n\n if (!config.locales.includes(baseLocale)) {\n consola.error(`Base locale \"${baseLocale}\" not found.`);\n return;\n }\n\n consola.info(`Checking locales in ${localesDir}`);\n consola.info(`Base locale: ${baseLocale}`);\n consola.log('');\n\n // Load base locale\n const baseTranslations = await loadLocale(config, baseLocale);\n const baseKeys = getAllKeys(baseTranslations);\n\n let totalMissing = 0;\n const missingByLocale: Record<string, string[]> = {};\n\n // Check each locale\n for (const locale of config.locales) {\n if (locale === baseLocale) continue;\n\n const translations = await loadLocale(config, locale);\n const missing: string[] = [];\n\n for (const { path: keyPath } of baseKeys) {\n const value = getValueAtPath(translations, keyPath);\n if (value === undefined) {\n missing.push(keyPath);\n }\n }\n\n if (missing.length > 0) {\n missingByLocale[locale] = missing;\n totalMissing += missing.length;\n }\n }\n\n // Report\n if (totalMissing === 0) {\n consola.success('All locales have all keys!');\n return;\n }\n\n consola.warn(`Found ${totalMissing} missing keys`);\n consola.log('');\n\n for (const [locale, missing] of Object.entries(missingByLocale)) {\n consola.log(`${locale} (${missing.length} missing):`);\n for (const key of missing.slice(0, 10)) {\n consola.log(` - ${key}`);\n }\n if (missing.length > 10) {\n consola.log(` ... and ${missing.length - 10} more`);\n }\n consola.log('');\n }\n\n // Summary\n consola.box({\n title: 'Summary',\n message: [\n `Base: ${baseLocale} (${baseKeys.length} keys)`,\n `Checked: ${config.locales.length - 1} locales`,\n `Missing: ${totalMissing} total`,\n ].join('\\n'),\n });\n } catch (error) {\n consola.error(error);\n }\n },\n});\n","/**\n * Add translation key to all locales\n */\n\nimport { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport * as path from 'node:path';\nimport {\n detectLocaleConfig,\n getDefaultLocalesDir,\n readLocaleFile,\n writeLocaleFile,\n getValueAtPath,\n loadLocale,\n} from '../utils';\n\nexport const addCommand = defineCommand({\n meta: {\n name: 'add',\n description: 'Add a translation key to all locales',\n },\n args: {\n key: {\n type: 'positional',\n description: 'Key path (e.g., \"tools.tour.next\")',\n required: true,\n },\n translations: {\n type: 'positional',\n description: 'JSON object with translations (e.g., \\'{\"en\":\"Next\",\"ru\":\"Далее\"}\\')',\n required: true,\n },\n dir: {\n type: 'string',\n alias: 'd',\n description: 'Locales directory',\n },\n force: {\n type: 'boolean',\n alias: 'f',\n description: 'Overwrite existing keys',\n default: false,\n },\n },\n async run({ args }) {\n const localesDir = args.dir\n ? path.resolve(process.cwd(), args.dir)\n : getDefaultLocalesDir();\n\n try {\n const config = detectLocaleConfig(localesDir);\n const keyPath = args.key;\n\n // Parse translations\n let translations: Record<string, string>;\n try {\n translations = JSON.parse(args.translations);\n } catch {\n consola.error('Invalid JSON for translations');\n consola.log('Example: \\'{\"en\":\"Next\",\"ru\":\"Далее\"}\\'');\n return;\n }\n\n consola.info(`Adding key: ${keyPath}`);\n consola.info(`Directory: ${localesDir}`);\n consola.log('');\n\n const keyParts = keyPath.split('.');\n let updated = 0;\n let skipped = 0;\n\n for (const locale of config.locales) {\n const translation = translations[locale];\n\n if (!translation) {\n consola.warn(` ${locale}: No translation provided, skipping`);\n skipped++;\n continue;\n }\n\n // Check if key exists\n const existing = await loadLocale(config, locale);\n const existingValue = getValueAtPath(existing, keyPath);\n\n if (existingValue !== undefined && !args.force) {\n consola.warn(` ${locale}: Key exists, use --force to overwrite`);\n skipped++;\n continue;\n }\n\n // Read and update file\n const { content, exportName } = readLocaleFile(config, locale);\n\n if (config.fileExtension === '.json') {\n // JSON file\n const json = JSON.parse(content);\n setNestedValue(json, keyParts, translation);\n writeLocaleFile(config, locale, JSON.stringify(json, null, 2));\n } else {\n // TypeScript file - use regex to insert\n const newContent = insertKeyIntoTS(content, exportName, keyParts, translation);\n writeLocaleFile(config, locale, newContent);\n }\n\n consola.success(` ${locale}: Added \"${translation}\"`);\n updated++;\n }\n\n consola.log('');\n consola.box({\n title: 'Result',\n message: `Updated: ${updated}, Skipped: ${skipped}`,\n });\n } catch (error) {\n consola.error(error);\n }\n },\n});\n\n/**\n * Set nested value in object\n */\nfunction setNestedValue(obj: Record<string, unknown>, keys: string[], value: string): void {\n let current = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = value;\n}\n\n/**\n * Insert key into TypeScript locale file\n */\nfunction insertKeyIntoTS(\n content: string,\n exportName: string,\n keyParts: string[],\n value: string\n): string {\n // Find the object for the first key part\n const firstKey = keyParts[0];\n const restKeys = keyParts.slice(1);\n\n // Check if top-level key exists\n const topLevelRegex = new RegExp(`(${firstKey}:\\\\s*\\\\{)`, 'g');\n const hasTopLevel = topLevelRegex.test(content);\n\n if (!hasTopLevel) {\n // Add new top-level key before closing brace\n const closingBrace = content.lastIndexOf('}');\n const indent = ' ';\n const newEntry = buildNestedEntry(keyParts, value, indent);\n return (\n content.slice(0, closingBrace) + '\\n' + newEntry + '\\n' + content.slice(closingBrace)\n );\n }\n\n // Navigate to the right nesting level and insert\n // This is a simplified approach - for complex cases, consider AST parsing\n let insertPoint = content.indexOf(`${firstKey}:`);\n if (insertPoint === -1) return content;\n\n // Find the opening brace for this section\n let braceCount = 0;\n let foundStart = false;\n let insertIndex = insertPoint;\n\n for (let i = insertPoint; i < content.length; i++) {\n if (content[i] === '{') {\n braceCount++;\n foundStart = true;\n } else if (content[i] === '}') {\n braceCount--;\n if (foundStart && braceCount === 0) {\n // Insert before this closing brace\n insertIndex = i;\n break;\n }\n }\n }\n\n // Build the entry\n const indent = ' '; // 4 spaces for nested\n const entry = restKeys.length === 0\n ? `${indent}${keyParts[keyParts.length - 1]}: '${escapeQuotes(value)}',`\n : buildNestedEntry(restKeys, value, indent);\n\n return content.slice(0, insertIndex) + '\\n' + entry + '\\n ' + content.slice(insertIndex);\n}\n\n/**\n * Build nested entry string\n */\nfunction buildNestedEntry(keys: string[], value: string, baseIndent: string): string {\n if (keys.length === 1) {\n return `${baseIndent}${keys[0]}: '${escapeQuotes(value)}',`;\n }\n\n const [first, ...rest] = keys;\n const nestedContent = buildNestedEntry(rest, value, baseIndent + ' ');\n return `${baseIndent}${first}: {\\n${nestedContent}\\n${baseIndent}},`;\n}\n\n/**\n * Escape single quotes in value\n */\nfunction escapeQuotes(value: string): string {\n return value.replace(/'/g, \"\\\\'\");\n}\n","#!/usr/bin/env node\n/**\n * i18n CLI - Unified tool for managing translations\n *\n * Usage:\n * pnpm i18n list [pattern] - List/search keys\n * pnpm i18n check - Find missing keys\n * pnpm i18n add <key> <json> - Add key to all locales\n *\n * Can work with:\n * - Package locales (default): packages/i18n/src/locales\n * - App locales: --dir apps/studio/i18n/locales\n */\n\nimport { defineCommand, runMain } from 'citty';\nimport { consola } from 'consola';\n\nimport { listCommand } from './commands/list';\nimport { checkCommand } from './commands/check';\nimport { addCommand } from './commands/add';\n\nconst main = defineCommand({\n meta: {\n name: 'i18n',\n version: '1.0.0',\n description: 'CLI for managing i18n translations',\n },\n subCommands: {\n list: listCommand,\n check: checkCommand,\n add: addCommand,\n },\n setup() {\n consola.box('i18n CLI');\n },\n});\n\nrunMain(main);\n"]}
1
+ {"version":3,"sources":["../../src/cli/utils/locales.ts","../../src/cli/commands/list.ts","../../src/cli/commands/check.ts","../../src/cli/commands/add.ts","../../src/cli/index.ts"],"names":["path2","consola","defineCommand","path3","path4"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Locale file utilities for CLI\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { consola } from 'consola';\nimport { createJiti } from 'jiti';\n\n// Create jiti instance for loading TypeScript files at runtime\nconst jiti = createJiti(import.meta.url);\n\nexport interface LocaleConfig {\n dir: string;\n fileExtension: '.ts' | '.json';\n locales: string[];\n}\n\n/**\n * Detect locale configuration from directory\n */\nexport function detectLocaleConfig(dir: string): LocaleConfig {\n if (!fs.existsSync(dir)) {\n throw new Error(`Directory not found: ${dir}`);\n }\n\n const files = fs.readdirSync(dir);\n\n // Check for .ts or .json files\n const tsFiles = files.filter((f) => f.endsWith('.ts') && f !== 'index.ts');\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const fileExtension = tsFiles.length > 0 ? '.ts' : '.json';\n const localeFiles = fileExtension === '.ts' ? tsFiles : jsonFiles;\n\n const locales = localeFiles\n .map((f) => f.replace(fileExtension, ''))\n .sort((a, b) => {\n if (a === 'en') return -1;\n if (b === 'en') return 1;\n return a.localeCompare(b);\n });\n\n return { dir, fileExtension, locales };\n}\n\n/**\n * Get default locales directory (package locales)\n */\nexport function getDefaultLocalesDir(): string {\n // Try to find package root\n let current = process.cwd();\n\n while (current !== '/') {\n const packageJson = path.join(current, 'package.json');\n if (fs.existsSync(packageJson)) {\n const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf-8'));\n if (pkg.name === '@djangocfg/i18n') {\n return path.join(current, 'src', 'locales');\n }\n }\n current = path.dirname(current);\n }\n\n // Fallback: relative to CLI file\n return path.resolve(__dirname, '../../locales');\n}\n\n/**\n * Get export name for locale (handles pt-BR -> ptBR)\n */\nexport function getExportName(locale: string): string {\n if (locale === 'pt-BR') return 'ptBR';\n return locale;\n}\n\n/**\n * Load locale translations\n */\nexport async function loadLocale(\n config: LocaleConfig,\n locale: string\n): Promise<Record<string, unknown>> {\n const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);\n\n if (!fs.existsSync(filePath)) {\n throw new Error(`Locale file not found: ${filePath}`);\n }\n\n if (config.fileExtension === '.json') {\n const content = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(content);\n }\n\n // TypeScript file - use jiti for runtime TypeScript loading\n try {\n const module = await jiti.import(filePath);\n const exportName = getExportName(locale);\n const m = module as Record<string, unknown>\n return (m[exportName] ?? m['default'] ?? {}) as Record<string, unknown>\n } catch (error) {\n consola.error(`Failed to load ${locale}:`, error);\n return {};\n }\n}\n\n/**\n * Read locale file content (for editing)\n */\nexport function readLocaleFile(\n config: LocaleConfig,\n locale: string\n): { content: string; exportName: string } {\n const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);\n\n if (!fs.existsSync(filePath)) {\n throw new Error(`Locale file not found: ${filePath}`);\n }\n\n const content = fs.readFileSync(filePath, 'utf-8');\n\n if (config.fileExtension === '.json') {\n return { content, exportName: '' };\n }\n\n // Destructured rather than indexed: `\\w+` cannot match empty, so a match\n // always carries group 1 — but only the binding proves that to the compiler,\n // and the same `throw` covers both halves instead of asserting one of them.\n const [, exportName] = content.match(/export const (\\w+)/) ?? [];\n if (!exportName) {\n throw new Error(`Could not find export in ${locale}${config.fileExtension}`);\n }\n\n return { content, exportName };\n}\n\n/**\n * Write locale file\n */\nexport function writeLocaleFile(\n config: LocaleConfig,\n locale: string,\n content: string\n): void {\n const filePath = path.join(config.dir, `${locale}${config.fileExtension}`);\n fs.writeFileSync(filePath, content, 'utf-8');\n}\n\n/**\n * Get all keys from object as dot-notation paths\n */\nexport function getAllKeys(\n obj: Record<string, unknown>,\n prefix = ''\n): Array<{ path: string; value: unknown }> {\n const keys: Array<{ path: string; value: unknown }> = [];\n\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix ? `${prefix}.${key}` : key;\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n keys.push(...getAllKeys(value as Record<string, unknown>, fullPath));\n } else {\n keys.push({ path: fullPath, value });\n }\n }\n\n return keys;\n}\n\n/**\n * Get value at nested path\n */\nexport function getValueAtPath(obj: Record<string, unknown>, keyPath: string): unknown {\n const keys = keyPath.split('.');\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current === null || typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\n/**\n * Set value at nested path.\n *\n * `isPlainRecord` rather than `typeof x === 'object'`: that test is true for\n * `null` and for arrays. A `null` on the path crashed with \"Cannot set\n * properties of null\", and an array swallowed the write silently — the value\n * went nowhere and the CLI reported success.\n *\n * An empty `keyPath` writes nothing. `''.split('.')` yields `['']`, and the\n * previous code indexed `keys[-1]`, producing a literal `\"undefined\"` key in\n * the locale file.\n */\nexport function setValueAtPath(\n obj: Record<string, unknown>,\n keyPath: string,\n value: unknown\n): void {\n const keys = keyPath.split('.').filter(Boolean);\n const leaf = keys.pop();\n if (leaf === undefined) return;\n\n let current = obj;\n for (const key of keys) {\n const next = current[key];\n current = isPlainRecord(next) ? next : (current[key] = {});\n }\n\n current[leaf] = value;\n}\n\n/** A value that can carry nested keys: an object, but not `null` and not an array. */\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Object keys that need no quoting in a TS/JS object literal. */\nconst BARE_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * Serialise a translations object back to a TypeScript object literal.\n *\n * Locale leaves are always strings and branches always plain objects, so this\n * needs no general-purpose value support — anything else is a bug upstream and\n * `JSON.stringify` is the honest fallback rather than a silent `[object]`.\n */\nfunction toObjectLiteral(value: unknown, indent: string): string {\n if (!isPlainRecord(value)) return JSON.stringify(value ?? null);\n\n const inner = indent + ' ';\n const body = Object.entries(value)\n .map(([key, child]) => {\n const name = BARE_KEY.test(key) ? key : JSON.stringify(key);\n return `${inner}${name}: ${toObjectLiteral(child, inner)},`;\n })\n .join('\\n');\n\n return body ? `{\\n${body}\\n${indent}}` : '{}';\n}\n\n/**\n * Replace the exported object literal in a TS locale file, keeping everything\n * around it — the type import, the doc comment, and the `: I18nTranslations`\n * annotation — untouched.\n *\n * The previous approach edited the text with `indexOf` and brace counting, and\n * inserted a whole nested chain at the outermost level: adding `ui.form.cancel`\n * to a file that already had `ui.form.save` produced a SECOND `form:` block\n * inside `ui`. In a TS object literal the later key wins, so the existing\n * translation was silently dropped — a data-loss bug, not a formatting one.\n * The file's own comment said \"for complex cases, consider AST parsing\".\n * Serialising the object `loadLocale` already parsed removes the need for both.\n */\nexport function renderTsLocale(\n originalContent: string,\n exportName: string,\n translations: Record<string, unknown>\n): string {\n const declaration = new RegExp(`(export\\\\s+const\\\\s+${exportName}\\\\b[^=]*=\\\\s*)`);\n const match = originalContent.match(declaration);\n if (!match || match.index === undefined) {\n throw new Error(`Could not find \"export const ${exportName}\" to update`);\n }\n\n const bodyStart = match.index + match[0].length;\n if (originalContent[bodyStart] !== '{') {\n throw new Error(`Expected an object literal after \"export const ${exportName}\"`);\n }\n\n const bodyEnd = matchBrace(originalContent, bodyStart);\n return (\n originalContent.slice(0, bodyStart) +\n toObjectLiteral(translations, '') +\n originalContent.slice(bodyEnd + 1)\n );\n}\n\n/**\n * Index of the `}` closing the `{` at `start`.\n *\n * String-aware: a brace inside a translation (`\"{count} items\"`) would\n * otherwise unbalance the count and truncate the file.\n */\nfunction matchBrace(source: string, start: number): number {\n let depth = 0;\n let quote: string | null = null;\n\n for (let i = start; i < source.length; i++) {\n const char = source[i];\n\n if (quote) {\n if (char === '\\\\') i++;\n else if (char === quote) quote = null;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === '`') quote = char;\n else if (char === '{') depth++;\n else if (char === '}' && --depth === 0) return i;\n }\n\n throw new Error('Unbalanced braces in locale file');\n}\n","/**\n * List/search translation keys\n */\n\nimport { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport * as path from 'node:path';\nimport {\n detectLocaleConfig,\n getDefaultLocalesDir,\n loadLocale,\n getAllKeys,\n} from '../utils';\n\nexport const listCommand = defineCommand({\n meta: {\n name: 'list',\n description: 'List and search translation keys',\n },\n args: {\n pattern: {\n type: 'positional',\n description: 'Search pattern (optional)',\n required: false,\n },\n dir: {\n type: 'string',\n alias: 'd',\n description: 'Locales directory',\n },\n locale: {\n type: 'string',\n alias: 'l',\n description: 'Locale to inspect (default: en)',\n default: 'en',\n },\n values: {\n type: 'boolean',\n alias: 'v',\n description: 'Show values',\n default: false,\n },\n },\n async run({ args }) {\n const localesDir = args.dir\n ? path.resolve(process.cwd(), args.dir)\n : getDefaultLocalesDir();\n\n try {\n const config = detectLocaleConfig(localesDir);\n const locale = args.locale || 'en';\n\n if (!config.locales.includes(locale)) {\n consola.error(`Locale \"${locale}\" not found. Available: ${config.locales.join(', ')}`);\n return;\n }\n\n consola.info(`Loading ${locale} from ${localesDir}`);\n const translations = await loadLocale(config, locale);\n const allKeys = getAllKeys(translations);\n\n // Filter by pattern\n const pattern = args.pattern?.toLowerCase();\n const filtered = pattern\n ? allKeys.filter(\n (k) =>\n k.path.toLowerCase().includes(pattern) ||\n String(k.value).toLowerCase().includes(pattern)\n )\n : allKeys;\n\n // Display\n consola.log('');\n consola.info(`Found ${filtered.length} keys${pattern ? ` matching \"${pattern}\"` : ''}`);\n consola.log('');\n\n for (const { path: keyPath, value } of filtered) {\n if (args.values) {\n consola.log(` ${keyPath}: ${JSON.stringify(value)}`);\n } else {\n consola.log(` ${keyPath}`);\n }\n }\n\n consola.log('');\n consola.info(`Total: ${filtered.length} / ${allKeys.length} keys`);\n } catch (error) {\n consola.error(error);\n }\n },\n});\n","/**\n * Check for missing translation keys\n */\n\nimport { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport * as path from 'node:path';\nimport {\n detectLocaleConfig,\n getDefaultLocalesDir,\n loadLocale,\n getAllKeys,\n getValueAtPath,\n} from '../utils';\n\nexport const checkCommand = defineCommand({\n meta: {\n name: 'check',\n description: 'Find missing translation keys across locales',\n },\n args: {\n dir: {\n type: 'string',\n alias: 'd',\n description: 'Locales directory',\n },\n base: {\n type: 'string',\n alias: 'b',\n description: 'Base locale to compare against',\n default: 'en',\n },\n fix: {\n type: 'boolean',\n description: 'Show suggested fixes',\n default: false,\n },\n },\n async run({ args }) {\n const localesDir = args.dir\n ? path.resolve(process.cwd(), args.dir)\n : getDefaultLocalesDir();\n\n try {\n const config = detectLocaleConfig(localesDir);\n const baseLocale = args.base || 'en';\n\n if (!config.locales.includes(baseLocale)) {\n consola.error(`Base locale \"${baseLocale}\" not found.`);\n return;\n }\n\n consola.info(`Checking locales in ${localesDir}`);\n consola.info(`Base locale: ${baseLocale}`);\n consola.log('');\n\n // Load base locale\n const baseTranslations = await loadLocale(config, baseLocale);\n const baseKeys = getAllKeys(baseTranslations);\n\n let totalMissing = 0;\n const missingByLocale: Record<string, string[]> = {};\n\n // Check each locale\n for (const locale of config.locales) {\n if (locale === baseLocale) continue;\n\n const translations = await loadLocale(config, locale);\n const missing: string[] = [];\n\n for (const { path: keyPath } of baseKeys) {\n const value = getValueAtPath(translations, keyPath);\n if (value === undefined) {\n missing.push(keyPath);\n }\n }\n\n if (missing.length > 0) {\n missingByLocale[locale] = missing;\n totalMissing += missing.length;\n }\n }\n\n // Report\n if (totalMissing === 0) {\n consola.success('All locales have all keys!');\n return;\n }\n\n consola.warn(`Found ${totalMissing} missing keys`);\n consola.log('');\n\n for (const [locale, missing] of Object.entries(missingByLocale)) {\n consola.log(`${locale} (${missing.length} missing):`);\n for (const key of missing.slice(0, 10)) {\n consola.log(` - ${key}`);\n }\n if (missing.length > 10) {\n consola.log(` ... and ${missing.length - 10} more`);\n }\n consola.log('');\n }\n\n // Summary\n consola.box({\n title: 'Summary',\n message: [\n `Base: ${baseLocale} (${baseKeys.length} keys)`,\n `Checked: ${config.locales.length - 1} locales`,\n `Missing: ${totalMissing} total`,\n ].join('\\n'),\n });\n } catch (error) {\n consola.error(error);\n }\n },\n});\n","/**\n * Add translation key to all locales\n */\n\nimport { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport * as path from 'node:path';\nimport {\n detectLocaleConfig,\n getDefaultLocalesDir,\n readLocaleFile,\n writeLocaleFile,\n getValueAtPath,\n setValueAtPath,\n renderTsLocale,\n loadLocale,\n} from '../utils';\n\nexport const addCommand = defineCommand({\n meta: {\n name: 'add',\n description: 'Add a translation key to all locales',\n },\n args: {\n key: {\n type: 'positional',\n description: 'Key path (e.g., \"tools.tour.next\")',\n required: true,\n },\n translations: {\n type: 'positional',\n description: 'JSON object with translations (e.g., \\'{\"en\":\"Next\",\"ru\":\"Далее\"}\\')',\n required: true,\n },\n dir: {\n type: 'string',\n alias: 'd',\n description: 'Locales directory',\n },\n force: {\n type: 'boolean',\n alias: 'f',\n description: 'Overwrite existing keys',\n default: false,\n },\n },\n async run({ args }) {\n const localesDir = args.dir\n ? path.resolve(process.cwd(), args.dir)\n : getDefaultLocalesDir();\n\n try {\n const config = detectLocaleConfig(localesDir);\n const keyPath = args.key;\n\n // Parse translations\n let translations: Record<string, string>;\n try {\n translations = JSON.parse(args.translations);\n } catch {\n consola.error('Invalid JSON for translations');\n consola.log('Example: \\'{\"en\":\"Next\",\"ru\":\"Далее\"}\\'');\n return;\n }\n\n consola.info(`Adding key: ${keyPath}`);\n consola.info(`Directory: ${localesDir}`);\n consola.log('');\n\n let updated = 0;\n let skipped = 0;\n\n for (const locale of config.locales) {\n const translation = translations[locale];\n\n if (!translation) {\n consola.warn(` ${locale}: No translation provided, skipping`);\n skipped++;\n continue;\n }\n\n // Check if key exists\n const existing = await loadLocale(config, locale);\n const existingValue = getValueAtPath(existing, keyPath);\n\n if (existingValue !== undefined && !args.force) {\n consola.warn(` ${locale}: Key exists, use --force to overwrite`);\n skipped++;\n continue;\n }\n\n // Read and update file\n const { content, exportName } = readLocaleFile(config, locale);\n\n if (config.fileExtension === '.json') {\n const json = JSON.parse(content);\n setValueAtPath(json, keyPath, translation);\n writeLocaleFile(config, locale, JSON.stringify(json, null, 2));\n } else {\n // `existing` is the same file, already parsed by `loadLocale` above\n // for the key-exists check. Editing that object and re-serialising is\n // what makes nested keys work: the old text-splicing path inserted a\n // duplicate parent block and lost its siblings.\n setValueAtPath(existing, keyPath, translation);\n writeLocaleFile(config, locale, renderTsLocale(content, exportName, existing));\n }\n\n consola.success(` ${locale}: Added \"${translation}\"`);\n updated++;\n }\n\n consola.log('');\n consola.box({\n title: 'Result',\n message: `Updated: ${updated}, Skipped: ${skipped}`,\n });\n } catch (error) {\n consola.error(error);\n }\n },\n});\n","#!/usr/bin/env node\n/**\n * i18n CLI - Unified tool for managing translations\n *\n * Usage:\n * pnpm i18n list [pattern] - List/search keys\n * pnpm i18n check - Find missing keys\n * pnpm i18n add <key> <json> - Add key to all locales\n *\n * Can work with:\n * - Package locales (default): packages/i18n/src/locales\n * - App locales: --dir apps/studio/i18n/locales\n */\n\nimport { defineCommand, runMain } from 'citty';\nimport { consola } from 'consola';\n\nimport { listCommand } from './commands/list';\nimport { checkCommand } from './commands/check';\nimport { addCommand } from './commands/add';\n\nconst main = defineCommand({\n meta: {\n name: 'i18n',\n version: '1.0.0',\n description: 'CLI for managing i18n translations',\n },\n subCommands: {\n list: listCommand,\n check: checkCommand,\n add: addCommand,\n },\n setup() {\n consola.box('i18n CLI');\n },\n});\n\nrunMain(main);\n"]}
package/dist/index.mjs CHANGED
@@ -647,9 +647,7 @@ function I18nProvider({
647
647
  [onLocaleChange]
648
648
  );
649
649
  React.useEffect(() => {
650
- if (initialLocale !== locale) {
651
- setLocaleState(initialLocale);
652
- }
650
+ setLocaleState((current) => current === initialLocale ? current : initialLocale);
653
651
  }, [initialLocale]);
654
652
  const value = React.useMemo(
655
653
  () => ({