@intlpullhq/cli 0.1.5 → 0.1.6
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 +138 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7527,7 +7527,7 @@ function runCompare(options) {
|
|
|
7527
7527
|
import { useState as useState15, useEffect as useEffect12 } from "react";
|
|
7528
7528
|
import { render as render11, Box as Box25, Text as Text26 } from "ink";
|
|
7529
7529
|
import { glob } from "glob";
|
|
7530
|
-
import { readFileSync as readFileSync6 } from "fs";
|
|
7530
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
7531
7531
|
|
|
7532
7532
|
// src/components/TaskList.tsx
|
|
7533
7533
|
import { Box as Box24, Text as Text25 } from "ink";
|
|
@@ -7579,14 +7579,23 @@ function TaskList({ tasks, title }) {
|
|
|
7579
7579
|
|
|
7580
7580
|
// src/commands/check.tsx
|
|
7581
7581
|
import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
7582
|
+
var LANG_CODE_REGEX = /^[a-z]{2}([-_][a-zA-Z]{2})?$/i;
|
|
7582
7583
|
function CheckCommand({ options }) {
|
|
7583
|
-
const
|
|
7584
|
+
const baseTasks = [
|
|
7584
7585
|
{ id: "config", label: "Loading configuration", status: "pending" },
|
|
7585
7586
|
{ id: "scan", label: "Scanning translation files", status: "pending" },
|
|
7586
7587
|
{ id: "compare", label: "Comparing translations", status: "pending" },
|
|
7587
7588
|
{ id: "report", label: "Generating report", status: "pending" }
|
|
7588
|
-
]
|
|
7589
|
+
];
|
|
7590
|
+
if (options.fix) {
|
|
7591
|
+
baseTasks.push({ id: "fix", label: "Fixing missing translations", status: "pending" });
|
|
7592
|
+
}
|
|
7593
|
+
if (options.output) {
|
|
7594
|
+
baseTasks.push({ id: "output", label: "Writing report file", status: "pending" });
|
|
7595
|
+
}
|
|
7596
|
+
const [tasks, setTasks] = useState15(baseTasks);
|
|
7589
7597
|
const [result, setResult] = useState15(null);
|
|
7598
|
+
const [fixResult, setFixResult] = useState15(null);
|
|
7590
7599
|
const [error, setError] = useState15(null);
|
|
7591
7600
|
const [done, setDone] = useState15(false);
|
|
7592
7601
|
const updateTask = (id, update) => {
|
|
@@ -7636,10 +7645,10 @@ function CheckCommand({ options }) {
|
|
|
7636
7645
|
const parts = file.split("/");
|
|
7637
7646
|
const fileName = parts[parts.length - 1];
|
|
7638
7647
|
const dirName = parts[parts.length - 2];
|
|
7639
|
-
if (
|
|
7648
|
+
if (LANG_CODE_REGEX.test(dirName)) {
|
|
7640
7649
|
if (!filesByLang[dirName]) filesByLang[dirName] = [];
|
|
7641
7650
|
filesByLang[dirName].push(file);
|
|
7642
|
-
} else if (
|
|
7651
|
+
} else if (LANG_CODE_REGEX.test(fileName.replace(".json", ""))) {
|
|
7643
7652
|
const lang = fileName.replace(".json", "");
|
|
7644
7653
|
if (!filesByLang[lang]) filesByLang[lang] = [];
|
|
7645
7654
|
filesByLang[lang].push(file);
|
|
@@ -7727,14 +7736,77 @@ ${parseErrors.join("\n")}`);
|
|
|
7727
7736
|
status: missingKeys.length > 0 ? "warning" : "success",
|
|
7728
7737
|
output: missingKeys.length > 0 ? `Found ${missingKeys.length} missing key(s)` : "All translations complete!"
|
|
7729
7738
|
});
|
|
7730
|
-
|
|
7739
|
+
const checkResult = {
|
|
7731
7740
|
sourceLanguage,
|
|
7732
7741
|
targetLanguages,
|
|
7733
7742
|
totalKeys,
|
|
7734
7743
|
missingKeys: missingKeys.slice(0, 10),
|
|
7735
7744
|
// Show first 10
|
|
7736
7745
|
coveragePercentage
|
|
7737
|
-
}
|
|
7746
|
+
};
|
|
7747
|
+
setResult(checkResult);
|
|
7748
|
+
if (options.fix && missingKeys.length > 0) {
|
|
7749
|
+
updateTask("fix", { status: "running" });
|
|
7750
|
+
let filesModified = 0;
|
|
7751
|
+
let keysAdded = 0;
|
|
7752
|
+
for (const targetLang of targetLanguages) {
|
|
7753
|
+
if (!filesByLang[targetLang]) continue;
|
|
7754
|
+
for (const file of filesByLang[targetLang]) {
|
|
7755
|
+
let content = {};
|
|
7756
|
+
try {
|
|
7757
|
+
content = JSON.parse(readFileSync6(file, "utf-8"));
|
|
7758
|
+
} catch {
|
|
7759
|
+
content = {};
|
|
7760
|
+
}
|
|
7761
|
+
const flatTarget = flattenObjectToRecord(content);
|
|
7762
|
+
let modified = false;
|
|
7763
|
+
for (const mk of missingKeys) {
|
|
7764
|
+
if (mk.missingIn.includes(targetLang) && !(mk.key in flatTarget)) {
|
|
7765
|
+
setNestedValue(content, mk.key, `[${targetLang.toUpperCase()}] ${mk.sourceValue}`);
|
|
7766
|
+
keysAdded++;
|
|
7767
|
+
modified = true;
|
|
7768
|
+
}
|
|
7769
|
+
}
|
|
7770
|
+
if (modified) {
|
|
7771
|
+
writeFileSync4(file, JSON.stringify(content, null, 2) + "\n");
|
|
7772
|
+
filesModified++;
|
|
7773
|
+
}
|
|
7774
|
+
}
|
|
7775
|
+
}
|
|
7776
|
+
setFixResult({ filesModified, keysAdded });
|
|
7777
|
+
updateTask("fix", {
|
|
7778
|
+
status: keysAdded > 0 ? "success" : "warning",
|
|
7779
|
+
output: keysAdded > 0 ? `Added ${keysAdded} key(s) to ${filesModified} file(s)` : "No keys to fix"
|
|
7780
|
+
});
|
|
7781
|
+
}
|
|
7782
|
+
if (options.output) {
|
|
7783
|
+
updateTask("output", { status: "running" });
|
|
7784
|
+
const fullReport = {
|
|
7785
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7786
|
+
sourceLanguage,
|
|
7787
|
+
targetLanguages,
|
|
7788
|
+
totalKeys,
|
|
7789
|
+
missingKeysCount: missingKeys.length,
|
|
7790
|
+
missingKeys: missingKeys.map((mk) => ({
|
|
7791
|
+
key: mk.key,
|
|
7792
|
+
sourceValue: mk.sourceValue,
|
|
7793
|
+
missingIn: mk.missingIn
|
|
7794
|
+
})),
|
|
7795
|
+
coveragePercentage
|
|
7796
|
+
};
|
|
7797
|
+
try {
|
|
7798
|
+
writeFileSync4(options.output, JSON.stringify(fullReport, null, 2) + "\n");
|
|
7799
|
+
updateTask("output", {
|
|
7800
|
+
status: "success",
|
|
7801
|
+
output: `Report written to ${options.output}`
|
|
7802
|
+
});
|
|
7803
|
+
} catch (writeErr) {
|
|
7804
|
+
updateTask("output", {
|
|
7805
|
+
status: "error",
|
|
7806
|
+
output: `Failed to write report: ${writeErr instanceof Error ? writeErr.message : "Unknown error"}`
|
|
7807
|
+
});
|
|
7808
|
+
}
|
|
7809
|
+
}
|
|
7738
7810
|
setDone(true);
|
|
7739
7811
|
} catch (err) {
|
|
7740
7812
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
@@ -7785,10 +7857,31 @@ ${parseErrors.join("\n")}`);
|
|
|
7785
7857
|
] }) })
|
|
7786
7858
|
] }, i))
|
|
7787
7859
|
] }),
|
|
7788
|
-
|
|
7860
|
+
fixResult && fixResult.keysAdded > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7861
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.success, children: [
|
|
7862
|
+
icons.success,
|
|
7863
|
+
" Fixed ",
|
|
7864
|
+
fixResult.keysAdded,
|
|
7865
|
+
" key(s) in ",
|
|
7866
|
+
fixResult.filesModified,
|
|
7867
|
+
" file(s)"
|
|
7868
|
+
] }),
|
|
7869
|
+
/* @__PURE__ */ jsxs26(Box25, { marginTop: 1, children: [
|
|
7870
|
+
/* @__PURE__ */ jsxs26(Text26, { color: colors.warning, children: [
|
|
7871
|
+
icons.warning,
|
|
7872
|
+
" "
|
|
7873
|
+
] }),
|
|
7874
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.textMuted, children: "Missing keys have been added with [LANG] prefix. Review and translate them." })
|
|
7875
|
+
] })
|
|
7876
|
+
] }),
|
|
7877
|
+
!options.fix && result.missingKeys.length > 0 && /* @__PURE__ */ jsx28(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
7789
7878
|
"Run ",
|
|
7790
7879
|
/* @__PURE__ */ jsx28(Text26, { color: colors.primary, children: "npx @intlpullhq/cli fix" }),
|
|
7791
7880
|
" to auto-generate missing translations"
|
|
7881
|
+
] }) }),
|
|
7882
|
+
options.output && /* @__PURE__ */ jsx28(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
7883
|
+
"Report saved to ",
|
|
7884
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.primary, children: options.output })
|
|
7792
7885
|
] }) })
|
|
7793
7886
|
] })
|
|
7794
7887
|
] });
|
|
@@ -7803,6 +7896,29 @@ function flattenObject2(obj, prefix, result) {
|
|
|
7803
7896
|
}
|
|
7804
7897
|
}
|
|
7805
7898
|
}
|
|
7899
|
+
function flattenObjectToRecord(obj, prefix = "") {
|
|
7900
|
+
const result = {};
|
|
7901
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
7902
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
7903
|
+
if (typeof value === "string") {
|
|
7904
|
+
result[newKey] = value;
|
|
7905
|
+
} else if (typeof value === "object" && value !== null) {
|
|
7906
|
+
Object.assign(result, flattenObjectToRecord(value, newKey));
|
|
7907
|
+
}
|
|
7908
|
+
}
|
|
7909
|
+
return result;
|
|
7910
|
+
}
|
|
7911
|
+
function setNestedValue(obj, key, value) {
|
|
7912
|
+
const parts = key.split(".");
|
|
7913
|
+
let current = obj;
|
|
7914
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
7915
|
+
if (!current[parts[i]] || typeof current[parts[i]] === "string") {
|
|
7916
|
+
current[parts[i]] = {};
|
|
7917
|
+
}
|
|
7918
|
+
current = current[parts[i]];
|
|
7919
|
+
}
|
|
7920
|
+
current[parts[parts.length - 1]] = value;
|
|
7921
|
+
}
|
|
7806
7922
|
function runCheck(options) {
|
|
7807
7923
|
render11(/* @__PURE__ */ jsx28(CheckCommand, { options }));
|
|
7808
7924
|
}
|
|
@@ -7810,7 +7926,7 @@ function runCheck(options) {
|
|
|
7810
7926
|
// src/commands/diff.tsx
|
|
7811
7927
|
import { useState as useState16, useEffect as useEffect13 } from "react";
|
|
7812
7928
|
import { render as render12, Box as Box26, Text as Text27 } from "ink";
|
|
7813
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
7929
|
+
import { readFileSync as readFileSync7, existsSync as existsSync6 } from "fs";
|
|
7814
7930
|
import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
7815
7931
|
async function fetchProjects5(apiUrl, apiKey) {
|
|
7816
7932
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
@@ -7890,7 +8006,7 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
7890
8006
|
];
|
|
7891
8007
|
let localFile = null;
|
|
7892
8008
|
for (const path4 of possiblePaths) {
|
|
7893
|
-
if (
|
|
8009
|
+
if (existsSync6(path4)) {
|
|
7894
8010
|
localFile = path4;
|
|
7895
8011
|
break;
|
|
7896
8012
|
}
|
|
@@ -8074,7 +8190,7 @@ function runDiff(options) {
|
|
|
8074
8190
|
// src/commands/fix.tsx
|
|
8075
8191
|
import { useState as useState17, useEffect as useEffect14 } from "react";
|
|
8076
8192
|
import { render as render13, Box as Box27, Text as Text28 } from "ink";
|
|
8077
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
8193
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
8078
8194
|
import { glob as glob2 } from "glob";
|
|
8079
8195
|
import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
8080
8196
|
function FixCommand({ options }) {
|
|
@@ -8110,7 +8226,8 @@ function FixCommand({ options }) {
|
|
|
8110
8226
|
`${localeDir}/**/common.json`,
|
|
8111
8227
|
`${localeDir}/**/*.json`,
|
|
8112
8228
|
`${localeDir}/*.json`,
|
|
8113
|
-
`./messages/*.json
|
|
8229
|
+
`./messages/*.json`,
|
|
8230
|
+
`./locales/**/*.json`
|
|
8114
8231
|
];
|
|
8115
8232
|
let localeFiles = [];
|
|
8116
8233
|
for (const pattern of patterns) {
|
|
@@ -8123,15 +8240,16 @@ function FixCommand({ options }) {
|
|
|
8123
8240
|
if (localeFiles.length === 0) {
|
|
8124
8241
|
throw new Error(`No translation files found in ${localeDir}`);
|
|
8125
8242
|
}
|
|
8243
|
+
const LANG_CODE_REGEX2 = /^[a-z]{2}([-_][a-zA-Z]{2})?$/i;
|
|
8126
8244
|
const filesByLang = {};
|
|
8127
8245
|
for (const file of localeFiles) {
|
|
8128
8246
|
const parts = file.split("/");
|
|
8129
8247
|
const fileName = parts[parts.length - 1];
|
|
8130
8248
|
const dirName = parts[parts.length - 2];
|
|
8131
|
-
if (
|
|
8249
|
+
if (LANG_CODE_REGEX2.test(dirName)) {
|
|
8132
8250
|
if (!filesByLang[dirName]) filesByLang[dirName] = [];
|
|
8133
8251
|
filesByLang[dirName].push(file);
|
|
8134
|
-
} else if (
|
|
8252
|
+
} else if (LANG_CODE_REGEX2.test(fileName.replace(".json", ""))) {
|
|
8135
8253
|
const lang = fileName.replace(".json", "");
|
|
8136
8254
|
if (!filesByLang[lang]) filesByLang[lang] = [];
|
|
8137
8255
|
filesByLang[lang].push(file);
|
|
@@ -8180,13 +8298,13 @@ ${parseErrors.join("\n")}`);
|
|
|
8180
8298
|
for (const key of sourceKeys) {
|
|
8181
8299
|
if (!(key in flatTarget)) {
|
|
8182
8300
|
const sourceValue = flatSource[key] || "";
|
|
8183
|
-
|
|
8301
|
+
setNestedValue2(content, key, `[${targetLang.toUpperCase()}] ${sourceValue}`);
|
|
8184
8302
|
keysAdded++;
|
|
8185
8303
|
modified = true;
|
|
8186
8304
|
}
|
|
8187
8305
|
}
|
|
8188
8306
|
if (modified && !options.dryRun) {
|
|
8189
|
-
|
|
8307
|
+
writeFileSync5(file, JSON.stringify(content, null, 2) + "\n");
|
|
8190
8308
|
filesModified++;
|
|
8191
8309
|
} else if (modified) {
|
|
8192
8310
|
filesModified++;
|
|
@@ -8265,7 +8383,7 @@ function flattenObject4(obj, prefix = "") {
|
|
|
8265
8383
|
}
|
|
8266
8384
|
return result;
|
|
8267
8385
|
}
|
|
8268
|
-
function
|
|
8386
|
+
function setNestedValue2(obj, key, value) {
|
|
8269
8387
|
const parts = key.split(".");
|
|
8270
8388
|
let current = obj;
|
|
8271
8389
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
@@ -8284,7 +8402,7 @@ function runFix(options) {
|
|
|
8284
8402
|
import { useState as useState18, useEffect as useEffect15, useCallback, useRef as useRef2 } from "react";
|
|
8285
8403
|
import { render as render14, Box as Box28, Text as Text29, useApp as useApp6, useInput as useInput7 } from "ink";
|
|
8286
8404
|
import Spinner13 from "ink-spinner";
|
|
8287
|
-
import { writeFileSync as
|
|
8405
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
8288
8406
|
import { join as join7 } from "path";
|
|
8289
8407
|
import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
8290
8408
|
async function fetchProjects6(apiUrl, apiKey) {
|
|
@@ -8424,7 +8542,7 @@ function writeTranslationFiles(bundle, outputDir, format, languages) {
|
|
|
8424
8542
|
for (const locale of targetLanguages) {
|
|
8425
8543
|
if (!bundle[locale]) continue;
|
|
8426
8544
|
const localeDir = join7(outputDir, locale);
|
|
8427
|
-
if (!
|
|
8545
|
+
if (!existsSync8(localeDir)) {
|
|
8428
8546
|
mkdirSync3(localeDir, { recursive: true });
|
|
8429
8547
|
}
|
|
8430
8548
|
let content;
|
|
@@ -8446,7 +8564,7 @@ function writeTranslationFiles(bundle, outputDir, format, languages) {
|
|
|
8446
8564
|
break;
|
|
8447
8565
|
}
|
|
8448
8566
|
const filePath = join7(localeDir, filename);
|
|
8449
|
-
|
|
8567
|
+
writeFileSync6(filePath, content);
|
|
8450
8568
|
writtenFiles.push(filePath);
|
|
8451
8569
|
}
|
|
8452
8570
|
return writtenFiles;
|
package/package.json
CHANGED