@intlpullhq/cli 0.1.5 → 0.1.7
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/README.md +48 -0
- package/dist/index.js +801 -925
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -918,8 +918,8 @@ import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
|
918
918
|
import { Box as Box9, Text as Text10, useApp as useApp2 } from "ink";
|
|
919
919
|
import SelectInput2 from "ink-select-input";
|
|
920
920
|
import Spinner3 from "ink-spinner";
|
|
921
|
-
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
922
|
-
import { join, dirname } from "path";
|
|
921
|
+
import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
922
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
923
923
|
|
|
924
924
|
// src/commands/pull/api.ts
|
|
925
925
|
async function fetchProjects2(apiUrl, apiKey) {
|
|
@@ -1080,16 +1080,23 @@ async function fetchTranslationsParallel(projectId, apiUrl, apiKey, options) {
|
|
|
1080
1080
|
})
|
|
1081
1081
|
);
|
|
1082
1082
|
const bundle = {};
|
|
1083
|
+
const namespacedBundle = {};
|
|
1083
1084
|
for (const result of results) {
|
|
1084
1085
|
if (!bundle[result.language]) {
|
|
1085
1086
|
bundle[result.language] = {};
|
|
1086
1087
|
}
|
|
1087
1088
|
Object.assign(bundle[result.language], result.translations);
|
|
1089
|
+
if (!namespacedBundle[result.language]) {
|
|
1090
|
+
namespacedBundle[result.language] = {};
|
|
1091
|
+
}
|
|
1092
|
+
namespacedBundle[result.language][result.namespace] = result.translations;
|
|
1088
1093
|
}
|
|
1089
1094
|
return {
|
|
1090
1095
|
bundle,
|
|
1096
|
+
namespacedBundle,
|
|
1091
1097
|
version: projectData.current_version || "1.0.0",
|
|
1092
|
-
namespaceCount: namespaces.length
|
|
1098
|
+
namespaceCount: namespaces.length,
|
|
1099
|
+
namespaces
|
|
1093
1100
|
};
|
|
1094
1101
|
}
|
|
1095
1102
|
async function fetchProjectInfo(projectId, apiUrl, apiKey) {
|
|
@@ -1190,6 +1197,51 @@ function MultiSelect({ items, selected, onToggle, onSubmit, isActive = true }) {
|
|
|
1190
1197
|
] });
|
|
1191
1198
|
}
|
|
1192
1199
|
|
|
1200
|
+
// src/lib/output-resolver.ts
|
|
1201
|
+
import { join, dirname, extname } from "path";
|
|
1202
|
+
import { mkdirSync, existsSync } from "fs";
|
|
1203
|
+
function hasNamespacePlaceholder(pattern) {
|
|
1204
|
+
return pattern.includes("[namespace]") || pattern.includes("{namespace}");
|
|
1205
|
+
}
|
|
1206
|
+
function getFormatExtension(format) {
|
|
1207
|
+
switch (format) {
|
|
1208
|
+
case "yaml":
|
|
1209
|
+
case "yml":
|
|
1210
|
+
return ".yaml";
|
|
1211
|
+
case "ts":
|
|
1212
|
+
case "typescript":
|
|
1213
|
+
return ".ts";
|
|
1214
|
+
case "json":
|
|
1215
|
+
default:
|
|
1216
|
+
return ".json";
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
function resolveOutputPath(pattern, options) {
|
|
1220
|
+
let resolved = pattern;
|
|
1221
|
+
resolved = resolved.replace(/\[locale\]/g, options.locale);
|
|
1222
|
+
resolved = resolved.replace(/\{locale\}/g, options.locale);
|
|
1223
|
+
if (options.namespace) {
|
|
1224
|
+
resolved = resolved.replace(/\[namespace\]/g, options.namespace);
|
|
1225
|
+
resolved = resolved.replace(/\{namespace\}/g, options.namespace);
|
|
1226
|
+
}
|
|
1227
|
+
if (options.format) {
|
|
1228
|
+
const currentExt = extname(resolved);
|
|
1229
|
+
const targetExt = getFormatExtension(options.format);
|
|
1230
|
+
if (currentExt && currentExt !== targetExt) {
|
|
1231
|
+
resolved = resolved.slice(0, -currentExt.length) + targetExt;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return {
|
|
1235
|
+
path: resolved,
|
|
1236
|
+
dir: dirname(resolved)
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
function ensureDir(dir) {
|
|
1240
|
+
if (!existsSync(dir)) {
|
|
1241
|
+
mkdirSync(dir, { recursive: true });
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1193
1245
|
// src/commands/pull/components/interactive-pull.tsx
|
|
1194
1246
|
import { Fragment, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1195
1247
|
function InteractivePull({ initialOptions }) {
|
|
@@ -1240,43 +1292,87 @@ function InteractivePull({ initialOptions }) {
|
|
|
1240
1292
|
const apiUrl = globalConfig.apiUrl || "https://api.intlpull.com";
|
|
1241
1293
|
const apiKey = resolved?.key;
|
|
1242
1294
|
setPullState((s) => ({ ...s, message: "Fetching translations..." }));
|
|
1243
|
-
const
|
|
1295
|
+
const result = await fetchTranslationsParallel(
|
|
1244
1296
|
projectId,
|
|
1245
1297
|
apiUrl,
|
|
1246
1298
|
apiKey,
|
|
1247
1299
|
{
|
|
1248
1300
|
languages: state.selectedLanguages.length > 0 ? state.selectedLanguages : void 0,
|
|
1249
|
-
platform: state.selectedPlatform
|
|
1301
|
+
platform: state.selectedPlatform,
|
|
1302
|
+
branch: state.selectedBranch,
|
|
1303
|
+
onProgress: (completed, total) => {
|
|
1304
|
+
setPullState((s) => ({
|
|
1305
|
+
...s,
|
|
1306
|
+
message: `Fetching translations (${completed}/${total})...`
|
|
1307
|
+
}));
|
|
1308
|
+
}
|
|
1250
1309
|
}
|
|
1251
1310
|
);
|
|
1311
|
+
const { bundle, namespacedBundle, version, namespaces } = result;
|
|
1252
1312
|
const locales = state.selectedLanguages.length > 0 ? state.selectedLanguages : Object.keys(bundle);
|
|
1253
1313
|
if (locales.length === 0) {
|
|
1254
1314
|
throw new Error("No translations found for this project.");
|
|
1255
1315
|
}
|
|
1256
|
-
const
|
|
1316
|
+
const outputPattern = initialOptions.output || projectConfig?.output || (projectConfig?.outputDir ? `${projectConfig.outputDir}/[locale].json` : "./messages/[locale].json");
|
|
1257
1317
|
const format = state.selectedFormat;
|
|
1258
1318
|
const ext = getFileExtension(format);
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1319
|
+
const useNamespaces = hasNamespacePlaceholder(outputPattern) && namespacedBundle && namespaces.length > 0;
|
|
1320
|
+
if (useNamespaces) {
|
|
1321
|
+
setPullState((s) => ({
|
|
1322
|
+
...s,
|
|
1323
|
+
message: `Writing ${locales.length} \xD7 ${namespaces.length} translation files...`
|
|
1324
|
+
}));
|
|
1325
|
+
} else {
|
|
1326
|
+
setPullState((s) => ({
|
|
1327
|
+
...s,
|
|
1328
|
+
message: `Writing ${locales.length} translation files...`
|
|
1329
|
+
}));
|
|
1330
|
+
}
|
|
1263
1331
|
const writtenFiles = [];
|
|
1264
1332
|
let totalKeyCount = 0;
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1333
|
+
if (useNamespaces && namespacedBundle) {
|
|
1334
|
+
for (const locale of locales) {
|
|
1335
|
+
if (!namespacedBundle[locale]) continue;
|
|
1336
|
+
for (const namespace of namespaces) {
|
|
1337
|
+
const translations = namespacedBundle[locale][namespace];
|
|
1338
|
+
if (!translations || Object.keys(translations).length === 0) continue;
|
|
1339
|
+
const { path: outputPath, dir } = resolveOutputPath(outputPattern, {
|
|
1340
|
+
locale,
|
|
1341
|
+
namespace,
|
|
1342
|
+
format
|
|
1343
|
+
});
|
|
1344
|
+
ensureDir(dir);
|
|
1345
|
+
const content = formatTranslations(translations, format, locale);
|
|
1346
|
+
writeFileSync(outputPath, content);
|
|
1347
|
+
writtenFiles.push(outputPath);
|
|
1348
|
+
totalKeyCount += Object.keys(translations).length;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
} else {
|
|
1352
|
+
for (const locale of locales) {
|
|
1353
|
+
if (!bundle[locale]) continue;
|
|
1354
|
+
let outputPath;
|
|
1355
|
+
if (outputPattern.includes("[locale]") || outputPattern.includes("{locale}")) {
|
|
1356
|
+
const resolved2 = resolveOutputPath(outputPattern, { locale, format });
|
|
1357
|
+
outputPath = resolved2.path;
|
|
1358
|
+
ensureDir(resolved2.dir);
|
|
1359
|
+
} else {
|
|
1360
|
+
outputPath = join2(outputPattern, `${locale}${ext}`);
|
|
1361
|
+
const dir = dirname2(outputPath);
|
|
1362
|
+
if (!existsSync2(dir)) {
|
|
1363
|
+
mkdirSync2(dir, { recursive: true });
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
const content = formatTranslations(bundle[locale], format, locale);
|
|
1367
|
+
writeFileSync(outputPath, content);
|
|
1368
|
+
writtenFiles.push(outputPath);
|
|
1369
|
+
totalKeyCount += Object.keys(bundle[locale]).length;
|
|
1271
1370
|
}
|
|
1272
|
-
const content = formatTranslations(bundle[locale], format, locale);
|
|
1273
|
-
writeFileSync(outputPath, content);
|
|
1274
|
-
writtenFiles.push(outputPath);
|
|
1275
|
-
totalKeyCount += Object.keys(bundle[locale]).length;
|
|
1276
1371
|
}
|
|
1372
|
+
const successMessage = useNamespaces ? `Successfully pulled ${locales.length} languages \xD7 ${namespaces.length} namespaces` : "Successfully pulled translations";
|
|
1277
1373
|
setPullState({
|
|
1278
1374
|
status: "success",
|
|
1279
|
-
message:
|
|
1375
|
+
message: successMessage,
|
|
1280
1376
|
files: writtenFiles,
|
|
1281
1377
|
details: {
|
|
1282
1378
|
version,
|
|
@@ -1545,8 +1641,8 @@ function InteractivePull({ initialOptions }) {
|
|
|
1545
1641
|
import { useState as useState7, useEffect as useEffect6 } from "react";
|
|
1546
1642
|
import { Box as Box10, Text as Text11 } from "ink";
|
|
1547
1643
|
import Spinner4 from "ink-spinner";
|
|
1548
|
-
import { writeFileSync as writeFileSync2, mkdirSync as
|
|
1549
|
-
import { join as
|
|
1644
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
|
|
1645
|
+
import { join as join3, dirname as dirname3 } from "path";
|
|
1550
1646
|
|
|
1551
1647
|
// src/lib/frameworks.ts
|
|
1552
1648
|
var LIBRARIES2 = {
|
|
@@ -1559,10 +1655,13 @@ var LIBRARIES2 = {
|
|
|
1559
1655
|
translationCall: (key, params) => params ? `t('${key}', ${params})` : `t('${key}')`,
|
|
1560
1656
|
pluralSupport: true,
|
|
1561
1657
|
defaultOutputDir: "./messages",
|
|
1562
|
-
|
|
1658
|
+
// next-intl uses flat files by default, but supports namespaced via getMessage()
|
|
1659
|
+
// Users can configure messages/[locale]/[namespace].json if they prefer namespacing
|
|
1660
|
+
filePattern: "[locale].json",
|
|
1563
1661
|
setupInstructions: [
|
|
1564
1662
|
"npm install next-intl",
|
|
1565
|
-
"Create messages/
|
|
1663
|
+
"Create messages/[locale].json files",
|
|
1664
|
+
"Configure src/i18n/request.ts to load from messages/",
|
|
1566
1665
|
"Add NextIntlClientProvider to layout",
|
|
1567
1666
|
"Configure next.config.js with createNextIntlPlugin()"
|
|
1568
1667
|
]
|
|
@@ -1576,10 +1675,13 @@ var LIBRARIES2 = {
|
|
|
1576
1675
|
translationCall: (key, params) => params ? `t('${key}', ${params})` : `t('${key}')`,
|
|
1577
1676
|
pluralSupport: true,
|
|
1578
1677
|
defaultOutputDir: "./public/locales",
|
|
1579
|
-
|
|
1678
|
+
// react-i18next with i18next-http-backend expects this structure
|
|
1679
|
+
// IMPORTANT: Must match backend.loadPath in i18n.js config
|
|
1680
|
+
filePattern: "[locale]/[namespace].json",
|
|
1580
1681
|
setupInstructions: [
|
|
1581
|
-
"npm install react-i18next i18next",
|
|
1582
|
-
"Create public/locales/
|
|
1682
|
+
"npm install react-i18next i18next i18next-http-backend",
|
|
1683
|
+
"Create public/locales/[locale]/[namespace].json files",
|
|
1684
|
+
'Configure i18n.js with backend.loadPath: "/locales/{{lng}}/{{ns}}.json"',
|
|
1583
1685
|
"Initialize i18next with i18n.init()",
|
|
1584
1686
|
"Wrap app with I18nextProvider"
|
|
1585
1687
|
]
|
|
@@ -1593,11 +1695,11 @@ var LIBRARIES2 = {
|
|
|
1593
1695
|
translationCall: (key, params) => params ? `i18next.t('${key}', ${params})` : `i18next.t('${key}')`,
|
|
1594
1696
|
pluralSupport: true,
|
|
1595
1697
|
defaultOutputDir: "./locales",
|
|
1596
|
-
filePattern: "
|
|
1698
|
+
filePattern: "[locale]/[namespace].json",
|
|
1597
1699
|
setupInstructions: [
|
|
1598
1700
|
"npm install i18next",
|
|
1599
|
-
"Create locales/
|
|
1600
|
-
"Initialize i18next with
|
|
1701
|
+
"Create locales/[locale]/[namespace].json files",
|
|
1702
|
+
"Initialize i18next with resources or backend loader"
|
|
1601
1703
|
]
|
|
1602
1704
|
},
|
|
1603
1705
|
"react-intl": {
|
|
@@ -1609,10 +1711,11 @@ var LIBRARIES2 = {
|
|
|
1609
1711
|
translationCall: (key, params) => params ? `intl.formatMessage({ id: '${key}' }, ${params})` : `intl.formatMessage({ id: '${key}' })`,
|
|
1610
1712
|
pluralSupport: true,
|
|
1611
1713
|
defaultOutputDir: "./src/lang",
|
|
1612
|
-
|
|
1714
|
+
// react-intl typically uses flat files (messages extracted by ID)
|
|
1715
|
+
filePattern: "[locale].json",
|
|
1613
1716
|
setupInstructions: [
|
|
1614
1717
|
"npm install react-intl",
|
|
1615
|
-
"Create src/lang/
|
|
1718
|
+
"Create src/lang/[locale].json files",
|
|
1616
1719
|
"Wrap app with IntlProvider",
|
|
1617
1720
|
"Import and pass messages to IntlProvider"
|
|
1618
1721
|
]
|
|
@@ -1626,10 +1729,11 @@ var LIBRARIES2 = {
|
|
|
1626
1729
|
translationCall: (key, params) => params ? `t('${key}', ${params})` : `t('${key}')`,
|
|
1627
1730
|
pluralSupport: true,
|
|
1628
1731
|
defaultOutputDir: "./src/locales",
|
|
1629
|
-
|
|
1732
|
+
// vue-i18n supports both flat and namespaced, defaulting to flat
|
|
1733
|
+
filePattern: "[locale].json",
|
|
1630
1734
|
setupInstructions: [
|
|
1631
1735
|
"npm install vue-i18n",
|
|
1632
|
-
"Create src/locales/
|
|
1736
|
+
"Create src/locales/[locale].json files",
|
|
1633
1737
|
"Create and configure i18n instance",
|
|
1634
1738
|
"Use app.use(i18n) in main.js"
|
|
1635
1739
|
]
|
|
@@ -1643,10 +1747,10 @@ var LIBRARIES2 = {
|
|
|
1643
1747
|
translationCall: (key, params) => params ? `$_('${key}', ${params})` : `$_('${key}')`,
|
|
1644
1748
|
pluralSupport: true,
|
|
1645
1749
|
defaultOutputDir: "./src/lib/i18n",
|
|
1646
|
-
filePattern: "
|
|
1750
|
+
filePattern: "[locale].json",
|
|
1647
1751
|
setupInstructions: [
|
|
1648
1752
|
"npm install svelte-i18n",
|
|
1649
|
-
"Create src/lib/i18n/
|
|
1753
|
+
"Create src/lib/i18n/[locale].json files",
|
|
1650
1754
|
"Initialize with init() and register()",
|
|
1651
1755
|
"Use $_ or $t stores in components"
|
|
1652
1756
|
]
|
|
@@ -1660,10 +1764,10 @@ var LIBRARIES2 = {
|
|
|
1660
1764
|
translationCall: (key, params) => params ? `t('${key}', ${params})` : `t('${key}')`,
|
|
1661
1765
|
pluralSupport: true,
|
|
1662
1766
|
defaultOutputDir: "./public/locales",
|
|
1663
|
-
filePattern: "
|
|
1767
|
+
filePattern: "[locale]/[namespace].json",
|
|
1664
1768
|
setupInstructions: [
|
|
1665
1769
|
"npx astro add astro-i18next",
|
|
1666
|
-
"Create public/locales/
|
|
1770
|
+
"Create public/locales/[locale]/[namespace].json files",
|
|
1667
1771
|
"Configure astro-i18next in astro.config.mjs"
|
|
1668
1772
|
]
|
|
1669
1773
|
}
|
|
@@ -1742,8 +1846,10 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
1742
1846
|
const branchDisplay = branch && branch !== "main" ? ` from branch '${branch}'` : "";
|
|
1743
1847
|
setState((s) => ({ ...s, status: "downloading", message: `Downloading ${targetLanguages.length} languages${branchDisplay}...` }));
|
|
1744
1848
|
let bundle;
|
|
1849
|
+
let namespacedBundle;
|
|
1745
1850
|
let version;
|
|
1746
1851
|
let namespaceCount = 0;
|
|
1852
|
+
let namespaces = [];
|
|
1747
1853
|
if (useParallel) {
|
|
1748
1854
|
const result = await fetchTranslationsParallel(
|
|
1749
1855
|
projectId,
|
|
@@ -1762,8 +1868,10 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
1762
1868
|
}
|
|
1763
1869
|
);
|
|
1764
1870
|
bundle = result.bundle;
|
|
1871
|
+
namespacedBundle = result.namespacedBundle;
|
|
1765
1872
|
version = result.version;
|
|
1766
1873
|
namespaceCount = result.namespaceCount;
|
|
1874
|
+
namespaces = result.namespaces;
|
|
1767
1875
|
const hasContent = Object.values(bundle).some(
|
|
1768
1876
|
(langBundle) => Object.keys(langBundle).length > 0
|
|
1769
1877
|
);
|
|
@@ -1776,6 +1884,8 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
1776
1884
|
});
|
|
1777
1885
|
bundle = exportResult.bundle;
|
|
1778
1886
|
version = exportResult.version;
|
|
1887
|
+
namespacedBundle = void 0;
|
|
1888
|
+
namespaces = [];
|
|
1779
1889
|
}
|
|
1780
1890
|
} else {
|
|
1781
1891
|
const result = await fetchTranslations(projectId, apiUrl, apiKey, {
|
|
@@ -1791,23 +1901,46 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
1791
1901
|
const writtenFiles = [];
|
|
1792
1902
|
let totalKeyCount = 0;
|
|
1793
1903
|
const filePattern = libraryConfig?.filePattern || "{locale}.json";
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1904
|
+
const useNamespaceFiles = hasNamespacePlaceholder(filePattern) && namespacedBundle && namespaces.length > 0;
|
|
1905
|
+
const outputPattern = filePattern.startsWith("./") ? filePattern : join3(outputDir, filePattern);
|
|
1906
|
+
if (useNamespaceFiles && namespacedBundle) {
|
|
1907
|
+
for (const locale of targetLanguages) {
|
|
1908
|
+
if (!namespacedBundle[locale]) continue;
|
|
1909
|
+
for (const namespace of namespaces) {
|
|
1910
|
+
const translations = namespacedBundle[locale][namespace];
|
|
1911
|
+
if (!translations || Object.keys(translations).length === 0) continue;
|
|
1912
|
+
const { path: outputPath, dir } = resolveOutputPath(outputPattern, {
|
|
1913
|
+
locale,
|
|
1914
|
+
namespace,
|
|
1915
|
+
format
|
|
1916
|
+
});
|
|
1917
|
+
ensureDir(dir);
|
|
1918
|
+
const content = formatTranslations(translations, format, locale);
|
|
1919
|
+
writeFileSync2(outputPath, content);
|
|
1920
|
+
writtenFiles.push(outputPath);
|
|
1921
|
+
totalKeyCount += Object.keys(translations).length;
|
|
1922
|
+
}
|
|
1802
1923
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1924
|
+
} else {
|
|
1925
|
+
for (const locale of targetLanguages) {
|
|
1926
|
+
if (!bundle[locale]) continue;
|
|
1927
|
+
let outputPath;
|
|
1928
|
+
if (filePattern.includes("{locale}") || filePattern.includes("[locale]")) {
|
|
1929
|
+
const { path: path4, dir } = resolveOutputPath(outputPattern, { locale, format });
|
|
1930
|
+
outputPath = path4;
|
|
1931
|
+
ensureDir(dir);
|
|
1932
|
+
} else {
|
|
1933
|
+
outputPath = join3(outputDir, `${locale}${ext}`);
|
|
1934
|
+
const dir = dirname3(outputPath);
|
|
1935
|
+
if (!existsSync3(dir)) {
|
|
1936
|
+
mkdirSync3(dir, { recursive: true });
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
const content = formatTranslations(bundle[locale], format, locale);
|
|
1940
|
+
writeFileSync2(outputPath, content);
|
|
1941
|
+
writtenFiles.push(outputPath);
|
|
1942
|
+
totalKeyCount += Object.keys(bundle[locale]).length;
|
|
1806
1943
|
}
|
|
1807
|
-
const content = formatTranslations(bundle[locale], format, locale);
|
|
1808
|
-
writeFileSync2(outputPath, content);
|
|
1809
|
-
writtenFiles.push(outputPath);
|
|
1810
|
-
totalKeyCount += Object.keys(bundle[locale]).length;
|
|
1811
1944
|
}
|
|
1812
1945
|
if (!projectConfig && projectId) {
|
|
1813
1946
|
const newConfig = {
|
|
@@ -1816,13 +1949,14 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
1816
1949
|
library: detected.library,
|
|
1817
1950
|
outputDir,
|
|
1818
1951
|
sourceLanguage: projectInfo.languages[0] || "en",
|
|
1819
|
-
namespaces: ["common"]
|
|
1952
|
+
namespaces: namespaces.length > 0 ? namespaces : ["common"]
|
|
1820
1953
|
};
|
|
1821
1954
|
saveProjectConfig(newConfig);
|
|
1822
1955
|
}
|
|
1956
|
+
const successMessage = useNamespaceFiles ? `Downloaded ${targetLanguages.length} languages \xD7 ${namespaceCount} namespaces` : useParallel ? `Downloaded ${targetLanguages.length} languages (${namespaceCount} namespaces merged)` : `Downloaded ${targetLanguages.length} languages`;
|
|
1823
1957
|
setState({
|
|
1824
1958
|
status: "success",
|
|
1825
|
-
message:
|
|
1959
|
+
message: successMessage,
|
|
1826
1960
|
project: projectInfo,
|
|
1827
1961
|
framework: detected,
|
|
1828
1962
|
outputDir,
|
|
@@ -1925,8 +2059,8 @@ function runPull(options) {
|
|
|
1925
2059
|
import React8 from "react";
|
|
1926
2060
|
import { render as render5, Box as Box11, Text as Text12, useInput as useInput3 } from "ink";
|
|
1927
2061
|
import Spinner5 from "ink-spinner";
|
|
1928
|
-
import { existsSync as
|
|
1929
|
-
import { join as
|
|
2062
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
2063
|
+
import { join as join5, basename as basename2, extname as extname3 } from "path";
|
|
1930
2064
|
|
|
1931
2065
|
// src/lib/languages.ts
|
|
1932
2066
|
var LANGUAGES = [
|
|
@@ -2097,8 +2231,8 @@ function formatLanguageDisplay(code, includeFlag = true) {
|
|
|
2097
2231
|
}
|
|
2098
2232
|
|
|
2099
2233
|
// src/lib/discovery.ts
|
|
2100
|
-
import { existsSync as
|
|
2101
|
-
import { join as
|
|
2234
|
+
import { existsSync as existsSync4, readdirSync, statSync, readFileSync } from "fs";
|
|
2235
|
+
import { join as join4, basename, extname as extname2, dirname as dirname4, relative } from "path";
|
|
2102
2236
|
import yaml from "js-yaml";
|
|
2103
2237
|
var COMMON_PATHS = [
|
|
2104
2238
|
// next-intl
|
|
@@ -2133,7 +2267,7 @@ function isLikelyLanguageCode(str) {
|
|
|
2133
2267
|
return false;
|
|
2134
2268
|
}
|
|
2135
2269
|
function isNamespaceFile(filename) {
|
|
2136
|
-
const name = basename(filename,
|
|
2270
|
+
const name = basename(filename, extname2(filename));
|
|
2137
2271
|
const commonNamespaces = [
|
|
2138
2272
|
"common",
|
|
2139
2273
|
"translation",
|
|
@@ -2183,7 +2317,7 @@ function countKeys(obj) {
|
|
|
2183
2317
|
return count;
|
|
2184
2318
|
}
|
|
2185
2319
|
function parseTranslationFile(filePath) {
|
|
2186
|
-
const ext =
|
|
2320
|
+
const ext = extname2(filePath).toLowerCase();
|
|
2187
2321
|
try {
|
|
2188
2322
|
if (ext === ".json") {
|
|
2189
2323
|
const content = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
@@ -2206,7 +2340,7 @@ function parseTranslationFile(filePath) {
|
|
|
2206
2340
|
}
|
|
2207
2341
|
function detectLanguageFromPath(filePath) {
|
|
2208
2342
|
const parts = filePath.split(/[\/\\]/);
|
|
2209
|
-
const fileName = basename(filePath,
|
|
2343
|
+
const fileName = basename(filePath, extname2(filePath));
|
|
2210
2344
|
if (isLikelyLanguageCode(fileName)) {
|
|
2211
2345
|
return normalizeLanguageCode(fileName);
|
|
2212
2346
|
}
|
|
@@ -2220,11 +2354,11 @@ function detectLanguageFromPath(filePath) {
|
|
|
2220
2354
|
}
|
|
2221
2355
|
function scanDirectory(dir, projectRoot, depth = 0, maxDepth = 3) {
|
|
2222
2356
|
const files = [];
|
|
2223
|
-
if (depth > maxDepth || !
|
|
2357
|
+
if (depth > maxDepth || !existsSync4(dir)) return files;
|
|
2224
2358
|
try {
|
|
2225
2359
|
const entries = readdirSync(dir);
|
|
2226
2360
|
for (const entry of entries) {
|
|
2227
|
-
const fullPath =
|
|
2361
|
+
const fullPath = join4(dir, entry);
|
|
2228
2362
|
const relativePath = relative(projectRoot, fullPath).replace(/\\/g, "/");
|
|
2229
2363
|
try {
|
|
2230
2364
|
const stat = statSync(fullPath);
|
|
@@ -2234,7 +2368,7 @@ function scanDirectory(dir, projectRoot, depth = 0, maxDepth = 3) {
|
|
|
2234
2368
|
}
|
|
2235
2369
|
files.push(...scanDirectory(fullPath, projectRoot, depth + 1, maxDepth));
|
|
2236
2370
|
} else if (stat.isFile()) {
|
|
2237
|
-
const ext =
|
|
2371
|
+
const ext = extname2(entry).toLowerCase();
|
|
2238
2372
|
if (![".json", ".yaml", ".yml", ".ts", ".js"].includes(ext)) continue;
|
|
2239
2373
|
if (entry.includes("config") || entry.includes("tsconfig")) continue;
|
|
2240
2374
|
const parsed = parseTranslationFile(fullPath);
|
|
@@ -2242,7 +2376,7 @@ function scanDirectory(dir, projectRoot, depth = 0, maxDepth = 3) {
|
|
|
2242
2376
|
const language = detectLanguageFromPath(fullPath);
|
|
2243
2377
|
if (!language) continue;
|
|
2244
2378
|
let namespace;
|
|
2245
|
-
const parentDir = basename(
|
|
2379
|
+
const parentDir = basename(dirname4(fullPath));
|
|
2246
2380
|
const fileName = basename(entry, ext);
|
|
2247
2381
|
if (isLikelyLanguageCode(parentDir)) {
|
|
2248
2382
|
namespace = fileName;
|
|
@@ -2282,7 +2416,7 @@ function determineStructure(files, baseDir) {
|
|
|
2282
2416
|
return maxFilesPerLang > 1 ? "nested" : "flat";
|
|
2283
2417
|
}
|
|
2284
2418
|
const hasNamespaceInFilename = files.some((f) => {
|
|
2285
|
-
const name = basename(f.path,
|
|
2419
|
+
const name = basename(f.path, extname2(f.path));
|
|
2286
2420
|
return name.includes(".") && !isLikelyLanguageCode(name);
|
|
2287
2421
|
});
|
|
2288
2422
|
if (hasNamespaceInFilename) return "namespace-based";
|
|
@@ -2345,13 +2479,13 @@ function discoverTranslationFiles(projectRoot = process.cwd()) {
|
|
|
2345
2479
|
const framework = detectFramework(projectRoot);
|
|
2346
2480
|
let files = [];
|
|
2347
2481
|
let baseDir = "";
|
|
2348
|
-
const configPath =
|
|
2349
|
-
if (
|
|
2482
|
+
const configPath = join4(projectRoot, ".intlpull.json");
|
|
2483
|
+
if (existsSync4(configPath)) {
|
|
2350
2484
|
try {
|
|
2351
2485
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
2352
2486
|
if (config.outputDir) {
|
|
2353
|
-
const configuredDir =
|
|
2354
|
-
if (
|
|
2487
|
+
const configuredDir = join4(projectRoot, config.outputDir);
|
|
2488
|
+
if (existsSync4(configuredDir)) {
|
|
2355
2489
|
files = scanDirectory(configuredDir, projectRoot);
|
|
2356
2490
|
if (files.length > 0) {
|
|
2357
2491
|
baseDir = configuredDir;
|
|
@@ -2364,8 +2498,8 @@ function discoverTranslationFiles(projectRoot = process.cwd()) {
|
|
|
2364
2498
|
}
|
|
2365
2499
|
if (files.length === 0) {
|
|
2366
2500
|
for (const commonPath of COMMON_PATHS) {
|
|
2367
|
-
const dir =
|
|
2368
|
-
if (
|
|
2501
|
+
const dir = join4(projectRoot, commonPath);
|
|
2502
|
+
if (existsSync4(dir)) {
|
|
2369
2503
|
const foundFiles = scanDirectory(dir, projectRoot);
|
|
2370
2504
|
if (foundFiles.length > 0) {
|
|
2371
2505
|
files = foundFiles;
|
|
@@ -2377,11 +2511,11 @@ function discoverTranslationFiles(projectRoot = process.cwd()) {
|
|
|
2377
2511
|
}
|
|
2378
2512
|
}
|
|
2379
2513
|
if (files.length === 0) {
|
|
2380
|
-
const srcDir =
|
|
2381
|
-
if (
|
|
2514
|
+
const srcDir = join4(projectRoot, "src");
|
|
2515
|
+
if (existsSync4(srcDir)) {
|
|
2382
2516
|
files = scanDirectory(srcDir, projectRoot, 0, 4);
|
|
2383
2517
|
if (files.length > 0) {
|
|
2384
|
-
const dirs = new Set(files.map((f) =>
|
|
2518
|
+
const dirs = new Set(files.map((f) => dirname4(f.path)));
|
|
2385
2519
|
baseDir = dirs.size === 1 ? [...dirs][0] : srcDir;
|
|
2386
2520
|
indicators.push("Found translation files in src/");
|
|
2387
2521
|
}
|
|
@@ -2473,7 +2607,7 @@ function readSourceTranslationsByNamespace(projectRoot = process.cwd(), sourceLa
|
|
|
2473
2607
|
const flattened = flattenTranslations(content);
|
|
2474
2608
|
const keyCount = Object.keys(flattened).length;
|
|
2475
2609
|
if (keyCount > 0) {
|
|
2476
|
-
const fileNameWithoutExt = basename(file.path,
|
|
2610
|
+
const fileNameWithoutExt = basename(file.path, extname2(file.path));
|
|
2477
2611
|
const namespace = file.namespace || (isLikelyLanguageCode(fileNameWithoutExt) ? "common" : fileNameWithoutExt);
|
|
2478
2612
|
namespaces.push({
|
|
2479
2613
|
namespace,
|
|
@@ -2520,7 +2654,7 @@ function readAllTranslationsByLanguage(projectRoot = process.cwd()) {
|
|
|
2520
2654
|
const flattened = flattenTranslations(content);
|
|
2521
2655
|
const keyCount = Object.keys(flattened).length;
|
|
2522
2656
|
if (keyCount > 0) {
|
|
2523
|
-
const fileNameWithoutExt = basename(file.path,
|
|
2657
|
+
const fileNameWithoutExt = basename(file.path, extname2(file.path));
|
|
2524
2658
|
const namespace = file.namespace || (isLikelyLanguageCode(fileNameWithoutExt) ? "common" : fileNameWithoutExt);
|
|
2525
2659
|
namespaces.push({
|
|
2526
2660
|
namespace,
|
|
@@ -2714,8 +2848,8 @@ To create this branch, go to the IntlPull dashboard or use: npx @intlpullhq/cli
|
|
|
2714
2848
|
status: "reading",
|
|
2715
2849
|
message: `Reading ${options.file}...`
|
|
2716
2850
|
}));
|
|
2717
|
-
const filePath =
|
|
2718
|
-
if (!
|
|
2851
|
+
const filePath = join5(cwd, options.file);
|
|
2852
|
+
if (!existsSync5(filePath)) {
|
|
2719
2853
|
setState((prev) => ({
|
|
2720
2854
|
...prev,
|
|
2721
2855
|
status: "error",
|
|
@@ -2726,7 +2860,7 @@ To create this branch, go to the IntlPull dashboard or use: npx @intlpullhq/cli
|
|
|
2726
2860
|
try {
|
|
2727
2861
|
const content = JSON.parse(readFileSync2(filePath, "utf-8"));
|
|
2728
2862
|
const keys = flattenTranslations(content);
|
|
2729
|
-
const namespace = basename2(options.file,
|
|
2863
|
+
const namespace = basename2(options.file, extname3(options.file));
|
|
2730
2864
|
namespaces = [{
|
|
2731
2865
|
namespace,
|
|
2732
2866
|
keys,
|
|
@@ -3777,9 +3911,9 @@ import { readFileSync as readFileSync3 } from "fs";
|
|
|
3777
3911
|
|
|
3778
3912
|
// src/commands/import/utils.ts
|
|
3779
3913
|
import { readdirSync as readdirSync2 } from "fs";
|
|
3780
|
-
import { join as
|
|
3914
|
+
import { join as join6, basename as basename3, extname as extname4 } from "path";
|
|
3781
3915
|
function detectLanguageFromFilename2(filename) {
|
|
3782
|
-
const name = basename3(filename,
|
|
3916
|
+
const name = basename3(filename, extname4(filename));
|
|
3783
3917
|
const patterns = [
|
|
3784
3918
|
/^([a-z]{2}(?:-[A-Z]{2})?)$/i,
|
|
3785
3919
|
// en, en-US
|
|
@@ -3799,7 +3933,7 @@ function detectLanguageFromFilename2(filename) {
|
|
|
3799
3933
|
return null;
|
|
3800
3934
|
}
|
|
3801
3935
|
function detectFormatFromExtension(filename) {
|
|
3802
|
-
const ext =
|
|
3936
|
+
const ext = extname4(filename).toLowerCase();
|
|
3803
3937
|
switch (ext) {
|
|
3804
3938
|
case ".yaml":
|
|
3805
3939
|
case ".yml":
|
|
@@ -3815,13 +3949,13 @@ function findTranslationFiles(dir, pattern) {
|
|
|
3815
3949
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
3816
3950
|
for (const entry of entries) {
|
|
3817
3951
|
if (entry.isFile()) {
|
|
3818
|
-
const ext =
|
|
3952
|
+
const ext = extname4(entry.name).toLowerCase();
|
|
3819
3953
|
if ([".json", ".yaml", ".yml"].includes(ext)) {
|
|
3820
3954
|
if (pattern) {
|
|
3821
3955
|
const regex = new RegExp(pattern.replace(/\*/g, ".*").replace(/\?/g, "."));
|
|
3822
3956
|
if (!regex.test(entry.name)) continue;
|
|
3823
3957
|
}
|
|
3824
|
-
const filePath =
|
|
3958
|
+
const filePath = join6(dir, entry.name);
|
|
3825
3959
|
files.push({
|
|
3826
3960
|
path: filePath,
|
|
3827
3961
|
name: entry.name,
|
|
@@ -3830,7 +3964,7 @@ function findTranslationFiles(dir, pattern) {
|
|
|
3830
3964
|
});
|
|
3831
3965
|
}
|
|
3832
3966
|
} else if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
3833
|
-
const subFiles = findTranslationFiles(
|
|
3967
|
+
const subFiles = findTranslationFiles(join6(dir, entry.name), pattern);
|
|
3834
3968
|
files.push(...subFiles);
|
|
3835
3969
|
}
|
|
3836
3970
|
}
|
|
@@ -6625,8 +6759,8 @@ import { render as render9, Box as Box22, Text as Text23, useApp as useApp5, use
|
|
|
6625
6759
|
import TextInput2 from "ink-text-input";
|
|
6626
6760
|
import SelectInput6 from "ink-select-input";
|
|
6627
6761
|
import Spinner11 from "ink-spinner";
|
|
6628
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
6629
|
-
import { join as
|
|
6762
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6, statSync as statSync3, readdirSync as readdirSync3 } from "fs";
|
|
6763
|
+
import { join as join7, basename as basename5, extname as extname5, relative as relative2 } from "path";
|
|
6630
6764
|
import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
6631
6765
|
function countKeys2(content) {
|
|
6632
6766
|
try {
|
|
@@ -6659,13 +6793,13 @@ function scanTranslationFiles(dir, pattern) {
|
|
|
6659
6793
|
return;
|
|
6660
6794
|
}
|
|
6661
6795
|
for (const entry of entries) {
|
|
6662
|
-
const fullPath =
|
|
6796
|
+
const fullPath = join7(currentDir, entry.name);
|
|
6663
6797
|
if (entry.isDirectory()) {
|
|
6664
6798
|
if (!["node_modules", ".git", "dist", "build", ".next", ".cache", "coverage"].includes(entry.name)) {
|
|
6665
6799
|
scan(fullPath);
|
|
6666
6800
|
}
|
|
6667
6801
|
} else if (entry.isFile()) {
|
|
6668
|
-
const ext =
|
|
6802
|
+
const ext = extname5(entry.name).toLowerCase();
|
|
6669
6803
|
if (extensions.includes(ext)) {
|
|
6670
6804
|
if (pattern && !entry.name.match(new RegExp(pattern.replace(/\*/g, ".*")))) {
|
|
6671
6805
|
continue;
|
|
@@ -6694,7 +6828,7 @@ function scanTranslationFiles(dir, pattern) {
|
|
|
6694
6828
|
}
|
|
6695
6829
|
}
|
|
6696
6830
|
}
|
|
6697
|
-
if (
|
|
6831
|
+
if (existsSync6(dir) && statSync3(dir).isDirectory()) {
|
|
6698
6832
|
scan(dir);
|
|
6699
6833
|
}
|
|
6700
6834
|
return files.sort((a, b) => a.language.localeCompare(b.language));
|
|
@@ -6799,8 +6933,8 @@ Make sure your files follow naming conventions like:
|
|
|
6799
6933
|
}
|
|
6800
6934
|
if (!options.project) {
|
|
6801
6935
|
try {
|
|
6802
|
-
const pkgPath =
|
|
6803
|
-
if (
|
|
6936
|
+
const pkgPath = join7(process.cwd(), "package.json");
|
|
6937
|
+
if (existsSync6(pkgPath)) {
|
|
6804
6938
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
6805
6939
|
if (pkg.name) {
|
|
6806
6940
|
setProjectName(pkg.name.replace(/^@[^/]+\//, ""));
|
|
@@ -7149,403 +7283,36 @@ function runMigrateFiles(path4, options) {
|
|
|
7149
7283
|
process.exit(1);
|
|
7150
7284
|
}
|
|
7151
7285
|
const targetPath = path4 || process.cwd();
|
|
7152
|
-
if (!
|
|
7286
|
+
if (!existsSync6(targetPath)) {
|
|
7153
7287
|
console.error(`Path not found: ${targetPath}`);
|
|
7154
7288
|
process.exit(1);
|
|
7155
7289
|
}
|
|
7156
7290
|
render9(/* @__PURE__ */ jsx25(MigrationUI, { options, path: targetPath }));
|
|
7157
7291
|
}
|
|
7158
7292
|
|
|
7159
|
-
// src/commands/compare.tsx
|
|
7160
|
-
import { useState as useState14, useEffect as useEffect11 } from "react";
|
|
7161
|
-
import { render as render10, Box as Box23, Text as Text24 } from "ink";
|
|
7162
|
-
import Spinner12 from "ink-spinner";
|
|
7163
|
-
import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
7164
|
-
var COMPETITOR_PRICING = {
|
|
7165
|
-
lokalise: {
|
|
7166
|
-
name: "Lokalise",
|
|
7167
|
-
tiers: [
|
|
7168
|
-
{ name: "Start", price: 120, keys: 2e3, users: 5, features: ["Basic features"] },
|
|
7169
|
-
{ name: "Essential", price: 450, keys: 1e4, users: 10, features: ["TM", "Glossary"] },
|
|
7170
|
-
{ name: "Pro", price: 990, keys: 3e4, users: 25, features: ["All features"] },
|
|
7171
|
-
{ name: "Enterprise", price: 2500, keys: 1e5, users: 50, features: ["SSO", "SLA"] }
|
|
7172
|
-
],
|
|
7173
|
-
perKeyPricing: { price: 0.045, unit: 1 },
|
|
7174
|
-
notes: [
|
|
7175
|
-
"Prices are per month, billed annually",
|
|
7176
|
-
"Overage charges apply for additional keys",
|
|
7177
|
-
"Enterprise pricing varies"
|
|
7178
|
-
]
|
|
7179
|
-
},
|
|
7180
|
-
crowdin: {
|
|
7181
|
-
name: "Crowdin",
|
|
7182
|
-
tiers: [
|
|
7183
|
-
{ name: "Free", price: 0, keys: 500, users: 1, features: ["1 public project"] },
|
|
7184
|
-
{ name: "Pro", price: 50, keys: 5e3, users: 3, features: ["Private projects"] },
|
|
7185
|
-
{ name: "Team", price: 250, keys: 25e3, users: 10, features: ["TM", "Glossary"] },
|
|
7186
|
-
{ name: "Enterprise", price: 600, keys: 1e5, users: 30, features: ["SSO", "API"] }
|
|
7187
|
-
],
|
|
7188
|
-
perKeyPricing: { price: 0.02, unit: 1 },
|
|
7189
|
-
notes: [
|
|
7190
|
-
"Prices are per month",
|
|
7191
|
-
"Free tier for open source",
|
|
7192
|
-
"Enterprise has custom pricing"
|
|
7193
|
-
]
|
|
7194
|
-
},
|
|
7195
|
-
phrase: {
|
|
7196
|
-
name: "Phrase",
|
|
7197
|
-
tiers: [
|
|
7198
|
-
{ name: "Starter", price: 75, keys: 1e3, users: 3, features: ["Basic"] },
|
|
7199
|
-
{ name: "Pro", price: 250, keys: 5e3, users: 10, features: ["TM", "API"] },
|
|
7200
|
-
{ name: "Business", price: 600, keys: 2e4, users: 25, features: ["Glossary"] },
|
|
7201
|
-
{ name: "Enterprise", price: 1500, keys: 1e5, users: 100, features: ["SSO", "SLA"] }
|
|
7202
|
-
],
|
|
7203
|
-
perKeyPricing: { price: 0.05, unit: 1 },
|
|
7204
|
-
notes: [
|
|
7205
|
-
"Prices are per month, billed annually",
|
|
7206
|
-
"Phrase Strings pricing",
|
|
7207
|
-
"Separate products for TMS"
|
|
7208
|
-
]
|
|
7209
|
-
}
|
|
7210
|
-
};
|
|
7211
|
-
var INTLPULL_PRICING = {
|
|
7212
|
-
name: "IntlPull",
|
|
7213
|
-
tiers: [
|
|
7214
|
-
{ name: "Free", price: 0, keys: 1e3, users: 2, features: ["In-context editing", "Chrome extension", "CLI", "OTA updates"] },
|
|
7215
|
-
{ name: "Pro", price: 19, keys: 1e4, users: 5, features: ["All Free features", "TM", "Glossary", "Branching"] },
|
|
7216
|
-
{ name: "Team", price: 49, keys: 5e4, users: 15, features: ["All Pro features", "Priority support", "Team roles"] },
|
|
7217
|
-
{ name: "Business", price: 99, keys: 2e5, users: 50, features: ["All Team features", "SSO", "SLA", "Dedicated support"] }
|
|
7218
|
-
],
|
|
7219
|
-
notes: [
|
|
7220
|
-
"No per-key overage charges",
|
|
7221
|
-
"All plans include OTA updates",
|
|
7222
|
-
"Developer-first tools included"
|
|
7223
|
-
]
|
|
7224
|
-
};
|
|
7225
|
-
function calculateCompetitorCost(provider, keys, users) {
|
|
7226
|
-
const pricing = COMPETITOR_PRICING[provider];
|
|
7227
|
-
if (!pricing) {
|
|
7228
|
-
return { tier: "Unknown", monthly: 0, annual: 0 };
|
|
7229
|
-
}
|
|
7230
|
-
const suitableTier = pricing.tiers.find((t) => t.keys >= keys && t.users >= users) || pricing.tiers[pricing.tiers.length - 1];
|
|
7231
|
-
return {
|
|
7232
|
-
tier: suitableTier.name,
|
|
7233
|
-
monthly: suitableTier.price,
|
|
7234
|
-
annual: suitableTier.price * 12
|
|
7235
|
-
};
|
|
7236
|
-
}
|
|
7237
|
-
function calculateIntlPullCost(keys, users) {
|
|
7238
|
-
const suitableTier = INTLPULL_PRICING.tiers.find((t) => t.keys >= keys && t.users >= users) || INTLPULL_PRICING.tiers[INTLPULL_PRICING.tiers.length - 1];
|
|
7239
|
-
return {
|
|
7240
|
-
tier: suitableTier.name,
|
|
7241
|
-
monthly: suitableTier.price,
|
|
7242
|
-
annual: suitableTier.price * 12
|
|
7243
|
-
};
|
|
7244
|
-
}
|
|
7245
|
-
function formatCurrency(amount) {
|
|
7246
|
-
return new Intl.NumberFormat("en-US", {
|
|
7247
|
-
style: "currency",
|
|
7248
|
-
currency: "USD",
|
|
7249
|
-
minimumFractionDigits: 0,
|
|
7250
|
-
maximumFractionDigits: 0
|
|
7251
|
-
}).format(amount);
|
|
7252
|
-
}
|
|
7253
|
-
function formatPercent(value) {
|
|
7254
|
-
return `${Math.round(value)}%`;
|
|
7255
|
-
}
|
|
7256
|
-
function CompareDisplay({ options }) {
|
|
7257
|
-
const [loading, setLoading] = useState14(true);
|
|
7258
|
-
const [result, setResult] = useState14(null);
|
|
7259
|
-
const [projectStats, setProjectStats] = useState14(null);
|
|
7260
|
-
useEffect11(() => {
|
|
7261
|
-
async function loadData() {
|
|
7262
|
-
const config = getProjectConfig();
|
|
7263
|
-
const resolved = getResolvedApiKey();
|
|
7264
|
-
const globalConfig = getGlobalConfig();
|
|
7265
|
-
let keys = options.keys || 1e3;
|
|
7266
|
-
let languages = options.languages || 2;
|
|
7267
|
-
let users = options.users || 5;
|
|
7268
|
-
if (resolved?.key && config?.projectId) {
|
|
7269
|
-
try {
|
|
7270
|
-
const apiUrl = globalConfig.apiUrl || "https://api.intlpull.com";
|
|
7271
|
-
const response = await fetch(`${apiUrl}/api/v1/projects/${config.projectId}`, {
|
|
7272
|
-
headers: { "X-API-Key": resolved.key }
|
|
7273
|
-
});
|
|
7274
|
-
if (response.ok) {
|
|
7275
|
-
const data = await response.json();
|
|
7276
|
-
keys = data.stats?.total_keys || keys;
|
|
7277
|
-
languages = data.languages?.length || languages;
|
|
7278
|
-
setProjectStats({ keys, languages });
|
|
7279
|
-
}
|
|
7280
|
-
} catch {
|
|
7281
|
-
}
|
|
7282
|
-
}
|
|
7283
|
-
const provider = options.from || "lokalise";
|
|
7284
|
-
const competitorCost = calculateCompetitorCost(provider, keys, users);
|
|
7285
|
-
const intlpullCost = calculateIntlPullCost(keys, users);
|
|
7286
|
-
const monthlySavings = competitorCost.monthly - intlpullCost.monthly;
|
|
7287
|
-
const annualSavings = competitorCost.annual - intlpullCost.annual;
|
|
7288
|
-
const percentSavings = competitorCost.monthly > 0 ? monthlySavings / competitorCost.monthly * 100 : 0;
|
|
7289
|
-
setResult({
|
|
7290
|
-
competitor: {
|
|
7291
|
-
name: COMPETITOR_PRICING[provider]?.name || provider,
|
|
7292
|
-
tier: competitorCost.tier,
|
|
7293
|
-
monthly: competitorCost.monthly,
|
|
7294
|
-
annual: competitorCost.annual
|
|
7295
|
-
},
|
|
7296
|
-
intlpull: intlpullCost,
|
|
7297
|
-
savings: {
|
|
7298
|
-
monthly: monthlySavings,
|
|
7299
|
-
annual: annualSavings,
|
|
7300
|
-
percent: percentSavings
|
|
7301
|
-
},
|
|
7302
|
-
inputs: { keys, languages, users }
|
|
7303
|
-
});
|
|
7304
|
-
setLoading(false);
|
|
7305
|
-
}
|
|
7306
|
-
loadData();
|
|
7307
|
-
}, [options]);
|
|
7308
|
-
if (loading) {
|
|
7309
|
-
return /* @__PURE__ */ jsxs24(Box23, { children: [
|
|
7310
|
-
/* @__PURE__ */ jsx26(Text24, { color: "cyan", children: /* @__PURE__ */ jsx26(Spinner12, { type: "dots" }) }),
|
|
7311
|
-
/* @__PURE__ */ jsx26(Text24, { children: " Calculating cost comparison..." })
|
|
7312
|
-
] });
|
|
7313
|
-
}
|
|
7314
|
-
if (!result) {
|
|
7315
|
-
return /* @__PURE__ */ jsx26(Box23, { children: /* @__PURE__ */ jsx26(Text24, { color: "red", children: "Failed to calculate comparison" }) });
|
|
7316
|
-
}
|
|
7317
|
-
if (options.json) {
|
|
7318
|
-
return /* @__PURE__ */ jsx26(Text24, { children: JSON.stringify(result, null, 2) });
|
|
7319
|
-
}
|
|
7320
|
-
return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", paddingX: 1, children: [
|
|
7321
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7322
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, color: "cyan", children: "IntlPull" }),
|
|
7323
|
-
/* @__PURE__ */ jsx26(Text24, { children: " \u2022 Cost Comparison" })
|
|
7324
|
-
] }),
|
|
7325
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, flexDirection: "column", children: [
|
|
7326
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, children: "Your Usage:" }),
|
|
7327
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7328
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Keys: " }),
|
|
7329
|
-
/* @__PURE__ */ jsx26(Text24, { children: result.inputs.keys.toLocaleString() }),
|
|
7330
|
-
projectStats && /* @__PURE__ */ jsx26(Text24, { color: "green", children: " (from project)" })
|
|
7331
|
-
] }),
|
|
7332
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7333
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Languages: " }),
|
|
7334
|
-
/* @__PURE__ */ jsx26(Text24, { children: result.inputs.languages })
|
|
7335
|
-
] }),
|
|
7336
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7337
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Users: " }),
|
|
7338
|
-
/* @__PURE__ */ jsx26(Text24, { children: result.inputs.users })
|
|
7339
|
-
] })
|
|
7340
|
-
] }),
|
|
7341
|
-
/* @__PURE__ */ jsx26(Box23, { marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsxs24(Box23, { borderStyle: "single", flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
7342
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7343
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, color: "red", children: result.competitor.name }),
|
|
7344
|
-
/* @__PURE__ */ jsxs24(Text24, { children: [
|
|
7345
|
-
" (",
|
|
7346
|
-
result.competitor.tier,
|
|
7347
|
-
" plan)"
|
|
7348
|
-
] })
|
|
7349
|
-
] }),
|
|
7350
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7351
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Monthly: " }),
|
|
7352
|
-
/* @__PURE__ */ jsx26(Text24, { children: formatCurrency(result.competitor.monthly) })
|
|
7353
|
-
] }),
|
|
7354
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7355
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Annual: " }),
|
|
7356
|
-
/* @__PURE__ */ jsx26(Text24, { children: formatCurrency(result.competitor.annual) })
|
|
7357
|
-
] }),
|
|
7358
|
-
/* @__PURE__ */ jsx26(Box23, { marginY: 1, children: /* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }),
|
|
7359
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7360
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, color: "green", children: "IntlPull" }),
|
|
7361
|
-
/* @__PURE__ */ jsxs24(Text24, { children: [
|
|
7362
|
-
" (",
|
|
7363
|
-
result.intlpull.tier,
|
|
7364
|
-
" plan)"
|
|
7365
|
-
] })
|
|
7366
|
-
] }),
|
|
7367
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7368
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Monthly: " }),
|
|
7369
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: formatCurrency(result.intlpull.monthly) })
|
|
7370
|
-
] }),
|
|
7371
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7372
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Annual: " }),
|
|
7373
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: formatCurrency(result.intlpull.annual) })
|
|
7374
|
-
] })
|
|
7375
|
-
] }) }),
|
|
7376
|
-
result.savings.monthly > 0 && /* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, flexDirection: "column", children: [
|
|
7377
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, color: "green", children: "\u{1F4B0} Your Savings with IntlPull:" }),
|
|
7378
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7379
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Monthly: " }),
|
|
7380
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", bold: true, children: formatCurrency(result.savings.monthly) }),
|
|
7381
|
-
/* @__PURE__ */ jsxs24(Text24, { dimColor: true, children: [
|
|
7382
|
-
" (",
|
|
7383
|
-
formatPercent(result.savings.percent),
|
|
7384
|
-
" less)"
|
|
7385
|
-
] })
|
|
7386
|
-
] }),
|
|
7387
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, children: [
|
|
7388
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Annual: " }),
|
|
7389
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", bold: true, children: formatCurrency(result.savings.annual) })
|
|
7390
|
-
] })
|
|
7391
|
-
] }),
|
|
7392
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, flexDirection: "column", children: [
|
|
7393
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, children: "What you get with IntlPull:" }),
|
|
7394
|
-
/* @__PURE__ */ jsxs24(Box23, { paddingLeft: 2, flexDirection: "column", children: [
|
|
7395
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: "\u2713 In-context editing (Chrome extension)" }),
|
|
7396
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: "\u2713 CLI with smart auto-detection" }),
|
|
7397
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: "\u2713 OTA updates for mobile apps" }),
|
|
7398
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: "\u2713 Git branch integration" }),
|
|
7399
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: "\u2713 AI-powered translation" }),
|
|
7400
|
-
/* @__PURE__ */ jsx26(Text24, { color: "green", children: "\u2713 No per-key overage charges" })
|
|
7401
|
-
] })
|
|
7402
|
-
] }),
|
|
7403
|
-
/* @__PURE__ */ jsxs24(Box23, { marginTop: 1, children: [
|
|
7404
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Ready to switch? Run: " }),
|
|
7405
|
-
/* @__PURE__ */ jsxs24(Text24, { color: "cyan", children: [
|
|
7406
|
-
"npx @intlpullhq/cli migrate from ",
|
|
7407
|
-
options.from || "lokalise"
|
|
7408
|
-
] })
|
|
7409
|
-
] })
|
|
7410
|
-
] });
|
|
7411
|
-
}
|
|
7412
|
-
function CompareAllProviders({ options }) {
|
|
7413
|
-
const [loading, setLoading] = useState14(true);
|
|
7414
|
-
const [results, setResults] = useState14([]);
|
|
7415
|
-
useEffect11(() => {
|
|
7416
|
-
const keys = options.keys || 5e3;
|
|
7417
|
-
const users = options.users || 5;
|
|
7418
|
-
const languages = options.languages || 3;
|
|
7419
|
-
const providers = ["lokalise", "crowdin", "phrase"];
|
|
7420
|
-
const comparisons = [];
|
|
7421
|
-
for (const provider of providers) {
|
|
7422
|
-
const competitorCost = calculateCompetitorCost(provider, keys, users);
|
|
7423
|
-
const intlpullCost2 = calculateIntlPullCost(keys, users);
|
|
7424
|
-
const monthlySavings = competitorCost.monthly - intlpullCost2.monthly;
|
|
7425
|
-
const annualSavings = competitorCost.annual - intlpullCost2.annual;
|
|
7426
|
-
const percentSavings = competitorCost.monthly > 0 ? monthlySavings / competitorCost.monthly * 100 : 0;
|
|
7427
|
-
comparisons.push({
|
|
7428
|
-
competitor: {
|
|
7429
|
-
name: COMPETITOR_PRICING[provider]?.name || provider,
|
|
7430
|
-
tier: competitorCost.tier,
|
|
7431
|
-
monthly: competitorCost.monthly,
|
|
7432
|
-
annual: competitorCost.annual
|
|
7433
|
-
},
|
|
7434
|
-
intlpull: intlpullCost2,
|
|
7435
|
-
savings: {
|
|
7436
|
-
monthly: monthlySavings,
|
|
7437
|
-
annual: annualSavings,
|
|
7438
|
-
percent: percentSavings
|
|
7439
|
-
},
|
|
7440
|
-
inputs: { keys, languages, users }
|
|
7441
|
-
});
|
|
7442
|
-
}
|
|
7443
|
-
setResults(comparisons);
|
|
7444
|
-
setLoading(false);
|
|
7445
|
-
}, [options]);
|
|
7446
|
-
if (loading) {
|
|
7447
|
-
return /* @__PURE__ */ jsxs24(Box23, { children: [
|
|
7448
|
-
/* @__PURE__ */ jsx26(Text24, { color: "cyan", children: /* @__PURE__ */ jsx26(Spinner12, { type: "dots" }) }),
|
|
7449
|
-
/* @__PURE__ */ jsx26(Text24, { children: " Calculating cost comparison..." })
|
|
7450
|
-
] });
|
|
7451
|
-
}
|
|
7452
|
-
if (options.json) {
|
|
7453
|
-
return /* @__PURE__ */ jsx26(Text24, { children: JSON.stringify(results, null, 2) });
|
|
7454
|
-
}
|
|
7455
|
-
const intlpullCost = results[0]?.intlpull;
|
|
7456
|
-
return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", paddingX: 1, children: [
|
|
7457
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7458
|
-
/* @__PURE__ */ jsx26(Text24, { bold: true, color: "cyan", children: "IntlPull" }),
|
|
7459
|
-
/* @__PURE__ */ jsx26(Text24, { children: " \u2022 Cost Comparison" })
|
|
7460
|
-
] }),
|
|
7461
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7462
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Comparing for: " }),
|
|
7463
|
-
/* @__PURE__ */ jsxs24(Text24, { children: [
|
|
7464
|
-
results[0]?.inputs.keys.toLocaleString(),
|
|
7465
|
-
" keys, "
|
|
7466
|
-
] }),
|
|
7467
|
-
/* @__PURE__ */ jsxs24(Text24, { children: [
|
|
7468
|
-
results[0]?.inputs.users,
|
|
7469
|
-
" users, "
|
|
7470
|
-
] }),
|
|
7471
|
-
/* @__PURE__ */ jsxs24(Text24, { children: [
|
|
7472
|
-
results[0]?.inputs.languages,
|
|
7473
|
-
" languages"
|
|
7474
|
-
] })
|
|
7475
|
-
] }),
|
|
7476
|
-
/* @__PURE__ */ jsx26(Box23, { marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsxs24(Box23, { borderStyle: "single", flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
7477
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7478
|
-
/* @__PURE__ */ jsx26(Box23, { width: 15, children: /* @__PURE__ */ jsx26(Text24, { bold: true, children: "Provider" }) }),
|
|
7479
|
-
/* @__PURE__ */ jsx26(Box23, { width: 10, children: /* @__PURE__ */ jsx26(Text24, { bold: true, children: "Plan" }) }),
|
|
7480
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { bold: true, children: "Monthly" }) }),
|
|
7481
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { bold: true, children: "Annual" }) }),
|
|
7482
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { bold: true, children: "Savings" }) })
|
|
7483
|
-
] }),
|
|
7484
|
-
results.map((r, i) => /* @__PURE__ */ jsxs24(Box23, { children: [
|
|
7485
|
-
/* @__PURE__ */ jsx26(Box23, { width: 15, children: /* @__PURE__ */ jsx26(Text24, { color: "red", children: r.competitor.name }) }),
|
|
7486
|
-
/* @__PURE__ */ jsx26(Box23, { width: 10, children: /* @__PURE__ */ jsx26(Text24, { children: r.competitor.tier }) }),
|
|
7487
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { children: formatCurrency(r.competitor.monthly) }) }),
|
|
7488
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { children: formatCurrency(r.competitor.annual) }) }),
|
|
7489
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { color: "green", children: r.savings.monthly > 0 ? formatPercent(r.savings.percent) : "-" }) })
|
|
7490
|
-
] }, i)),
|
|
7491
|
-
/* @__PURE__ */ jsx26(Box23, { marginY: 1, children: /* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }),
|
|
7492
|
-
/* @__PURE__ */ jsxs24(Box23, { children: [
|
|
7493
|
-
/* @__PURE__ */ jsx26(Box23, { width: 15, children: /* @__PURE__ */ jsx26(Text24, { color: "green", bold: true, children: "IntlPull" }) }),
|
|
7494
|
-
/* @__PURE__ */ jsx26(Box23, { width: 10, children: /* @__PURE__ */ jsx26(Text24, { children: intlpullCost?.tier }) }),
|
|
7495
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { color: "green", bold: true, children: formatCurrency(intlpullCost?.monthly || 0) }) }),
|
|
7496
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { color: "green", bold: true, children: formatCurrency(intlpullCost?.annual || 0) }) }),
|
|
7497
|
-
/* @__PURE__ */ jsx26(Box23, { width: 12, children: /* @__PURE__ */ jsx26(Text24, { color: "green", bold: true, children: "Best" }) })
|
|
7498
|
-
] })
|
|
7499
|
-
] }) }),
|
|
7500
|
-
/* @__PURE__ */ jsxs24(Box23, { marginBottom: 1, children: [
|
|
7501
|
-
/* @__PURE__ */ jsx26(Text24, { children: "\u{1F4B0} Save up to " }),
|
|
7502
|
-
/* @__PURE__ */ jsxs24(Text24, { color: "green", bold: true, children: [
|
|
7503
|
-
formatCurrency(Math.max(...results.map((r) => r.savings.annual))),
|
|
7504
|
-
"/year"
|
|
7505
|
-
] }),
|
|
7506
|
-
/* @__PURE__ */ jsx26(Text24, { children: " by switching to IntlPull" })
|
|
7507
|
-
] }),
|
|
7508
|
-
/* @__PURE__ */ jsxs24(Box23, { marginTop: 1, children: [
|
|
7509
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Ready to switch? Run: " }),
|
|
7510
|
-
/* @__PURE__ */ jsx26(Text24, { color: "cyan", children: "npx @intlpullhq/cli migrate from <provider>" })
|
|
7511
|
-
] }),
|
|
7512
|
-
/* @__PURE__ */ jsxs24(Box23, { marginTop: 1, children: [
|
|
7513
|
-
/* @__PURE__ */ jsx26(Text24, { dimColor: true, children: "Compare with your usage: " }),
|
|
7514
|
-
/* @__PURE__ */ jsx26(Text24, { color: "cyan", children: "npx @intlpullhq/cli compare --keys 10000 --users 10" })
|
|
7515
|
-
] })
|
|
7516
|
-
] });
|
|
7517
|
-
}
|
|
7518
|
-
function runCompare(options) {
|
|
7519
|
-
if (options.from) {
|
|
7520
|
-
render10(/* @__PURE__ */ jsx26(CompareDisplay, { options }));
|
|
7521
|
-
} else {
|
|
7522
|
-
render10(/* @__PURE__ */ jsx26(CompareAllProviders, { options }));
|
|
7523
|
-
}
|
|
7524
|
-
}
|
|
7525
|
-
|
|
7526
7293
|
// src/commands/check.tsx
|
|
7527
|
-
import { useState as
|
|
7528
|
-
import { render as
|
|
7294
|
+
import { useState as useState14, useEffect as useEffect11 } from "react";
|
|
7295
|
+
import { render as render10, Box as Box24, Text as Text25 } from "ink";
|
|
7529
7296
|
import { glob } from "glob";
|
|
7530
|
-
import { readFileSync as readFileSync6 } from "fs";
|
|
7297
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
7531
7298
|
|
|
7532
7299
|
// src/components/TaskList.tsx
|
|
7533
|
-
import { Box as
|
|
7534
|
-
import { jsx as
|
|
7300
|
+
import { Box as Box23, Text as Text24 } from "ink";
|
|
7301
|
+
import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
7535
7302
|
function getStatusIcon(status) {
|
|
7536
7303
|
switch (status) {
|
|
7537
7304
|
case "pending":
|
|
7538
|
-
return /* @__PURE__ */
|
|
7305
|
+
return /* @__PURE__ */ jsx26(Text24, { color: colors.textDim, children: icons.pending });
|
|
7539
7306
|
case "running":
|
|
7540
|
-
return /* @__PURE__ */
|
|
7307
|
+
return /* @__PURE__ */ jsx26(Spinner2, { type: "dots", color: colors.primary });
|
|
7541
7308
|
case "success":
|
|
7542
|
-
return /* @__PURE__ */
|
|
7309
|
+
return /* @__PURE__ */ jsx26(Text24, { color: colors.success, children: icons.success });
|
|
7543
7310
|
case "error":
|
|
7544
|
-
return /* @__PURE__ */
|
|
7311
|
+
return /* @__PURE__ */ jsx26(Text24, { color: colors.error, children: icons.error });
|
|
7545
7312
|
case "warning":
|
|
7546
|
-
return /* @__PURE__ */
|
|
7313
|
+
return /* @__PURE__ */ jsx26(Text24, { color: colors.warning, children: icons.warning });
|
|
7547
7314
|
case "skipped":
|
|
7548
|
-
return /* @__PURE__ */
|
|
7315
|
+
return /* @__PURE__ */ jsx26(Text24, { color: colors.textDim, children: icons.skipped });
|
|
7549
7316
|
}
|
|
7550
7317
|
}
|
|
7551
7318
|
function getStatusColor(status) {
|
|
@@ -7565,36 +7332,45 @@ function getStatusColor(status) {
|
|
|
7565
7332
|
}
|
|
7566
7333
|
}
|
|
7567
7334
|
function TaskList({ tasks, title }) {
|
|
7568
|
-
return /* @__PURE__ */
|
|
7569
|
-
title && /* @__PURE__ */
|
|
7570
|
-
tasks.map((task) => /* @__PURE__ */
|
|
7571
|
-
/* @__PURE__ */
|
|
7572
|
-
/* @__PURE__ */
|
|
7573
|
-
/* @__PURE__ */
|
|
7574
|
-
task.output && task.status !== "pending" && /* @__PURE__ */
|
|
7335
|
+
return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", marginY: 1, children: [
|
|
7336
|
+
title && /* @__PURE__ */ jsx26(Box23, { marginBottom: 1, children: /* @__PURE__ */ jsx26(Text24, { bold: true, color: colors.text, children: title }) }),
|
|
7337
|
+
tasks.map((task) => /* @__PURE__ */ jsxs24(Box23, { marginLeft: 1, children: [
|
|
7338
|
+
/* @__PURE__ */ jsx26(Box23, { width: 3, children: getStatusIcon(task.status) }),
|
|
7339
|
+
/* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
|
|
7340
|
+
/* @__PURE__ */ jsx26(Text24, { color: getStatusColor(task.status), children: task.label }),
|
|
7341
|
+
task.output && task.status !== "pending" && /* @__PURE__ */ jsx26(Text24, { color: colors.textDim, dimColor: true, children: task.output })
|
|
7575
7342
|
] })
|
|
7576
7343
|
] }, task.id))
|
|
7577
7344
|
] });
|
|
7578
7345
|
}
|
|
7579
7346
|
|
|
7580
7347
|
// src/commands/check.tsx
|
|
7581
|
-
import { jsx as
|
|
7348
|
+
import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
7349
|
+
var LANG_CODE_REGEX = /^[a-z]{2}([-_][a-zA-Z]{2})?$/i;
|
|
7582
7350
|
function CheckCommand({ options }) {
|
|
7583
|
-
const
|
|
7351
|
+
const baseTasks = [
|
|
7584
7352
|
{ id: "config", label: "Loading configuration", status: "pending" },
|
|
7585
7353
|
{ id: "scan", label: "Scanning translation files", status: "pending" },
|
|
7586
7354
|
{ id: "compare", label: "Comparing translations", status: "pending" },
|
|
7587
7355
|
{ id: "report", label: "Generating report", status: "pending" }
|
|
7588
|
-
]
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7356
|
+
];
|
|
7357
|
+
if (options.fix) {
|
|
7358
|
+
baseTasks.push({ id: "fix", label: "Fixing missing translations", status: "pending" });
|
|
7359
|
+
}
|
|
7360
|
+
if (options.output) {
|
|
7361
|
+
baseTasks.push({ id: "output", label: "Writing report file", status: "pending" });
|
|
7362
|
+
}
|
|
7363
|
+
const [tasks, setTasks] = useState14(baseTasks);
|
|
7364
|
+
const [result, setResult] = useState14(null);
|
|
7365
|
+
const [fixResult, setFixResult] = useState14(null);
|
|
7366
|
+
const [error, setError] = useState14(null);
|
|
7367
|
+
const [done, setDone] = useState14(false);
|
|
7592
7368
|
const updateTask = (id, update) => {
|
|
7593
7369
|
setTasks(
|
|
7594
7370
|
(prev) => prev.map((t) => t.id === id ? { ...t, ...update } : t)
|
|
7595
7371
|
);
|
|
7596
7372
|
};
|
|
7597
|
-
|
|
7373
|
+
useEffect11(() => {
|
|
7598
7374
|
async function run() {
|
|
7599
7375
|
try {
|
|
7600
7376
|
updateTask("config", { status: "running" });
|
|
@@ -7636,10 +7412,10 @@ function CheckCommand({ options }) {
|
|
|
7636
7412
|
const parts = file.split("/");
|
|
7637
7413
|
const fileName = parts[parts.length - 1];
|
|
7638
7414
|
const dirName = parts[parts.length - 2];
|
|
7639
|
-
if (
|
|
7415
|
+
if (LANG_CODE_REGEX.test(dirName)) {
|
|
7640
7416
|
if (!filesByLang[dirName]) filesByLang[dirName] = [];
|
|
7641
7417
|
filesByLang[dirName].push(file);
|
|
7642
|
-
} else if (
|
|
7418
|
+
} else if (LANG_CODE_REGEX.test(fileName.replace(".json", ""))) {
|
|
7643
7419
|
const lang = fileName.replace(".json", "");
|
|
7644
7420
|
if (!filesByLang[lang]) filesByLang[lang] = [];
|
|
7645
7421
|
filesByLang[lang].push(file);
|
|
@@ -7727,14 +7503,77 @@ ${parseErrors.join("\n")}`);
|
|
|
7727
7503
|
status: missingKeys.length > 0 ? "warning" : "success",
|
|
7728
7504
|
output: missingKeys.length > 0 ? `Found ${missingKeys.length} missing key(s)` : "All translations complete!"
|
|
7729
7505
|
});
|
|
7730
|
-
|
|
7506
|
+
const checkResult = {
|
|
7731
7507
|
sourceLanguage,
|
|
7732
7508
|
targetLanguages,
|
|
7733
7509
|
totalKeys,
|
|
7734
7510
|
missingKeys: missingKeys.slice(0, 10),
|
|
7735
7511
|
// Show first 10
|
|
7736
7512
|
coveragePercentage
|
|
7737
|
-
}
|
|
7513
|
+
};
|
|
7514
|
+
setResult(checkResult);
|
|
7515
|
+
if (options.fix && missingKeys.length > 0) {
|
|
7516
|
+
updateTask("fix", { status: "running" });
|
|
7517
|
+
let filesModified = 0;
|
|
7518
|
+
let keysAdded = 0;
|
|
7519
|
+
for (const targetLang of targetLanguages) {
|
|
7520
|
+
if (!filesByLang[targetLang]) continue;
|
|
7521
|
+
for (const file of filesByLang[targetLang]) {
|
|
7522
|
+
let content = {};
|
|
7523
|
+
try {
|
|
7524
|
+
content = JSON.parse(readFileSync6(file, "utf-8"));
|
|
7525
|
+
} catch {
|
|
7526
|
+
content = {};
|
|
7527
|
+
}
|
|
7528
|
+
const flatTarget = flattenObjectToRecord(content);
|
|
7529
|
+
let modified = false;
|
|
7530
|
+
for (const mk of missingKeys) {
|
|
7531
|
+
if (mk.missingIn.includes(targetLang) && !(mk.key in flatTarget)) {
|
|
7532
|
+
setNestedValue(content, mk.key, `[${targetLang.toUpperCase()}] ${mk.sourceValue}`);
|
|
7533
|
+
keysAdded++;
|
|
7534
|
+
modified = true;
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
if (modified) {
|
|
7538
|
+
writeFileSync4(file, JSON.stringify(content, null, 2) + "\n");
|
|
7539
|
+
filesModified++;
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
}
|
|
7543
|
+
setFixResult({ filesModified, keysAdded });
|
|
7544
|
+
updateTask("fix", {
|
|
7545
|
+
status: keysAdded > 0 ? "success" : "warning",
|
|
7546
|
+
output: keysAdded > 0 ? `Added ${keysAdded} key(s) to ${filesModified} file(s)` : "No keys to fix"
|
|
7547
|
+
});
|
|
7548
|
+
}
|
|
7549
|
+
if (options.output) {
|
|
7550
|
+
updateTask("output", { status: "running" });
|
|
7551
|
+
const fullReport = {
|
|
7552
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7553
|
+
sourceLanguage,
|
|
7554
|
+
targetLanguages,
|
|
7555
|
+
totalKeys,
|
|
7556
|
+
missingKeysCount: missingKeys.length,
|
|
7557
|
+
missingKeys: missingKeys.map((mk) => ({
|
|
7558
|
+
key: mk.key,
|
|
7559
|
+
sourceValue: mk.sourceValue,
|
|
7560
|
+
missingIn: mk.missingIn
|
|
7561
|
+
})),
|
|
7562
|
+
coveragePercentage
|
|
7563
|
+
};
|
|
7564
|
+
try {
|
|
7565
|
+
writeFileSync4(options.output, JSON.stringify(fullReport, null, 2) + "\n");
|
|
7566
|
+
updateTask("output", {
|
|
7567
|
+
status: "success",
|
|
7568
|
+
output: `Report written to ${options.output}`
|
|
7569
|
+
});
|
|
7570
|
+
} catch (writeErr) {
|
|
7571
|
+
updateTask("output", {
|
|
7572
|
+
status: "error",
|
|
7573
|
+
output: `Failed to write report: ${writeErr instanceof Error ? writeErr.message : "Unknown error"}`
|
|
7574
|
+
});
|
|
7575
|
+
}
|
|
7576
|
+
}
|
|
7738
7577
|
setDone(true);
|
|
7739
7578
|
} catch (err) {
|
|
7740
7579
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
@@ -7743,52 +7582,73 @@ ${parseErrors.join("\n")}`);
|
|
|
7743
7582
|
}
|
|
7744
7583
|
run();
|
|
7745
7584
|
}, [options]);
|
|
7746
|
-
return /* @__PURE__ */
|
|
7747
|
-
/* @__PURE__ */
|
|
7748
|
-
/* @__PURE__ */
|
|
7749
|
-
error && /* @__PURE__ */
|
|
7750
|
-
done && result && /* @__PURE__ */
|
|
7751
|
-
result.missingKeys.length === 0 ? /* @__PURE__ */
|
|
7585
|
+
return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", children: [
|
|
7586
|
+
/* @__PURE__ */ jsx27(Header, { compact: true }),
|
|
7587
|
+
/* @__PURE__ */ jsx27(TaskList, { tasks, title: "Checking translations" }),
|
|
7588
|
+
error && /* @__PURE__ */ jsx27(Alert, { type: "error", title: "Error", children: error }),
|
|
7589
|
+
done && result && /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", marginTop: 1, children: [
|
|
7590
|
+
result.missingKeys.length === 0 ? /* @__PURE__ */ jsxs25(Alert, { type: "success", title: "All translations complete", children: [
|
|
7752
7591
|
result.totalKeys,
|
|
7753
7592
|
" keys are translated in all ",
|
|
7754
7593
|
result.targetLanguages.length,
|
|
7755
7594
|
" language(s)"
|
|
7756
|
-
] }) : /* @__PURE__ */
|
|
7595
|
+
] }) : /* @__PURE__ */ jsxs25(Alert, { type: "warning", title: "Missing translations found", children: [
|
|
7757
7596
|
result.missingKeys.length,
|
|
7758
7597
|
" key(s) missing across target languages"
|
|
7759
7598
|
] }),
|
|
7760
|
-
/* @__PURE__ */
|
|
7761
|
-
/* @__PURE__ */
|
|
7762
|
-
Object.entries(result.coveragePercentage).map(([lang, pct]) => /* @__PURE__ */
|
|
7763
|
-
/* @__PURE__ */
|
|
7764
|
-
/* @__PURE__ */
|
|
7599
|
+
/* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", marginTop: 1, children: [
|
|
7600
|
+
/* @__PURE__ */ jsx27(Text25, { bold: true, color: colors.text, children: "Coverage:" }),
|
|
7601
|
+
Object.entries(result.coveragePercentage).map(([lang, pct]) => /* @__PURE__ */ jsxs25(Box24, { marginLeft: 2, children: [
|
|
7602
|
+
/* @__PURE__ */ jsx27(Text25, { color: pct === 100 ? colors.success : pct >= 80 ? colors.warning : colors.error, children: pct === 100 ? icons.success : icons.warning }),
|
|
7603
|
+
/* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7765
7604
|
" ",
|
|
7766
7605
|
lang,
|
|
7767
7606
|
": ",
|
|
7768
|
-
/* @__PURE__ */
|
|
7607
|
+
/* @__PURE__ */ jsxs25(Text25, { color: pct === 100 ? colors.success : colors.text, children: [
|
|
7769
7608
|
pct,
|
|
7770
7609
|
"%"
|
|
7771
7610
|
] })
|
|
7772
7611
|
] })
|
|
7773
7612
|
] }, lang))
|
|
7774
7613
|
] }),
|
|
7775
|
-
result.missingKeys.length > 0 && /* @__PURE__ */
|
|
7776
|
-
/* @__PURE__ */
|
|
7777
|
-
result.missingKeys.map((mk, i) => /* @__PURE__ */
|
|
7778
|
-
/* @__PURE__ */
|
|
7614
|
+
result.missingKeys.length > 0 && /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", marginTop: 1, children: [
|
|
7615
|
+
/* @__PURE__ */ jsx27(Text25, { bold: true, color: colors.text, children: "Missing keys (first 10):" }),
|
|
7616
|
+
result.missingKeys.map((mk, i) => /* @__PURE__ */ jsxs25(Box24, { marginLeft: 2, flexDirection: "column", children: [
|
|
7617
|
+
/* @__PURE__ */ jsxs25(Text25, { color: colors.error, children: [
|
|
7779
7618
|
"\u2022 ",
|
|
7780
7619
|
mk.key
|
|
7781
7620
|
] }),
|
|
7782
|
-
/* @__PURE__ */
|
|
7621
|
+
/* @__PURE__ */ jsx27(Box24, { marginLeft: 2, children: /* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7783
7622
|
"Missing in: ",
|
|
7784
7623
|
mk.missingIn.join(", ")
|
|
7785
7624
|
] }) })
|
|
7786
7625
|
] }, i))
|
|
7787
7626
|
] }),
|
|
7788
|
-
|
|
7627
|
+
fixResult && fixResult.keysAdded > 0 && /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", marginTop: 1, children: [
|
|
7628
|
+
/* @__PURE__ */ jsxs25(Text25, { bold: true, color: colors.success, children: [
|
|
7629
|
+
icons.success,
|
|
7630
|
+
" Fixed ",
|
|
7631
|
+
fixResult.keysAdded,
|
|
7632
|
+
" key(s) in ",
|
|
7633
|
+
fixResult.filesModified,
|
|
7634
|
+
" file(s)"
|
|
7635
|
+
] }),
|
|
7636
|
+
/* @__PURE__ */ jsxs25(Box24, { marginTop: 1, children: [
|
|
7637
|
+
/* @__PURE__ */ jsxs25(Text25, { color: colors.warning, children: [
|
|
7638
|
+
icons.warning,
|
|
7639
|
+
" "
|
|
7640
|
+
] }),
|
|
7641
|
+
/* @__PURE__ */ jsx27(Text25, { color: colors.textMuted, children: "Missing keys have been added with [LANG] prefix. Review and translate them." })
|
|
7642
|
+
] })
|
|
7643
|
+
] }),
|
|
7644
|
+
!options.fix && result.missingKeys.length > 0 && /* @__PURE__ */ jsx27(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7789
7645
|
"Run ",
|
|
7790
|
-
/* @__PURE__ */
|
|
7646
|
+
/* @__PURE__ */ jsx27(Text25, { color: colors.primary, children: "npx @intlpullhq/cli fix" }),
|
|
7791
7647
|
" to auto-generate missing translations"
|
|
7648
|
+
] }) }),
|
|
7649
|
+
options.output && /* @__PURE__ */ jsx27(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7650
|
+
"Report saved to ",
|
|
7651
|
+
/* @__PURE__ */ jsx27(Text25, { color: colors.primary, children: options.output })
|
|
7792
7652
|
] }) })
|
|
7793
7653
|
] })
|
|
7794
7654
|
] });
|
|
@@ -7803,15 +7663,38 @@ function flattenObject2(obj, prefix, result) {
|
|
|
7803
7663
|
}
|
|
7804
7664
|
}
|
|
7805
7665
|
}
|
|
7666
|
+
function flattenObjectToRecord(obj, prefix = "") {
|
|
7667
|
+
const result = {};
|
|
7668
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
7669
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
7670
|
+
if (typeof value === "string") {
|
|
7671
|
+
result[newKey] = value;
|
|
7672
|
+
} else if (typeof value === "object" && value !== null) {
|
|
7673
|
+
Object.assign(result, flattenObjectToRecord(value, newKey));
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
return result;
|
|
7677
|
+
}
|
|
7678
|
+
function setNestedValue(obj, key, value) {
|
|
7679
|
+
const parts = key.split(".");
|
|
7680
|
+
let current = obj;
|
|
7681
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
7682
|
+
if (!current[parts[i]] || typeof current[parts[i]] === "string") {
|
|
7683
|
+
current[parts[i]] = {};
|
|
7684
|
+
}
|
|
7685
|
+
current = current[parts[i]];
|
|
7686
|
+
}
|
|
7687
|
+
current[parts[parts.length - 1]] = value;
|
|
7688
|
+
}
|
|
7806
7689
|
function runCheck(options) {
|
|
7807
|
-
|
|
7690
|
+
render10(/* @__PURE__ */ jsx27(CheckCommand, { options }));
|
|
7808
7691
|
}
|
|
7809
7692
|
|
|
7810
7693
|
// src/commands/diff.tsx
|
|
7811
|
-
import { useState as
|
|
7812
|
-
import { render as
|
|
7694
|
+
import { useState as useState15, useEffect as useEffect12 } from "react";
|
|
7695
|
+
import { render as render11, Box as Box25, Text as Text26 } from "ink";
|
|
7813
7696
|
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
7814
|
-
import { jsx as
|
|
7697
|
+
import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
7815
7698
|
async function fetchProjects5(apiUrl, apiKey) {
|
|
7816
7699
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
7817
7700
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -7828,21 +7711,21 @@ async function fetchProjects5(apiUrl, apiKey) {
|
|
|
7828
7711
|
}));
|
|
7829
7712
|
}
|
|
7830
7713
|
function DiffCommand({ options }) {
|
|
7831
|
-
const [tasks, setTasks] =
|
|
7714
|
+
const [tasks, setTasks] = useState15([
|
|
7832
7715
|
{ id: "config", label: "Loading configuration", status: "pending" },
|
|
7833
7716
|
{ id: "local", label: "Reading local files", status: "pending" },
|
|
7834
7717
|
{ id: "remote", label: "Fetching from IntlPull", status: "pending" },
|
|
7835
7718
|
{ id: "compare", label: "Comparing changes", status: "pending" }
|
|
7836
7719
|
]);
|
|
7837
|
-
const [result, setResult] =
|
|
7838
|
-
const [error, setError] =
|
|
7839
|
-
const [done, setDone] =
|
|
7720
|
+
const [result, setResult] = useState15(null);
|
|
7721
|
+
const [error, setError] = useState15(null);
|
|
7722
|
+
const [done, setDone] = useState15(false);
|
|
7840
7723
|
const updateTask = (id, update) => {
|
|
7841
7724
|
setTasks(
|
|
7842
7725
|
(prev) => prev.map((t) => t.id === id ? { ...t, ...update } : t)
|
|
7843
7726
|
);
|
|
7844
7727
|
};
|
|
7845
|
-
|
|
7728
|
+
useEffect12(() => {
|
|
7846
7729
|
async function run() {
|
|
7847
7730
|
try {
|
|
7848
7731
|
updateTask("config", { status: "running" });
|
|
@@ -7963,12 +7846,12 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
7963
7846
|
}
|
|
7964
7847
|
run();
|
|
7965
7848
|
}, [options.source, options.target]);
|
|
7966
|
-
return /* @__PURE__ */
|
|
7967
|
-
/* @__PURE__ */
|
|
7968
|
-
/* @__PURE__ */
|
|
7969
|
-
error && /* @__PURE__ */
|
|
7970
|
-
done && result && /* @__PURE__ */
|
|
7971
|
-
result.total === 0 ? /* @__PURE__ */
|
|
7849
|
+
return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
|
|
7850
|
+
/* @__PURE__ */ jsx28(Header, { compact: true }),
|
|
7851
|
+
/* @__PURE__ */ jsx28(TaskList, { tasks, title: "Computing diff" }),
|
|
7852
|
+
error && /* @__PURE__ */ jsx28(Alert, { type: "error", title: "Error", children: error }),
|
|
7853
|
+
done && result && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7854
|
+
result.total === 0 ? /* @__PURE__ */ jsx28(Alert, { type: "success", title: "No changes", children: "Local files match IntlPull" }) : /* @__PURE__ */ jsxs26(Alert, { type: "info", title: "Changes detected", children: [
|
|
7972
7855
|
result.added.length,
|
|
7973
7856
|
" new locally, ",
|
|
7974
7857
|
result.removed.length,
|
|
@@ -7976,79 +7859,79 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
7976
7859
|
result.changed.length,
|
|
7977
7860
|
" modified"
|
|
7978
7861
|
] }),
|
|
7979
|
-
result.added.length > 0 && /* @__PURE__ */
|
|
7980
|
-
/* @__PURE__ */
|
|
7862
|
+
result.added.length > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7863
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.success, children: [
|
|
7981
7864
|
"+ New in local (",
|
|
7982
7865
|
result.added.length,
|
|
7983
7866
|
"):"
|
|
7984
7867
|
] }),
|
|
7985
|
-
result.added.slice(0, 5).map((entry, i) => /* @__PURE__ */
|
|
7986
|
-
/* @__PURE__ */
|
|
7987
|
-
/* @__PURE__ */
|
|
7988
|
-
/* @__PURE__ */
|
|
7868
|
+
result.added.slice(0, 5).map((entry, i) => /* @__PURE__ */ jsxs26(Box25, { marginLeft: 2, children: [
|
|
7869
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.success, children: "+ " }),
|
|
7870
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.text, children: entry.key }),
|
|
7871
|
+
/* @__PURE__ */ jsxs26(Text26, { color: colors.textDim, children: [
|
|
7989
7872
|
' = "',
|
|
7990
7873
|
truncate(entry.newValue || "", 40),
|
|
7991
7874
|
'"'
|
|
7992
7875
|
] })
|
|
7993
7876
|
] }, i)),
|
|
7994
|
-
result.added.length > 5 && /* @__PURE__ */
|
|
7877
|
+
result.added.length > 5 && /* @__PURE__ */ jsx28(Box25, { marginLeft: 2, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
7995
7878
|
"... and ",
|
|
7996
7879
|
result.added.length - 5,
|
|
7997
7880
|
" more"
|
|
7998
7881
|
] }) })
|
|
7999
7882
|
] }),
|
|
8000
|
-
result.removed.length > 0 && /* @__PURE__ */
|
|
8001
|
-
/* @__PURE__ */
|
|
7883
|
+
result.removed.length > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7884
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.error, children: [
|
|
8002
7885
|
"- Missing locally (",
|
|
8003
7886
|
result.removed.length,
|
|
8004
7887
|
"):"
|
|
8005
7888
|
] }),
|
|
8006
|
-
result.removed.slice(0, 5).map((entry, i) => /* @__PURE__ */
|
|
8007
|
-
/* @__PURE__ */
|
|
8008
|
-
/* @__PURE__ */
|
|
7889
|
+
result.removed.slice(0, 5).map((entry, i) => /* @__PURE__ */ jsxs26(Box25, { marginLeft: 2, children: [
|
|
7890
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.error, children: "- " }),
|
|
7891
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.text, children: entry.key })
|
|
8009
7892
|
] }, i)),
|
|
8010
|
-
result.removed.length > 5 && /* @__PURE__ */
|
|
7893
|
+
result.removed.length > 5 && /* @__PURE__ */ jsx28(Box25, { marginLeft: 2, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8011
7894
|
"... and ",
|
|
8012
7895
|
result.removed.length - 5,
|
|
8013
7896
|
" more"
|
|
8014
7897
|
] }) })
|
|
8015
7898
|
] }),
|
|
8016
|
-
result.changed.length > 0 && /* @__PURE__ */
|
|
8017
|
-
/* @__PURE__ */
|
|
7899
|
+
result.changed.length > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7900
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.warning, children: [
|
|
8018
7901
|
"~ Modified (",
|
|
8019
7902
|
result.changed.length,
|
|
8020
7903
|
"):"
|
|
8021
7904
|
] }),
|
|
8022
|
-
result.changed.slice(0, 5).map((entry, i) => /* @__PURE__ */
|
|
8023
|
-
/* @__PURE__ */
|
|
8024
|
-
/* @__PURE__ */
|
|
8025
|
-
/* @__PURE__ */
|
|
7905
|
+
result.changed.slice(0, 5).map((entry, i) => /* @__PURE__ */ jsxs26(Box25, { marginLeft: 2, flexDirection: "column", children: [
|
|
7906
|
+
/* @__PURE__ */ jsxs26(Box25, { children: [
|
|
7907
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.warning, children: "~ " }),
|
|
7908
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.text, children: entry.key })
|
|
8026
7909
|
] }),
|
|
8027
|
-
/* @__PURE__ */
|
|
7910
|
+
/* @__PURE__ */ jsx28(Box25, { marginLeft: 4, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textDim, children: [
|
|
8028
7911
|
'remote: "',
|
|
8029
7912
|
truncate(entry.oldValue || "", 30),
|
|
8030
7913
|
'"'
|
|
8031
7914
|
] }) }),
|
|
8032
|
-
/* @__PURE__ */
|
|
7915
|
+
/* @__PURE__ */ jsx28(Box25, { marginLeft: 4, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textDim, children: [
|
|
8033
7916
|
'local: "',
|
|
8034
7917
|
truncate(entry.newValue || "", 30),
|
|
8035
7918
|
'"'
|
|
8036
7919
|
] }) })
|
|
8037
7920
|
] }, i)),
|
|
8038
|
-
result.changed.length > 5 && /* @__PURE__ */
|
|
7921
|
+
result.changed.length > 5 && /* @__PURE__ */ jsx28(Box25, { marginLeft: 2, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8039
7922
|
"... and ",
|
|
8040
7923
|
result.changed.length - 5,
|
|
8041
7924
|
" more"
|
|
8042
7925
|
] }) })
|
|
8043
7926
|
] }),
|
|
8044
|
-
result.added.length > 0 && /* @__PURE__ */
|
|
7927
|
+
result.added.length > 0 && /* @__PURE__ */ jsx28(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8045
7928
|
"Run ",
|
|
8046
|
-
/* @__PURE__ */
|
|
7929
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.primary, children: "npx @intlpullhq/cli upload" }),
|
|
8047
7930
|
" to upload new keys to IntlPull"
|
|
8048
7931
|
] }) }),
|
|
8049
|
-
result.removed.length > 0 && /* @__PURE__ */
|
|
7932
|
+
result.removed.length > 0 && /* @__PURE__ */ jsx28(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8050
7933
|
"Run ",
|
|
8051
|
-
/* @__PURE__ */
|
|
7934
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.primary, children: "npx @intlpullhq/cli download" }),
|
|
8052
7935
|
" to download missing keys from IntlPull"
|
|
8053
7936
|
] }) })
|
|
8054
7937
|
] })
|
|
@@ -8068,30 +7951,30 @@ function truncate(str, length) {
|
|
|
8068
7951
|
return str.length > length ? str.substring(0, length) + "..." : str;
|
|
8069
7952
|
}
|
|
8070
7953
|
function runDiff(options) {
|
|
8071
|
-
|
|
7954
|
+
render11(/* @__PURE__ */ jsx28(DiffCommand, { options }));
|
|
8072
7955
|
}
|
|
8073
7956
|
|
|
8074
7957
|
// src/commands/fix.tsx
|
|
8075
|
-
import { useState as
|
|
8076
|
-
import { render as
|
|
8077
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
7958
|
+
import { useState as useState16, useEffect as useEffect13 } from "react";
|
|
7959
|
+
import { render as render12, Box as Box26, Text as Text27 } from "ink";
|
|
7960
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
8078
7961
|
import { glob as glob2 } from "glob";
|
|
8079
|
-
import { jsx as
|
|
7962
|
+
import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
8080
7963
|
function FixCommand({ options }) {
|
|
8081
|
-
const [tasks, setTasks] =
|
|
7964
|
+
const [tasks, setTasks] = useState16([
|
|
8082
7965
|
{ id: "config", label: "Loading configuration", status: "pending" },
|
|
8083
7966
|
{ id: "scan", label: "Scanning for missing keys", status: "pending" },
|
|
8084
7967
|
{ id: "fix", label: "Adding missing keys", status: "pending" }
|
|
8085
7968
|
]);
|
|
8086
|
-
const [result, setResult] =
|
|
8087
|
-
const [error, setError] =
|
|
8088
|
-
const [done, setDone] =
|
|
7969
|
+
const [result, setResult] = useState16(null);
|
|
7970
|
+
const [error, setError] = useState16(null);
|
|
7971
|
+
const [done, setDone] = useState16(false);
|
|
8089
7972
|
const updateTask = (id, update) => {
|
|
8090
7973
|
setTasks(
|
|
8091
7974
|
(prev) => prev.map((t) => t.id === id ? { ...t, ...update } : t)
|
|
8092
7975
|
);
|
|
8093
7976
|
};
|
|
8094
|
-
|
|
7977
|
+
useEffect13(() => {
|
|
8095
7978
|
async function run() {
|
|
8096
7979
|
try {
|
|
8097
7980
|
updateTask("config", { status: "running" });
|
|
@@ -8110,7 +7993,8 @@ function FixCommand({ options }) {
|
|
|
8110
7993
|
`${localeDir}/**/common.json`,
|
|
8111
7994
|
`${localeDir}/**/*.json`,
|
|
8112
7995
|
`${localeDir}/*.json`,
|
|
8113
|
-
`./messages/*.json
|
|
7996
|
+
`./messages/*.json`,
|
|
7997
|
+
`./locales/**/*.json`
|
|
8114
7998
|
];
|
|
8115
7999
|
let localeFiles = [];
|
|
8116
8000
|
for (const pattern of patterns) {
|
|
@@ -8123,15 +8007,16 @@ function FixCommand({ options }) {
|
|
|
8123
8007
|
if (localeFiles.length === 0) {
|
|
8124
8008
|
throw new Error(`No translation files found in ${localeDir}`);
|
|
8125
8009
|
}
|
|
8010
|
+
const LANG_CODE_REGEX2 = /^[a-z]{2}([-_][a-zA-Z]{2})?$/i;
|
|
8126
8011
|
const filesByLang = {};
|
|
8127
8012
|
for (const file of localeFiles) {
|
|
8128
8013
|
const parts = file.split("/");
|
|
8129
8014
|
const fileName = parts[parts.length - 1];
|
|
8130
8015
|
const dirName = parts[parts.length - 2];
|
|
8131
|
-
if (
|
|
8016
|
+
if (LANG_CODE_REGEX2.test(dirName)) {
|
|
8132
8017
|
if (!filesByLang[dirName]) filesByLang[dirName] = [];
|
|
8133
8018
|
filesByLang[dirName].push(file);
|
|
8134
|
-
} else if (
|
|
8019
|
+
} else if (LANG_CODE_REGEX2.test(fileName.replace(".json", ""))) {
|
|
8135
8020
|
const lang = fileName.replace(".json", "");
|
|
8136
8021
|
if (!filesByLang[lang]) filesByLang[lang] = [];
|
|
8137
8022
|
filesByLang[lang].push(file);
|
|
@@ -8180,13 +8065,13 @@ ${parseErrors.join("\n")}`);
|
|
|
8180
8065
|
for (const key of sourceKeys) {
|
|
8181
8066
|
if (!(key in flatTarget)) {
|
|
8182
8067
|
const sourceValue = flatSource[key] || "";
|
|
8183
|
-
|
|
8068
|
+
setNestedValue2(content, key, `[${targetLang.toUpperCase()}] ${sourceValue}`);
|
|
8184
8069
|
keysAdded++;
|
|
8185
8070
|
modified = true;
|
|
8186
8071
|
}
|
|
8187
8072
|
}
|
|
8188
8073
|
if (modified && !options.dryRun) {
|
|
8189
|
-
|
|
8074
|
+
writeFileSync5(file, JSON.stringify(content, null, 2) + "\n");
|
|
8190
8075
|
filesModified++;
|
|
8191
8076
|
} else if (modified) {
|
|
8192
8077
|
filesModified++;
|
|
@@ -8210,44 +8095,44 @@ ${parseErrors.join("\n")}`);
|
|
|
8210
8095
|
}
|
|
8211
8096
|
run();
|
|
8212
8097
|
}, [options]);
|
|
8213
|
-
return /* @__PURE__ */
|
|
8214
|
-
/* @__PURE__ */
|
|
8215
|
-
/* @__PURE__ */
|
|
8216
|
-
error && /* @__PURE__ */
|
|
8217
|
-
done && result && /* @__PURE__ */
|
|
8218
|
-
result.keysAdded === 0 ? /* @__PURE__ */
|
|
8098
|
+
return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
|
|
8099
|
+
/* @__PURE__ */ jsx29(Header, { compact: true }),
|
|
8100
|
+
/* @__PURE__ */ jsx29(TaskList, { tasks, title: "Fixing missing translations" }),
|
|
8101
|
+
error && /* @__PURE__ */ jsx29(Alert, { type: "error", title: "Error", children: error }),
|
|
8102
|
+
done && result && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", marginTop: 1, children: [
|
|
8103
|
+
result.keysAdded === 0 ? /* @__PURE__ */ jsx29(Alert, { type: "success", title: "No fixes needed", children: "All translations are complete" }) : options.dryRun ? /* @__PURE__ */ jsxs27(Alert, { type: "warning", title: "Dry run complete", children: [
|
|
8219
8104
|
"Would add ",
|
|
8220
8105
|
result.keysAdded,
|
|
8221
8106
|
" missing key(s) to ",
|
|
8222
8107
|
result.filesModified,
|
|
8223
8108
|
" file(s)"
|
|
8224
|
-
] }) : /* @__PURE__ */
|
|
8109
|
+
] }) : /* @__PURE__ */ jsxs27(Alert, { type: "success", title: "Fix complete", children: [
|
|
8225
8110
|
"Added ",
|
|
8226
8111
|
result.keysAdded,
|
|
8227
8112
|
" missing key(s) to ",
|
|
8228
8113
|
result.filesModified,
|
|
8229
8114
|
" file(s)"
|
|
8230
8115
|
] }),
|
|
8231
|
-
result.keysAdded > 0 && /* @__PURE__ */
|
|
8232
|
-
/* @__PURE__ */
|
|
8233
|
-
result.languages.map((lang) => /* @__PURE__ */
|
|
8234
|
-
/* @__PURE__ */
|
|
8116
|
+
result.keysAdded > 0 && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", marginTop: 1, children: [
|
|
8117
|
+
/* @__PURE__ */ jsx29(Text27, { bold: true, color: colors.text, children: "Languages fixed:" }),
|
|
8118
|
+
result.languages.map((lang) => /* @__PURE__ */ jsxs27(Box26, { marginLeft: 2, children: [
|
|
8119
|
+
/* @__PURE__ */ jsxs27(Text27, { color: colors.success, children: [
|
|
8235
8120
|
icons.success,
|
|
8236
8121
|
" "
|
|
8237
8122
|
] }),
|
|
8238
|
-
/* @__PURE__ */
|
|
8123
|
+
/* @__PURE__ */ jsx29(Text27, { color: colors.text, children: lang })
|
|
8239
8124
|
] }, lang))
|
|
8240
8125
|
] }),
|
|
8241
|
-
result.keysAdded > 0 && !options.dryRun && /* @__PURE__ */
|
|
8242
|
-
/* @__PURE__ */
|
|
8126
|
+
result.keysAdded > 0 && !options.dryRun && /* @__PURE__ */ jsxs27(Box26, { marginTop: 1, children: [
|
|
8127
|
+
/* @__PURE__ */ jsxs27(Text27, { color: colors.warning, children: [
|
|
8243
8128
|
icons.warning,
|
|
8244
8129
|
" "
|
|
8245
8130
|
] }),
|
|
8246
|
-
/* @__PURE__ */
|
|
8131
|
+
/* @__PURE__ */ jsx29(Text27, { color: colors.textMuted, children: "Missing keys have been added with [LANG] prefix. Review and translate them." })
|
|
8247
8132
|
] }),
|
|
8248
|
-
options.dryRun && result.keysAdded > 0 && /* @__PURE__ */
|
|
8133
|
+
options.dryRun && result.keysAdded > 0 && /* @__PURE__ */ jsx29(Box26, { marginTop: 1, children: /* @__PURE__ */ jsxs27(Text27, { color: colors.textMuted, children: [
|
|
8249
8134
|
"Run without ",
|
|
8250
|
-
/* @__PURE__ */
|
|
8135
|
+
/* @__PURE__ */ jsx29(Text27, { color: colors.primary, children: "--dry-run" }),
|
|
8251
8136
|
" to apply fixes"
|
|
8252
8137
|
] }) })
|
|
8253
8138
|
] })
|
|
@@ -8265,7 +8150,7 @@ function flattenObject4(obj, prefix = "") {
|
|
|
8265
8150
|
}
|
|
8266
8151
|
return result;
|
|
8267
8152
|
}
|
|
8268
|
-
function
|
|
8153
|
+
function setNestedValue2(obj, key, value) {
|
|
8269
8154
|
const parts = key.split(".");
|
|
8270
8155
|
let current = obj;
|
|
8271
8156
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
@@ -8277,16 +8162,16 @@ function setNestedValue(obj, key, value) {
|
|
|
8277
8162
|
current[parts[parts.length - 1]] = value;
|
|
8278
8163
|
}
|
|
8279
8164
|
function runFix(options) {
|
|
8280
|
-
|
|
8165
|
+
render12(/* @__PURE__ */ jsx29(FixCommand, { options }));
|
|
8281
8166
|
}
|
|
8282
8167
|
|
|
8283
8168
|
// src/commands/listen.tsx
|
|
8284
|
-
import { useState as
|
|
8285
|
-
import { render as
|
|
8286
|
-
import
|
|
8287
|
-
import { writeFileSync as
|
|
8288
|
-
import { join as
|
|
8289
|
-
import { jsx as
|
|
8169
|
+
import { useState as useState17, useEffect as useEffect14, useCallback, useRef as useRef2 } from "react";
|
|
8170
|
+
import { render as render13, Box as Box27, Text as Text28, useApp as useApp6, useInput as useInput7 } from "ink";
|
|
8171
|
+
import Spinner12 from "ink-spinner";
|
|
8172
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "fs";
|
|
8173
|
+
import { join as join8 } from "path";
|
|
8174
|
+
import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
8290
8175
|
async function fetchProjects6(apiUrl, apiKey) {
|
|
8291
8176
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
8292
8177
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -8423,9 +8308,9 @@ function writeTranslationFiles(bundle, outputDir, format, languages) {
|
|
|
8423
8308
|
const targetLanguages = languages || Object.keys(bundle);
|
|
8424
8309
|
for (const locale of targetLanguages) {
|
|
8425
8310
|
if (!bundle[locale]) continue;
|
|
8426
|
-
const localeDir =
|
|
8311
|
+
const localeDir = join8(outputDir, locale);
|
|
8427
8312
|
if (!existsSync9(localeDir)) {
|
|
8428
|
-
|
|
8313
|
+
mkdirSync4(localeDir, { recursive: true });
|
|
8429
8314
|
}
|
|
8430
8315
|
let content;
|
|
8431
8316
|
let filename;
|
|
@@ -8445,8 +8330,8 @@ function writeTranslationFiles(bundle, outputDir, format, languages) {
|
|
|
8445
8330
|
filename = "translations.json";
|
|
8446
8331
|
break;
|
|
8447
8332
|
}
|
|
8448
|
-
const filePath =
|
|
8449
|
-
|
|
8333
|
+
const filePath = join8(localeDir, filename);
|
|
8334
|
+
writeFileSync6(filePath, content);
|
|
8450
8335
|
writtenFiles.push(filePath);
|
|
8451
8336
|
}
|
|
8452
8337
|
return writtenFiles;
|
|
@@ -8468,7 +8353,7 @@ function formatRelativeTime(date) {
|
|
|
8468
8353
|
}
|
|
8469
8354
|
function ListenComponent({ options }) {
|
|
8470
8355
|
const { exit } = useApp6();
|
|
8471
|
-
const [state, setState] =
|
|
8356
|
+
const [state, setState] = useState17({
|
|
8472
8357
|
status: "connecting",
|
|
8473
8358
|
message: "Connecting to IntlPull...",
|
|
8474
8359
|
lastSync: null,
|
|
@@ -8592,7 +8477,7 @@ function ListenComponent({ options }) {
|
|
|
8592
8477
|
}));
|
|
8593
8478
|
}
|
|
8594
8479
|
}, [options, state.version, state.syncCount]);
|
|
8595
|
-
|
|
8480
|
+
useEffect14(() => {
|
|
8596
8481
|
sync();
|
|
8597
8482
|
if (options.once) {
|
|
8598
8483
|
return;
|
|
@@ -8604,7 +8489,7 @@ function ListenComponent({ options }) {
|
|
|
8604
8489
|
}, intervalMs);
|
|
8605
8490
|
return () => clearInterval(interval);
|
|
8606
8491
|
}, [sync, intervalMs, options.once]);
|
|
8607
|
-
|
|
8492
|
+
useEffect14(() => {
|
|
8608
8493
|
if (!options.timeout || options.once) return;
|
|
8609
8494
|
const timeoutMs = options.timeout * 1e3;
|
|
8610
8495
|
const timer = setTimeout(() => {
|
|
@@ -8617,7 +8502,7 @@ function ListenComponent({ options }) {
|
|
|
8617
8502
|
}, timeoutMs);
|
|
8618
8503
|
return () => clearTimeout(timer);
|
|
8619
8504
|
}, [options.timeout, options.once, exit]);
|
|
8620
|
-
|
|
8505
|
+
useEffect14(() => {
|
|
8621
8506
|
if (options.once && state.status === "watching") {
|
|
8622
8507
|
setTimeout(() => exit(), 100);
|
|
8623
8508
|
}
|
|
@@ -8636,57 +8521,57 @@ function ListenComponent({ options }) {
|
|
|
8636
8521
|
error: "\u2717",
|
|
8637
8522
|
stopped: "\u25FB"
|
|
8638
8523
|
}[state.status];
|
|
8639
|
-
return /* @__PURE__ */
|
|
8640
|
-
/* @__PURE__ */
|
|
8641
|
-
/* @__PURE__ */
|
|
8642
|
-
/* @__PURE__ */
|
|
8643
|
-
isInteractive && /* @__PURE__ */
|
|
8524
|
+
return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", paddingX: 1, children: [
|
|
8525
|
+
/* @__PURE__ */ jsxs28(Box27, { marginBottom: 1, children: [
|
|
8526
|
+
/* @__PURE__ */ jsx30(Text28, { bold: true, color: "cyan", children: "IntlPull" }),
|
|
8527
|
+
/* @__PURE__ */ jsx30(Text28, { children: " \u2022 Live Sync" }),
|
|
8528
|
+
isInteractive && /* @__PURE__ */ jsx30(Text28, { dimColor: true, children: " (press q to quit)" })
|
|
8644
8529
|
] }),
|
|
8645
|
-
/* @__PURE__ */
|
|
8646
|
-
state.status === "syncing" ? /* @__PURE__ */
|
|
8647
|
-
/* @__PURE__ */
|
|
8648
|
-
/* @__PURE__ */
|
|
8530
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8531
|
+
state.status === "syncing" ? /* @__PURE__ */ jsx30(Text28, { color: "cyan", children: /* @__PURE__ */ jsx30(Spinner12, { type: "dots" }) }) : /* @__PURE__ */ jsx30(Text28, { color: statusColor, children: statusIcon }),
|
|
8532
|
+
/* @__PURE__ */ jsx30(Text28, { children: " " }),
|
|
8533
|
+
/* @__PURE__ */ jsx30(Text28, { color: statusColor, children: state.message })
|
|
8649
8534
|
] }),
|
|
8650
|
-
state.error && /* @__PURE__ */
|
|
8651
|
-
/* @__PURE__ */
|
|
8652
|
-
/* @__PURE__ */
|
|
8535
|
+
state.error && /* @__PURE__ */ jsxs28(Box27, { marginTop: 1, flexDirection: "column", children: [
|
|
8536
|
+
/* @__PURE__ */ jsx30(Text28, { color: "red", children: state.error }),
|
|
8537
|
+
/* @__PURE__ */ jsxs28(Text28, { dimColor: true, children: [
|
|
8653
8538
|
"Retrying in ",
|
|
8654
8539
|
intervalMs / 1e3,
|
|
8655
8540
|
"s..."
|
|
8656
8541
|
] })
|
|
8657
8542
|
] }),
|
|
8658
|
-
state.lastSync && !options.quiet && /* @__PURE__ */
|
|
8659
|
-
/* @__PURE__ */
|
|
8660
|
-
/* @__PURE__ */
|
|
8661
|
-
/* @__PURE__ */
|
|
8543
|
+
state.lastSync && !options.quiet && /* @__PURE__ */ jsxs28(Box27, { marginTop: 1, flexDirection: "column", children: [
|
|
8544
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8545
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Output: " }),
|
|
8546
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.outputDir })
|
|
8662
8547
|
] }),
|
|
8663
|
-
/* @__PURE__ */
|
|
8664
|
-
/* @__PURE__ */
|
|
8665
|
-
/* @__PURE__ */
|
|
8548
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8549
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Version: " }),
|
|
8550
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.version })
|
|
8666
8551
|
] }),
|
|
8667
|
-
/* @__PURE__ */
|
|
8668
|
-
/* @__PURE__ */
|
|
8669
|
-
/* @__PURE__ */
|
|
8552
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8553
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Languages: " }),
|
|
8554
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.languages.join(", ") })
|
|
8670
8555
|
] }),
|
|
8671
|
-
/* @__PURE__ */
|
|
8672
|
-
/* @__PURE__ */
|
|
8673
|
-
/* @__PURE__ */
|
|
8556
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8557
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Keys: " }),
|
|
8558
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.keyCount })
|
|
8674
8559
|
] }),
|
|
8675
|
-
/* @__PURE__ */
|
|
8676
|
-
/* @__PURE__ */
|
|
8677
|
-
/* @__PURE__ */
|
|
8560
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8561
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Last sync: " }),
|
|
8562
|
+
/* @__PURE__ */ jsxs28(Text28, { children: [
|
|
8678
8563
|
formatTime(state.lastSync),
|
|
8679
8564
|
" (",
|
|
8680
8565
|
formatRelativeTime(state.lastSync),
|
|
8681
8566
|
")"
|
|
8682
8567
|
] })
|
|
8683
8568
|
] }),
|
|
8684
|
-
/* @__PURE__ */
|
|
8685
|
-
/* @__PURE__ */
|
|
8686
|
-
/* @__PURE__ */
|
|
8569
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8570
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Syncs: " }),
|
|
8571
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.syncCount })
|
|
8687
8572
|
] })
|
|
8688
8573
|
] }),
|
|
8689
|
-
state.status === "watching" && !options.once && !options.quiet && /* @__PURE__ */
|
|
8574
|
+
state.status === "watching" && !options.once && !options.quiet && /* @__PURE__ */ jsx30(Box27, { marginTop: 1, children: /* @__PURE__ */ jsxs28(Text28, { dimColor: true, children: [
|
|
8690
8575
|
"Polling every ",
|
|
8691
8576
|
intervalMs / 1e3,
|
|
8692
8577
|
"s for changes..."
|
|
@@ -8809,14 +8694,14 @@ function runListen(options) {
|
|
|
8809
8694
|
runOnceSync(options);
|
|
8810
8695
|
return;
|
|
8811
8696
|
}
|
|
8812
|
-
|
|
8697
|
+
render13(/* @__PURE__ */ jsx30(ListenComponent, { options }));
|
|
8813
8698
|
}
|
|
8814
8699
|
|
|
8815
8700
|
// src/commands/publish.tsx
|
|
8816
|
-
import { useState as
|
|
8817
|
-
import { render as
|
|
8818
|
-
import
|
|
8819
|
-
import { jsx as
|
|
8701
|
+
import { useState as useState18, useEffect as useEffect15 } from "react";
|
|
8702
|
+
import { render as render14, Box as Box28, Text as Text29 } from "ink";
|
|
8703
|
+
import Spinner13 from "ink-spinner";
|
|
8704
|
+
import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
8820
8705
|
async function fetchProjects7(apiUrl, apiKey) {
|
|
8821
8706
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
8822
8707
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -8986,11 +8871,11 @@ function generateVersion() {
|
|
|
8986
8871
|
return `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}-${now.getHours()}${now.getMinutes()}`;
|
|
8987
8872
|
}
|
|
8988
8873
|
function PublishComponent({ options }) {
|
|
8989
|
-
const [state, setState] =
|
|
8874
|
+
const [state, setState] = useState18({
|
|
8990
8875
|
status: "detecting",
|
|
8991
8876
|
message: "Detecting project..."
|
|
8992
8877
|
});
|
|
8993
|
-
|
|
8878
|
+
useEffect15(() => {
|
|
8994
8879
|
async function run() {
|
|
8995
8880
|
try {
|
|
8996
8881
|
const resolved = getResolvedApiKey();
|
|
@@ -9074,96 +8959,96 @@ function PublishComponent({ options }) {
|
|
|
9074
8959
|
}
|
|
9075
8960
|
return null;
|
|
9076
8961
|
}
|
|
9077
|
-
return /* @__PURE__ */
|
|
9078
|
-
/* @__PURE__ */
|
|
9079
|
-
/* @__PURE__ */
|
|
9080
|
-
/* @__PURE__ */
|
|
9081
|
-
options.dryRun && /* @__PURE__ */
|
|
8962
|
+
return /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", paddingX: 1, children: [
|
|
8963
|
+
/* @__PURE__ */ jsxs29(Box28, { marginBottom: 1, children: [
|
|
8964
|
+
/* @__PURE__ */ jsx31(Text29, { bold: true, color: "cyan", children: "IntlPull" }),
|
|
8965
|
+
/* @__PURE__ */ jsx31(Text29, { children: " Publish OTA Release" }),
|
|
8966
|
+
options.dryRun && /* @__PURE__ */ jsx31(Text29, { color: "yellow", children: " (dry run)" })
|
|
9082
8967
|
] }),
|
|
9083
|
-
(state.status === "detecting" || state.status === "publishing") && /* @__PURE__ */
|
|
9084
|
-
/* @__PURE__ */
|
|
9085
|
-
/* @__PURE__ */
|
|
8968
|
+
(state.status === "detecting" || state.status === "publishing") && /* @__PURE__ */ jsxs29(Box28, { children: [
|
|
8969
|
+
/* @__PURE__ */ jsx31(Text29, { color: "cyan", children: /* @__PURE__ */ jsx31(Spinner13, { type: "dots" }) }),
|
|
8970
|
+
/* @__PURE__ */ jsxs29(Text29, { children: [
|
|
9086
8971
|
" ",
|
|
9087
8972
|
state.message
|
|
9088
8973
|
] })
|
|
9089
8974
|
] }),
|
|
9090
|
-
state.status === "success" && /* @__PURE__ */
|
|
9091
|
-
/* @__PURE__ */
|
|
8975
|
+
state.status === "success" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
|
|
8976
|
+
/* @__PURE__ */ jsxs29(Text29, { color: "green", children: [
|
|
9092
8977
|
"\u2713 ",
|
|
9093
8978
|
state.message
|
|
9094
8979
|
] }),
|
|
9095
|
-
state.projectName && /* @__PURE__ */
|
|
8980
|
+
state.projectName && /* @__PURE__ */ jsx31(Box28, { marginTop: 1, children: /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9096
8981
|
"Project: ",
|
|
9097
|
-
/* @__PURE__ */
|
|
8982
|
+
/* @__PURE__ */ jsx31(Text29, { color: "white", children: state.projectName })
|
|
9098
8983
|
] }) }),
|
|
9099
|
-
state.branch && /* @__PURE__ */
|
|
8984
|
+
state.branch && /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9100
8985
|
"Branch: ",
|
|
9101
|
-
/* @__PURE__ */
|
|
8986
|
+
/* @__PURE__ */ jsx31(Text29, { color: "white", children: state.branch })
|
|
9102
8987
|
] }),
|
|
9103
|
-
state.release && /* @__PURE__ */
|
|
9104
|
-
/* @__PURE__ */
|
|
8988
|
+
state.release && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 1, children: [
|
|
8989
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9105
8990
|
"Version: ",
|
|
9106
|
-
/* @__PURE__ */
|
|
8991
|
+
/* @__PURE__ */ jsx31(Text29, { color: "green", bold: true, children: state.release.version })
|
|
9107
8992
|
] }),
|
|
9108
|
-
/* @__PURE__ */
|
|
8993
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9109
8994
|
"Languages: ",
|
|
9110
8995
|
state.release.languages.join(", ")
|
|
9111
8996
|
] }),
|
|
9112
|
-
/* @__PURE__ */
|
|
8997
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9113
8998
|
"Keys: ",
|
|
9114
8999
|
state.release.keyCount
|
|
9115
9000
|
] }),
|
|
9116
|
-
/* @__PURE__ */
|
|
9001
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9117
9002
|
"Bundle size: ",
|
|
9118
9003
|
formatBytes(state.release.bundleSize)
|
|
9119
9004
|
] }),
|
|
9120
|
-
/* @__PURE__ */
|
|
9121
|
-
/* @__PURE__ */
|
|
9122
|
-
/* @__PURE__ */
|
|
9005
|
+
/* @__PURE__ */ jsxs29(Box28, { marginTop: 1, flexDirection: "column", children: [
|
|
9006
|
+
/* @__PURE__ */ jsx31(Text29, { dimColor: true, children: "SDK Manifest URL:" }),
|
|
9007
|
+
/* @__PURE__ */ jsxs29(Text29, { color: "cyan", children: [
|
|
9123
9008
|
" ",
|
|
9124
9009
|
state.release.bundleUrl.replace(/\/[^/]+\.json$/, "/manifest")
|
|
9125
9010
|
] })
|
|
9126
9011
|
] })
|
|
9127
9012
|
] }),
|
|
9128
|
-
!state.release && state.summary && /* @__PURE__ */
|
|
9129
|
-
/* @__PURE__ */
|
|
9130
|
-
/* @__PURE__ */
|
|
9013
|
+
!state.release && state.summary && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 1, children: [
|
|
9014
|
+
/* @__PURE__ */ jsx31(Text29, { dimColor: true, children: "Would publish:" }),
|
|
9015
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9131
9016
|
" Languages: ",
|
|
9132
9017
|
state.summary.languages.join(", ")
|
|
9133
9018
|
] }),
|
|
9134
|
-
/* @__PURE__ */
|
|
9019
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9135
9020
|
" Keys: ",
|
|
9136
9021
|
state.summary.keyCount
|
|
9137
9022
|
] }),
|
|
9138
|
-
/* @__PURE__ */
|
|
9023
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9139
9024
|
" Estimated size: ",
|
|
9140
9025
|
formatBytes(state.summary.estimatedSize)
|
|
9141
9026
|
] })
|
|
9142
9027
|
] })
|
|
9143
9028
|
] }),
|
|
9144
|
-
state.status === "error" && /* @__PURE__ */
|
|
9145
|
-
/* @__PURE__ */
|
|
9029
|
+
state.status === "error" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
|
|
9030
|
+
/* @__PURE__ */ jsxs29(Text29, { color: "red", children: [
|
|
9146
9031
|
"\u2717 ",
|
|
9147
9032
|
state.message
|
|
9148
9033
|
] }),
|
|
9149
|
-
/* @__PURE__ */
|
|
9150
|
-
/* @__PURE__ */
|
|
9151
|
-
/* @__PURE__ */
|
|
9152
|
-
/* @__PURE__ */
|
|
9153
|
-
/* @__PURE__ */
|
|
9034
|
+
/* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 1, children: [
|
|
9035
|
+
/* @__PURE__ */ jsx31(Text29, { dimColor: true, children: "Tips:" }),
|
|
9036
|
+
/* @__PURE__ */ jsx31(Text29, { color: "gray", children: " \u2022 Run `npx @intlpullhq/cli upload` to upload translations first" }),
|
|
9037
|
+
/* @__PURE__ */ jsx31(Text29, { color: "gray", children: " \u2022 Use `npx @intlpullhq/cli publish 1.0.0` to specify a version" }),
|
|
9038
|
+
/* @__PURE__ */ jsx31(Text29, { color: "gray", children: " \u2022 Use `npx @intlpullhq/cli publish --dry-run` to preview" })
|
|
9154
9039
|
] })
|
|
9155
9040
|
] })
|
|
9156
9041
|
] });
|
|
9157
9042
|
}
|
|
9158
9043
|
function runPublish(options) {
|
|
9159
|
-
|
|
9044
|
+
render14(/* @__PURE__ */ jsx31(PublishComponent, { options }));
|
|
9160
9045
|
}
|
|
9161
9046
|
|
|
9162
9047
|
// src/commands/releases.tsx
|
|
9163
|
-
import { useState as
|
|
9164
|
-
import { render as
|
|
9165
|
-
import
|
|
9166
|
-
import { jsx as
|
|
9048
|
+
import { useState as useState19, useEffect as useEffect16 } from "react";
|
|
9049
|
+
import { render as render15, Box as Box29, Text as Text30 } from "ink";
|
|
9050
|
+
import Spinner14 from "ink-spinner";
|
|
9051
|
+
import { jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
9167
9052
|
async function fetchProjects8(apiUrl, apiKey) {
|
|
9168
9053
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
9169
9054
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -9237,11 +9122,11 @@ function formatDate(dateStr) {
|
|
|
9237
9122
|
});
|
|
9238
9123
|
}
|
|
9239
9124
|
function ReleasesComponent({ options }) {
|
|
9240
|
-
const [state, setState] =
|
|
9125
|
+
const [state, setState] = useState19({
|
|
9241
9126
|
status: "loading",
|
|
9242
9127
|
message: "Loading releases..."
|
|
9243
9128
|
});
|
|
9244
|
-
|
|
9129
|
+
useEffect16(() => {
|
|
9245
9130
|
async function run() {
|
|
9246
9131
|
try {
|
|
9247
9132
|
const resolved = getResolvedApiKey();
|
|
@@ -9306,41 +9191,41 @@ function ReleasesComponent({ options }) {
|
|
|
9306
9191
|
}
|
|
9307
9192
|
return null;
|
|
9308
9193
|
}
|
|
9309
|
-
return /* @__PURE__ */
|
|
9310
|
-
/* @__PURE__ */
|
|
9311
|
-
/* @__PURE__ */
|
|
9312
|
-
/* @__PURE__ */
|
|
9194
|
+
return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", paddingX: 1, children: [
|
|
9195
|
+
/* @__PURE__ */ jsxs30(Box29, { marginBottom: 1, children: [
|
|
9196
|
+
/* @__PURE__ */ jsx32(Text30, { bold: true, color: "cyan", children: "IntlPull" }),
|
|
9197
|
+
/* @__PURE__ */ jsx32(Text30, { children: " OTA Releases" })
|
|
9313
9198
|
] }),
|
|
9314
|
-
state.status === "loading" && /* @__PURE__ */
|
|
9315
|
-
/* @__PURE__ */
|
|
9316
|
-
/* @__PURE__ */
|
|
9199
|
+
state.status === "loading" && /* @__PURE__ */ jsxs30(Box29, { children: [
|
|
9200
|
+
/* @__PURE__ */ jsx32(Text30, { color: "cyan", children: /* @__PURE__ */ jsx32(Spinner14, { type: "dots" }) }),
|
|
9201
|
+
/* @__PURE__ */ jsxs30(Text30, { children: [
|
|
9317
9202
|
" ",
|
|
9318
9203
|
state.message
|
|
9319
9204
|
] })
|
|
9320
9205
|
] }),
|
|
9321
|
-
state.status === "success" && state.deleted && /* @__PURE__ */
|
|
9206
|
+
state.status === "success" && state.deleted && /* @__PURE__ */ jsxs30(Text30, { color: "green", children: [
|
|
9322
9207
|
"\u2713 ",
|
|
9323
9208
|
state.message
|
|
9324
9209
|
] }),
|
|
9325
|
-
state.status === "success" && state.releases && /* @__PURE__ */
|
|
9326
|
-
state.projectName && /* @__PURE__ */
|
|
9210
|
+
state.status === "success" && state.releases && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
|
|
9211
|
+
state.projectName && /* @__PURE__ */ jsx32(Box29, { marginBottom: 1, children: /* @__PURE__ */ jsxs30(Text30, { dimColor: true, children: [
|
|
9327
9212
|
"Project: ",
|
|
9328
|
-
/* @__PURE__ */
|
|
9213
|
+
/* @__PURE__ */ jsx32(Text30, { color: "white", children: state.projectName })
|
|
9329
9214
|
] }) }),
|
|
9330
|
-
state.releases.length === 0 ? /* @__PURE__ */
|
|
9331
|
-
/* @__PURE__ */
|
|
9332
|
-
/* @__PURE__ */
|
|
9333
|
-
] }) : /* @__PURE__ */
|
|
9334
|
-
state.releases.map((release, i) => /* @__PURE__ */
|
|
9335
|
-
/* @__PURE__ */
|
|
9336
|
-
/* @__PURE__ */
|
|
9337
|
-
/* @__PURE__ */
|
|
9215
|
+
state.releases.length === 0 ? /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
|
|
9216
|
+
/* @__PURE__ */ jsx32(Text30, { color: "yellow", children: "No releases found" }),
|
|
9217
|
+
/* @__PURE__ */ jsx32(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text30, { dimColor: true, children: "Run `npx @intlpullhq/cli publish [version]` to create your first OTA release." }) })
|
|
9218
|
+
] }) : /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
|
|
9219
|
+
state.releases.map((release, i) => /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginBottom: i < state.releases.length - 1 ? 1 : 0, children: [
|
|
9220
|
+
/* @__PURE__ */ jsxs30(Box29, { children: [
|
|
9221
|
+
/* @__PURE__ */ jsx32(Text30, { color: "green", bold: true, children: release.version }),
|
|
9222
|
+
/* @__PURE__ */ jsxs30(Text30, { dimColor: true, children: [
|
|
9338
9223
|
" \u2022 ",
|
|
9339
9224
|
formatDate(release.publishedAt)
|
|
9340
9225
|
] }),
|
|
9341
|
-
i === 0 && /* @__PURE__ */
|
|
9226
|
+
i === 0 && /* @__PURE__ */ jsx32(Text30, { color: "cyan", children: " (latest)" })
|
|
9342
9227
|
] }),
|
|
9343
|
-
/* @__PURE__ */
|
|
9228
|
+
/* @__PURE__ */ jsx32(Box29, { paddingLeft: 2, children: /* @__PURE__ */ jsxs30(Text30, { dimColor: true, children: [
|
|
9344
9229
|
release.keyCount,
|
|
9345
9230
|
" keys \u2022 ",
|
|
9346
9231
|
release.languages.length,
|
|
@@ -9348,44 +9233,44 @@ function ReleasesComponent({ options }) {
|
|
|
9348
9233
|
formatBytes2(release.bundleSize),
|
|
9349
9234
|
release.downloadCount > 0 && ` \u2022 ${release.downloadCount} downloads`
|
|
9350
9235
|
] }) }),
|
|
9351
|
-
/* @__PURE__ */
|
|
9236
|
+
/* @__PURE__ */ jsx32(Box29, { paddingLeft: 2, children: /* @__PURE__ */ jsxs30(Text30, { color: "gray", children: [
|
|
9352
9237
|
"ID: ",
|
|
9353
9238
|
release.id
|
|
9354
9239
|
] }) })
|
|
9355
9240
|
] }, release.id)),
|
|
9356
|
-
/* @__PURE__ */
|
|
9241
|
+
/* @__PURE__ */ jsx32(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text30, { dimColor: true, children: "Use `npx @intlpullhq/cli releases delete <id>` to delete a release." }) })
|
|
9357
9242
|
] })
|
|
9358
9243
|
] }),
|
|
9359
|
-
state.status === "error" && /* @__PURE__ */
|
|
9360
|
-
/* @__PURE__ */
|
|
9244
|
+
state.status === "error" && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
|
|
9245
|
+
/* @__PURE__ */ jsxs30(Text30, { color: "red", children: [
|
|
9361
9246
|
"\u2717 ",
|
|
9362
9247
|
state.message
|
|
9363
9248
|
] }),
|
|
9364
|
-
/* @__PURE__ */
|
|
9365
|
-
/* @__PURE__ */
|
|
9366
|
-
/* @__PURE__ */
|
|
9367
|
-
/* @__PURE__ */
|
|
9249
|
+
/* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: 1, children: [
|
|
9250
|
+
/* @__PURE__ */ jsx32(Text30, { dimColor: true, children: "Tips:" }),
|
|
9251
|
+
/* @__PURE__ */ jsx32(Text30, { color: "gray", children: " \u2022 Run `npx @intlpullhq/cli publish` to create a release first" }),
|
|
9252
|
+
/* @__PURE__ */ jsx32(Text30, { color: "gray", children: " \u2022 OTA requires Professional or Enterprise plan" })
|
|
9368
9253
|
] })
|
|
9369
9254
|
] })
|
|
9370
9255
|
] });
|
|
9371
9256
|
}
|
|
9372
9257
|
function runReleases(options) {
|
|
9373
|
-
|
|
9258
|
+
render15(/* @__PURE__ */ jsx32(ReleasesComponent, { options }));
|
|
9374
9259
|
}
|
|
9375
9260
|
|
|
9376
9261
|
// src/commands/workflow.tsx
|
|
9377
|
-
import { useState as
|
|
9378
|
-
import { render as
|
|
9379
|
-
import { Fragment as Fragment5, jsx as
|
|
9262
|
+
import { useState as useState20, useEffect as useEffect17 } from "react";
|
|
9263
|
+
import { render as render16, Box as Box30, Text as Text31 } from "ink";
|
|
9264
|
+
import { Fragment as Fragment5, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
9380
9265
|
function WorkflowStatusCommand({ projectId }) {
|
|
9381
|
-
const [loading, setLoading] =
|
|
9382
|
-
const [error, setError] =
|
|
9383
|
-
const [projectName, setProjectName] =
|
|
9384
|
-
const [workflowsEnabled, setWorkflowsEnabled] =
|
|
9385
|
-
const [workflows, setWorkflows] =
|
|
9386
|
-
const [pending, setPending] =
|
|
9387
|
-
const [locked, setLocked] =
|
|
9388
|
-
|
|
9266
|
+
const [loading, setLoading] = useState20(true);
|
|
9267
|
+
const [error, setError] = useState20(null);
|
|
9268
|
+
const [projectName, setProjectName] = useState20("");
|
|
9269
|
+
const [workflowsEnabled, setWorkflowsEnabled] = useState20(false);
|
|
9270
|
+
const [workflows, setWorkflows] = useState20([]);
|
|
9271
|
+
const [pending, setPending] = useState20([]);
|
|
9272
|
+
const [locked, setLocked] = useState20([]);
|
|
9273
|
+
useEffect17(() => {
|
|
9389
9274
|
async function fetchData() {
|
|
9390
9275
|
const resolved = getResolvedApiKey();
|
|
9391
9276
|
if (!resolved?.key) {
|
|
@@ -9425,36 +9310,36 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9425
9310
|
}
|
|
9426
9311
|
fetchData();
|
|
9427
9312
|
}, [projectId]);
|
|
9428
|
-
return /* @__PURE__ */
|
|
9429
|
-
/* @__PURE__ */
|
|
9430
|
-
loading && /* @__PURE__ */
|
|
9431
|
-
error && /* @__PURE__ */
|
|
9432
|
-
!loading && !error && !workflowsEnabled && /* @__PURE__ */
|
|
9313
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9314
|
+
/* @__PURE__ */ jsx33(Header, { compact: true }),
|
|
9315
|
+
loading && /* @__PURE__ */ jsx33(Box30, { marginY: 1, children: /* @__PURE__ */ jsx33(Spinner2, { label: "Fetching workflow status..." }) }),
|
|
9316
|
+
error && /* @__PURE__ */ jsx33(Alert, { type: "error", title: "Error", children: error }),
|
|
9317
|
+
!loading && !error && !workflowsEnabled && /* @__PURE__ */ jsxs31(Alert, { type: "warning", title: "Workflows Not Enabled", children: [
|
|
9433
9318
|
'Workflows are not enabled for project "',
|
|
9434
9319
|
projectName,
|
|
9435
9320
|
'".',
|
|
9436
9321
|
"\n",
|
|
9437
9322
|
"Enable them in the project settings at app.intlpull.com"
|
|
9438
9323
|
] }),
|
|
9439
|
-
!loading && !error && workflowsEnabled && /* @__PURE__ */
|
|
9440
|
-
/* @__PURE__ */
|
|
9441
|
-
|
|
9324
|
+
!loading && !error && workflowsEnabled && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9325
|
+
/* @__PURE__ */ jsx33(
|
|
9326
|
+
Box30,
|
|
9442
9327
|
{
|
|
9443
9328
|
borderStyle: "round",
|
|
9444
9329
|
borderColor: colors.primary,
|
|
9445
9330
|
paddingX: 2,
|
|
9446
9331
|
paddingY: 1,
|
|
9447
9332
|
marginY: 1,
|
|
9448
|
-
children: /* @__PURE__ */
|
|
9449
|
-
/* @__PURE__ */
|
|
9450
|
-
/* @__PURE__ */
|
|
9451
|
-
/* @__PURE__ */
|
|
9333
|
+
children: /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9334
|
+
/* @__PURE__ */ jsxs31(Box30, { children: [
|
|
9335
|
+
/* @__PURE__ */ jsx33(Text31, { bold: true, color: colors.text, children: projectName }),
|
|
9336
|
+
/* @__PURE__ */ jsxs31(Text31, { color: colors.success, children: [
|
|
9452
9337
|
" ",
|
|
9453
9338
|
icons.check,
|
|
9454
9339
|
" Workflows Enabled"
|
|
9455
9340
|
] })
|
|
9456
9341
|
] }),
|
|
9457
|
-
/* @__PURE__ */
|
|
9342
|
+
/* @__PURE__ */ jsx33(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9458
9343
|
icons.bullet,
|
|
9459
9344
|
" ",
|
|
9460
9345
|
workflows.length,
|
|
@@ -9462,14 +9347,14 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9462
9347
|
workflows.length !== 1 ? "s" : "",
|
|
9463
9348
|
" configured"
|
|
9464
9349
|
] }) }),
|
|
9465
|
-
/* @__PURE__ */
|
|
9350
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: pending.length > 0 ? colors.warning : colors.textMuted, children: [
|
|
9466
9351
|
icons.bullet,
|
|
9467
9352
|
" ",
|
|
9468
9353
|
pending.length,
|
|
9469
9354
|
" pending approval",
|
|
9470
9355
|
pending.length !== 1 ? "s" : ""
|
|
9471
9356
|
] }) }),
|
|
9472
|
-
/* @__PURE__ */
|
|
9357
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: locked.length > 0 ? colors.info : colors.textMuted, children: [
|
|
9473
9358
|
icons.bullet,
|
|
9474
9359
|
" ",
|
|
9475
9360
|
locked.length,
|
|
@@ -9479,15 +9364,15 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9479
9364
|
] })
|
|
9480
9365
|
}
|
|
9481
9366
|
),
|
|
9482
|
-
workflows.filter((w) => w.is_active).length > 0 && /* @__PURE__ */
|
|
9483
|
-
/* @__PURE__ */
|
|
9484
|
-
workflows.filter((w) => w.is_active).map((w) => /* @__PURE__ */
|
|
9485
|
-
/* @__PURE__ */
|
|
9486
|
-
/* @__PURE__ */
|
|
9367
|
+
workflows.filter((w) => w.is_active).length > 0 && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
|
|
9368
|
+
/* @__PURE__ */ jsx33(Text31, { bold: true, color: colors.text, children: "Active Workflow" }),
|
|
9369
|
+
workflows.filter((w) => w.is_active).map((w) => /* @__PURE__ */ jsxs31(Box30, { marginTop: 1, flexDirection: "column", children: [
|
|
9370
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.primary, children: w.name }),
|
|
9371
|
+
/* @__PURE__ */ jsx33(Box30, { marginLeft: 2, flexDirection: "column", children: w.stages.map((stage, i) => /* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9487
9372
|
i + 1,
|
|
9488
9373
|
". ",
|
|
9489
9374
|
stage.name,
|
|
9490
|
-
stage.required_role && /* @__PURE__ */
|
|
9375
|
+
stage.required_role && /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9491
9376
|
" (requires: ",
|
|
9492
9377
|
stage.required_role,
|
|
9493
9378
|
")"
|
|
@@ -9495,56 +9380,56 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9495
9380
|
] }) }, stage.id)) })
|
|
9496
9381
|
] }, w.id))
|
|
9497
9382
|
] }),
|
|
9498
|
-
pending.length > 0 && /* @__PURE__ */
|
|
9499
|
-
/* @__PURE__ */
|
|
9383
|
+
pending.length > 0 && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 2, children: [
|
|
9384
|
+
/* @__PURE__ */ jsxs31(Text31, { bold: true, color: colors.warning, children: [
|
|
9500
9385
|
"Pending Approvals (",
|
|
9501
9386
|
pending.length,
|
|
9502
9387
|
")"
|
|
9503
9388
|
] }),
|
|
9504
|
-
pending.slice(0, 10).map((p) => /* @__PURE__ */
|
|
9505
|
-
/* @__PURE__ */
|
|
9506
|
-
/* @__PURE__ */
|
|
9507
|
-
/* @__PURE__ */
|
|
9389
|
+
pending.slice(0, 10).map((p) => /* @__PURE__ */ jsxs31(Box30, { marginTop: 1, flexDirection: "column", children: [
|
|
9390
|
+
/* @__PURE__ */ jsxs31(Box30, { children: [
|
|
9391
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.text, children: p.key }),
|
|
9392
|
+
/* @__PURE__ */ jsxs31(Text31, { color: colors.textDim, children: [
|
|
9508
9393
|
" [",
|
|
9509
9394
|
p.language,
|
|
9510
9395
|
"]"
|
|
9511
9396
|
] })
|
|
9512
9397
|
] }),
|
|
9513
|
-
/* @__PURE__ */
|
|
9398
|
+
/* @__PURE__ */ jsx33(Box30, { marginLeft: 2, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9514
9399
|
"Stage: ",
|
|
9515
9400
|
p.stage_name,
|
|
9516
9401
|
p.required_role && ` (requires: ${p.required_role})`
|
|
9517
9402
|
] }) }),
|
|
9518
|
-
/* @__PURE__ */
|
|
9403
|
+
/* @__PURE__ */ jsx33(Box30, { marginLeft: 2, children: /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9519
9404
|
"ID: ",
|
|
9520
9405
|
p.translation_id
|
|
9521
9406
|
] }) })
|
|
9522
9407
|
] }, p.id)),
|
|
9523
|
-
pending.length > 10 && /* @__PURE__ */
|
|
9408
|
+
pending.length > 10 && /* @__PURE__ */ jsx33(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9524
9409
|
"... and ",
|
|
9525
9410
|
pending.length - 10,
|
|
9526
9411
|
" more"
|
|
9527
9412
|
] }) })
|
|
9528
9413
|
] }),
|
|
9529
|
-
pending.length === 0 && /* @__PURE__ */
|
|
9414
|
+
pending.length === 0 && /* @__PURE__ */ jsx33(Box30, { marginTop: 2, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.success, children: [
|
|
9530
9415
|
icons.check,
|
|
9531
9416
|
" No pending approvals"
|
|
9532
9417
|
] }) }),
|
|
9533
|
-
/* @__PURE__ */
|
|
9418
|
+
/* @__PURE__ */ jsx33(Box30, { marginTop: 2, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9534
9419
|
icons.info,
|
|
9535
9420
|
" Use",
|
|
9536
9421
|
" ",
|
|
9537
|
-
/* @__PURE__ */
|
|
9422
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.primary, children: "npx @intlpullhq/cli workflow approve <id>" }),
|
|
9538
9423
|
" to approve"
|
|
9539
9424
|
] }) })
|
|
9540
9425
|
] })
|
|
9541
9426
|
] });
|
|
9542
9427
|
}
|
|
9543
9428
|
function WorkflowPendingCommand({ projectId }) {
|
|
9544
|
-
const [loading, setLoading] =
|
|
9545
|
-
const [error, setError] =
|
|
9546
|
-
const [pending, setPending] =
|
|
9547
|
-
|
|
9429
|
+
const [loading, setLoading] = useState20(true);
|
|
9430
|
+
const [error, setError] = useState20(null);
|
|
9431
|
+
const [pending, setPending] = useState20([]);
|
|
9432
|
+
useEffect17(() => {
|
|
9548
9433
|
async function fetchData() {
|
|
9549
9434
|
const resolved = getResolvedApiKey();
|
|
9550
9435
|
if (!resolved?.key) {
|
|
@@ -9571,19 +9456,19 @@ function WorkflowPendingCommand({ projectId }) {
|
|
|
9571
9456
|
}
|
|
9572
9457
|
fetchData();
|
|
9573
9458
|
}, [projectId]);
|
|
9574
|
-
return /* @__PURE__ */
|
|
9575
|
-
/* @__PURE__ */
|
|
9576
|
-
loading && /* @__PURE__ */
|
|
9577
|
-
error && /* @__PURE__ */
|
|
9578
|
-
!loading && !error && pending.length === 0 && /* @__PURE__ */
|
|
9579
|
-
!loading && !error && pending.length > 0 && /* @__PURE__ */
|
|
9580
|
-
/* @__PURE__ */
|
|
9459
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9460
|
+
/* @__PURE__ */ jsx33(Header, { compact: true }),
|
|
9461
|
+
loading && /* @__PURE__ */ jsx33(Box30, { marginY: 1, children: /* @__PURE__ */ jsx33(Spinner2, { label: "Fetching pending approvals..." }) }),
|
|
9462
|
+
error && /* @__PURE__ */ jsx33(Alert, { type: "error", title: "Error", children: error }),
|
|
9463
|
+
!loading && !error && pending.length === 0 && /* @__PURE__ */ jsx33(Alert, { type: "success", title: "All Clear", children: "No pending approvals" }),
|
|
9464
|
+
!loading && !error && pending.length > 0 && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9465
|
+
/* @__PURE__ */ jsx33(Box30, { marginY: 1, children: /* @__PURE__ */ jsxs31(Text31, { bold: true, color: colors.warning, children: [
|
|
9581
9466
|
pending.length,
|
|
9582
9467
|
" Pending Approval",
|
|
9583
9468
|
pending.length !== 1 ? "s" : ""
|
|
9584
9469
|
] }) }),
|
|
9585
|
-
pending.map((p) => /* @__PURE__ */
|
|
9586
|
-
|
|
9470
|
+
pending.map((p) => /* @__PURE__ */ jsxs31(
|
|
9471
|
+
Box30,
|
|
9587
9472
|
{
|
|
9588
9473
|
borderStyle: "single",
|
|
9589
9474
|
borderColor: colors.textDim,
|
|
@@ -9592,33 +9477,33 @@ function WorkflowPendingCommand({ projectId }) {
|
|
|
9592
9477
|
marginBottom: 1,
|
|
9593
9478
|
flexDirection: "column",
|
|
9594
9479
|
children: [
|
|
9595
|
-
/* @__PURE__ */
|
|
9596
|
-
/* @__PURE__ */
|
|
9480
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsx33(Text31, { bold: true, color: colors.text, children: p.key }) }),
|
|
9481
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9597
9482
|
"Language: ",
|
|
9598
|
-
/* @__PURE__ */
|
|
9483
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.info, children: p.language }),
|
|
9599
9484
|
" | ",
|
|
9600
9485
|
"Namespace: ",
|
|
9601
|
-
/* @__PURE__ */
|
|
9486
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.info, children: p.namespace })
|
|
9602
9487
|
] }) }),
|
|
9603
|
-
/* @__PURE__ */
|
|
9488
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9604
9489
|
"Stage: ",
|
|
9605
|
-
/* @__PURE__ */
|
|
9606
|
-
p.required_role && /* @__PURE__ */
|
|
9490
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.warning, children: p.stage_name }),
|
|
9491
|
+
p.required_role && /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9607
9492
|
" (requires: ",
|
|
9608
9493
|
p.required_role,
|
|
9609
9494
|
")"
|
|
9610
9495
|
] })
|
|
9611
9496
|
] }) }),
|
|
9612
|
-
/* @__PURE__ */
|
|
9497
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9613
9498
|
"Value: ",
|
|
9614
|
-
/* @__PURE__ */
|
|
9499
|
+
/* @__PURE__ */ jsxs31(Text31, { color: colors.text, children: [
|
|
9615
9500
|
'"',
|
|
9616
9501
|
p.value.substring(0, 50),
|
|
9617
9502
|
p.value.length > 50 ? "..." : "",
|
|
9618
9503
|
'"'
|
|
9619
9504
|
] })
|
|
9620
9505
|
] }) }),
|
|
9621
|
-
/* @__PURE__ */
|
|
9506
|
+
/* @__PURE__ */ jsx33(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9622
9507
|
"ID: ",
|
|
9623
9508
|
p.translation_id
|
|
9624
9509
|
] }) })
|
|
@@ -9649,11 +9534,11 @@ The translation is locked and cannot be modified.`;
|
|
|
9649
9534
|
return errorMessage;
|
|
9650
9535
|
}
|
|
9651
9536
|
function ApproveCommand({ projectId, translationId, comment }) {
|
|
9652
|
-
const [loading, setLoading] =
|
|
9653
|
-
const [error, setError] =
|
|
9654
|
-
const [success, setSuccess] =
|
|
9655
|
-
const [stageName, setStageName] =
|
|
9656
|
-
|
|
9537
|
+
const [loading, setLoading] = useState20(true);
|
|
9538
|
+
const [error, setError] = useState20(null);
|
|
9539
|
+
const [success, setSuccess] = useState20(false);
|
|
9540
|
+
const [stageName, setStageName] = useState20("");
|
|
9541
|
+
useEffect17(() => {
|
|
9657
9542
|
async function approve() {
|
|
9658
9543
|
const resolved = getResolvedApiKey();
|
|
9659
9544
|
if (!resolved?.key) {
|
|
@@ -9682,15 +9567,15 @@ function ApproveCommand({ projectId, translationId, comment }) {
|
|
|
9682
9567
|
}
|
|
9683
9568
|
approve();
|
|
9684
9569
|
}, [projectId, translationId, comment]);
|
|
9685
|
-
return /* @__PURE__ */
|
|
9686
|
-
/* @__PURE__ */
|
|
9687
|
-
loading && /* @__PURE__ */
|
|
9688
|
-
error && /* @__PURE__ */
|
|
9689
|
-
success && /* @__PURE__ */
|
|
9570
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9571
|
+
/* @__PURE__ */ jsx33(Header, { compact: true }),
|
|
9572
|
+
loading && /* @__PURE__ */ jsx33(Box30, { marginY: 1, children: /* @__PURE__ */ jsx33(Spinner2, { label: "Approving translation..." }) }),
|
|
9573
|
+
error && /* @__PURE__ */ jsx33(Alert, { type: "error", title: "Approval Failed", children: error }),
|
|
9574
|
+
success && /* @__PURE__ */ jsxs31(Alert, { type: "success", title: "Approved", children: [
|
|
9690
9575
|
'Translation approved at stage "',
|
|
9691
9576
|
stageName,
|
|
9692
9577
|
'"',
|
|
9693
|
-
comment && /* @__PURE__ */
|
|
9578
|
+
comment && /* @__PURE__ */ jsxs31(Fragment5, { children: [
|
|
9694
9579
|
"\n",
|
|
9695
9580
|
"Comment: ",
|
|
9696
9581
|
comment
|
|
@@ -9699,10 +9584,10 @@ function ApproveCommand({ projectId, translationId, comment }) {
|
|
|
9699
9584
|
] });
|
|
9700
9585
|
}
|
|
9701
9586
|
function RejectCommand({ projectId, translationId, reason }) {
|
|
9702
|
-
const [loading, setLoading] =
|
|
9703
|
-
const [error, setError] =
|
|
9704
|
-
const [success, setSuccess] =
|
|
9705
|
-
|
|
9587
|
+
const [loading, setLoading] = useState20(true);
|
|
9588
|
+
const [error, setError] = useState20(null);
|
|
9589
|
+
const [success, setSuccess] = useState20(false);
|
|
9590
|
+
useEffect17(() => {
|
|
9706
9591
|
async function reject() {
|
|
9707
9592
|
const resolved = getResolvedApiKey();
|
|
9708
9593
|
if (!resolved?.key) {
|
|
@@ -9730,11 +9615,11 @@ function RejectCommand({ projectId, translationId, reason }) {
|
|
|
9730
9615
|
}
|
|
9731
9616
|
reject();
|
|
9732
9617
|
}, [projectId, translationId, reason]);
|
|
9733
|
-
return /* @__PURE__ */
|
|
9734
|
-
/* @__PURE__ */
|
|
9735
|
-
loading && /* @__PURE__ */
|
|
9736
|
-
error && /* @__PURE__ */
|
|
9737
|
-
success && /* @__PURE__ */
|
|
9618
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9619
|
+
/* @__PURE__ */ jsx33(Header, { compact: true }),
|
|
9620
|
+
loading && /* @__PURE__ */ jsx33(Box30, { marginY: 1, children: /* @__PURE__ */ jsx33(Spinner2, { label: "Rejecting translation..." }) }),
|
|
9621
|
+
error && /* @__PURE__ */ jsx33(Alert, { type: "error", title: "Rejection Failed", children: error }),
|
|
9622
|
+
success && /* @__PURE__ */ jsxs31(Alert, { type: "warning", title: "Rejected", children: [
|
|
9738
9623
|
'Translation rejected with reason: "',
|
|
9739
9624
|
reason,
|
|
9740
9625
|
'"'
|
|
@@ -9742,16 +9627,16 @@ function RejectCommand({ projectId, translationId, reason }) {
|
|
|
9742
9627
|
] });
|
|
9743
9628
|
}
|
|
9744
9629
|
function runWorkflowStatus(options) {
|
|
9745
|
-
|
|
9630
|
+
render16(/* @__PURE__ */ jsx33(WorkflowStatusCommand, { projectId: options.project }));
|
|
9746
9631
|
}
|
|
9747
9632
|
function runWorkflowPending(options) {
|
|
9748
|
-
|
|
9633
|
+
render16(/* @__PURE__ */ jsx33(WorkflowPendingCommand, { projectId: options.project }));
|
|
9749
9634
|
}
|
|
9750
9635
|
function runWorkflowApprove(translationId, options) {
|
|
9751
|
-
|
|
9636
|
+
render16(/* @__PURE__ */ jsx33(ApproveCommand, { projectId: options.project, translationId, comment: options.comment }));
|
|
9752
9637
|
}
|
|
9753
9638
|
function runWorkflowReject(translationId, options) {
|
|
9754
|
-
|
|
9639
|
+
render16(/* @__PURE__ */ jsx33(RejectCommand, { projectId: options.project, translationId, reason: options.reason }));
|
|
9755
9640
|
}
|
|
9756
9641
|
|
|
9757
9642
|
// src/commands/emails.tsx
|
|
@@ -10010,9 +9895,9 @@ async function runEmailsStatus(options) {
|
|
|
10010
9895
|
}
|
|
10011
9896
|
|
|
10012
9897
|
// src/commands/zendesk.tsx
|
|
10013
|
-
import { useState as
|
|
10014
|
-
import { render as
|
|
10015
|
-
import { Fragment as Fragment6, jsx as
|
|
9898
|
+
import { useState as useState21, useEffect as useEffect18 } from "react";
|
|
9899
|
+
import { render as render17, Box as Box31, Text as Text32 } from "ink";
|
|
9900
|
+
import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
10016
9901
|
async function fetchZendeskIntegration(apiUrl, apiKey, projectId) {
|
|
10017
9902
|
const response = await fetch(`${apiUrl}/api/v1/projects/${projectId}/integrations/zendesk`, {
|
|
10018
9903
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -10083,11 +9968,11 @@ async function disconnectZendesk(apiUrl, apiKey, projectId) {
|
|
|
10083
9968
|
if (!response.ok) throw new Error(`Failed to disconnect: ${response.status}`);
|
|
10084
9969
|
}
|
|
10085
9970
|
function ZendeskStatusCommand() {
|
|
10086
|
-
const [loading, setLoading] =
|
|
10087
|
-
const [integration, setIntegration] =
|
|
10088
|
-
const [status, setStatus] =
|
|
10089
|
-
const [error, setError] =
|
|
10090
|
-
|
|
9971
|
+
const [loading, setLoading] = useState21(true);
|
|
9972
|
+
const [integration, setIntegration] = useState21(null);
|
|
9973
|
+
const [status, setStatus] = useState21(null);
|
|
9974
|
+
const [error, setError] = useState21(null);
|
|
9975
|
+
useEffect18(() => {
|
|
10091
9976
|
async function fetch2() {
|
|
10092
9977
|
const resolved = getResolvedApiKey();
|
|
10093
9978
|
if (!resolved?.key) {
|
|
@@ -10119,54 +10004,54 @@ function ZendeskStatusCommand() {
|
|
|
10119
10004
|
}
|
|
10120
10005
|
fetch2();
|
|
10121
10006
|
}, []);
|
|
10122
|
-
return /* @__PURE__ */
|
|
10123
|
-
/* @__PURE__ */
|
|
10124
|
-
loading && /* @__PURE__ */
|
|
10125
|
-
error && /* @__PURE__ */
|
|
10126
|
-
integration && !integration.connected && /* @__PURE__ */
|
|
10127
|
-
integration?.connected && integration.integration && /* @__PURE__ */
|
|
10128
|
-
/* @__PURE__ */
|
|
10129
|
-
/* @__PURE__ */
|
|
10007
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
10008
|
+
/* @__PURE__ */ jsx34(Header, { compact: true }),
|
|
10009
|
+
loading && /* @__PURE__ */ jsx34(Spinner2, { label: "Fetching Zendesk status..." }),
|
|
10010
|
+
error && /* @__PURE__ */ jsx34(Alert, { type: "error", title: "Error", children: error }),
|
|
10011
|
+
integration && !integration.connected && /* @__PURE__ */ jsx34(Alert, { type: "warning", title: "Not Connected", children: "Zendesk is not connected. Run: npx @intlpullhq/cli zendesk connect --subdomain YOUR_SUBDOMAIN --email YOUR_EMAIL --token YOUR_TOKEN" }),
|
|
10012
|
+
integration?.connected && integration.integration && /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginY: 1, children: [
|
|
10013
|
+
/* @__PURE__ */ jsx34(Box31, { borderStyle: "round", borderColor: colors.success, paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
10014
|
+
/* @__PURE__ */ jsxs32(Text32, { bold: true, color: colors.success, children: [
|
|
10130
10015
|
icons.success,
|
|
10131
10016
|
" Connected to Zendesk"
|
|
10132
10017
|
] }),
|
|
10133
|
-
/* @__PURE__ */
|
|
10018
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10134
10019
|
"Subdomain: ",
|
|
10135
10020
|
integration.integration.subdomain,
|
|
10136
10021
|
".zendesk.com"
|
|
10137
10022
|
] }),
|
|
10138
|
-
/* @__PURE__ */
|
|
10023
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10139
10024
|
"Source Locale: ",
|
|
10140
10025
|
integration.integration.source_locale
|
|
10141
10026
|
] }),
|
|
10142
|
-
integration.integration.last_sync_at && /* @__PURE__ */
|
|
10027
|
+
integration.integration.last_sync_at && /* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10143
10028
|
"Last Sync: ",
|
|
10144
10029
|
new Date(integration.integration.last_sync_at).toLocaleString()
|
|
10145
10030
|
] })
|
|
10146
10031
|
] }) }),
|
|
10147
|
-
status && /* @__PURE__ */
|
|
10148
|
-
/* @__PURE__ */
|
|
10149
|
-
/* @__PURE__ */
|
|
10032
|
+
status && /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: 1, children: [
|
|
10033
|
+
/* @__PURE__ */ jsx34(Text32, { bold: true, children: "Content Summary" }),
|
|
10034
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10150
10035
|
icons.bullet,
|
|
10151
10036
|
" ",
|
|
10152
10037
|
status.total_articles,
|
|
10153
10038
|
" articles"
|
|
10154
10039
|
] }),
|
|
10155
|
-
/* @__PURE__ */
|
|
10040
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10156
10041
|
icons.bullet,
|
|
10157
10042
|
" ",
|
|
10158
10043
|
status.total_categories,
|
|
10159
10044
|
" categories"
|
|
10160
10045
|
] }),
|
|
10161
|
-
/* @__PURE__ */
|
|
10046
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10162
10047
|
icons.bullet,
|
|
10163
10048
|
" ",
|
|
10164
10049
|
status.total_sections,
|
|
10165
10050
|
" sections"
|
|
10166
10051
|
] }),
|
|
10167
|
-
status.target_locales.length > 0 && /* @__PURE__ */
|
|
10168
|
-
/* @__PURE__ */
|
|
10169
|
-
status.target_locales.map((locale) => /* @__PURE__ */
|
|
10052
|
+
status.target_locales.length > 0 && /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: 1, children: [
|
|
10053
|
+
/* @__PURE__ */ jsx34(Text32, { bold: true, children: "Translation Progress" }),
|
|
10054
|
+
status.target_locales.map((locale) => /* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10170
10055
|
icons.bullet,
|
|
10171
10056
|
" ",
|
|
10172
10057
|
locale,
|
|
@@ -10182,10 +10067,10 @@ function ZendeskStatusCommand() {
|
|
|
10182
10067
|
] });
|
|
10183
10068
|
}
|
|
10184
10069
|
function ZendeskConnectCommand({ subdomain, email, token }) {
|
|
10185
|
-
const [loading, setLoading] =
|
|
10186
|
-
const [result, setResult] =
|
|
10187
|
-
const [error, setError] =
|
|
10188
|
-
|
|
10070
|
+
const [loading, setLoading] = useState21(true);
|
|
10071
|
+
const [result, setResult] = useState21(null);
|
|
10072
|
+
const [error, setError] = useState21(null);
|
|
10073
|
+
useEffect18(() => {
|
|
10189
10074
|
async function connect() {
|
|
10190
10075
|
const resolved = getResolvedApiKey();
|
|
10191
10076
|
if (!resolved?.key) {
|
|
@@ -10213,11 +10098,11 @@ function ZendeskConnectCommand({ subdomain, email, token }) {
|
|
|
10213
10098
|
}
|
|
10214
10099
|
connect();
|
|
10215
10100
|
}, [subdomain, email, token]);
|
|
10216
|
-
return /* @__PURE__ */
|
|
10217
|
-
/* @__PURE__ */
|
|
10218
|
-
loading && /* @__PURE__ */
|
|
10219
|
-
error && /* @__PURE__ */
|
|
10220
|
-
result?.connected && /* @__PURE__ */
|
|
10101
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
10102
|
+
/* @__PURE__ */ jsx34(Header, { compact: true }),
|
|
10103
|
+
loading && /* @__PURE__ */ jsx34(Spinner2, { label: "Connecting to Zendesk..." }),
|
|
10104
|
+
error && /* @__PURE__ */ jsx34(Alert, { type: "error", title: "Connection Failed", children: error }),
|
|
10105
|
+
result?.connected && /* @__PURE__ */ jsxs32(Alert, { type: "success", title: "Connected!", children: [
|
|
10221
10106
|
"Successfully connected to ",
|
|
10222
10107
|
subdomain,
|
|
10223
10108
|
".zendesk.com",
|
|
@@ -10227,10 +10112,10 @@ function ZendeskConnectCommand({ subdomain, email, token }) {
|
|
|
10227
10112
|
] });
|
|
10228
10113
|
}
|
|
10229
10114
|
function ZendeskSyncCommand({ direction, locale, excludeDrafts, autoTranslate, translateProvider }) {
|
|
10230
|
-
const [loading, setLoading] =
|
|
10231
|
-
const [result, setResult] =
|
|
10232
|
-
const [error, setError] =
|
|
10233
|
-
|
|
10115
|
+
const [loading, setLoading] = useState21(true);
|
|
10116
|
+
const [result, setResult] = useState21(null);
|
|
10117
|
+
const [error, setError] = useState21(null);
|
|
10118
|
+
useEffect18(() => {
|
|
10234
10119
|
async function sync() {
|
|
10235
10120
|
const resolved = getResolvedApiKey();
|
|
10236
10121
|
if (!resolved?.key) {
|
|
@@ -10272,11 +10157,11 @@ function ZendeskSyncCommand({ direction, locale, excludeDrafts, autoTranslate, t
|
|
|
10272
10157
|
}
|
|
10273
10158
|
sync();
|
|
10274
10159
|
}, [direction, locale, excludeDrafts, autoTranslate, translateProvider]);
|
|
10275
|
-
return /* @__PURE__ */
|
|
10276
|
-
/* @__PURE__ */
|
|
10277
|
-
loading && /* @__PURE__ */
|
|
10278
|
-
error && /* @__PURE__ */
|
|
10279
|
-
result?.success && /* @__PURE__ */
|
|
10160
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
10161
|
+
/* @__PURE__ */ jsx34(Header, { compact: true }),
|
|
10162
|
+
loading && /* @__PURE__ */ jsx34(Spinner2, { label: `${direction === "pull" ? "Pulling from" : "Pushing to"} Zendesk...${autoTranslate ? " (with auto-translation)" : ""}` }),
|
|
10163
|
+
error && /* @__PURE__ */ jsx34(Alert, { type: "error", title: "Sync Failed", children: error }),
|
|
10164
|
+
result?.success && /* @__PURE__ */ jsx34(Alert, { type: "success", title: "Sync Complete", children: direction === "pull" ? /* @__PURE__ */ jsxs32(Fragment6, { children: [
|
|
10280
10165
|
"Imported ",
|
|
10281
10166
|
result.articles_synced,
|
|
10282
10167
|
" articles, ",
|
|
@@ -10289,24 +10174,24 @@ function ZendeskSyncCommand({ direction, locale, excludeDrafts, autoTranslate, t
|
|
|
10289
10174
|
result.keys_created,
|
|
10290
10175
|
", updated: ",
|
|
10291
10176
|
result.keys_updated,
|
|
10292
|
-
result.auto_translate_job && /* @__PURE__ */
|
|
10177
|
+
result.auto_translate_job && /* @__PURE__ */ jsxs32(Fragment6, { children: [
|
|
10293
10178
|
"\n",
|
|
10294
10179
|
"Auto-translation started: ",
|
|
10295
10180
|
result.auto_translate_job
|
|
10296
10181
|
] })
|
|
10297
|
-
] }) : /* @__PURE__ */
|
|
10182
|
+
] }) : /* @__PURE__ */ jsxs32(Fragment6, { children: [
|
|
10298
10183
|
"Pushed translations for ",
|
|
10299
10184
|
result.articles_synced,
|
|
10300
10185
|
" articles to Zendesk"
|
|
10301
10186
|
] }) }),
|
|
10302
|
-
result && !result.success && /* @__PURE__ */
|
|
10187
|
+
result && !result.success && /* @__PURE__ */ jsx34(Alert, { type: "error", title: "Sync Failed", children: result.error || "Unknown error" })
|
|
10303
10188
|
] });
|
|
10304
10189
|
}
|
|
10305
10190
|
function ZendeskDisconnectCommand() {
|
|
10306
|
-
const [loading, setLoading] =
|
|
10307
|
-
const [success, setSuccess] =
|
|
10308
|
-
const [error, setError] =
|
|
10309
|
-
|
|
10191
|
+
const [loading, setLoading] = useState21(true);
|
|
10192
|
+
const [success, setSuccess] = useState21(false);
|
|
10193
|
+
const [error, setError] = useState21(null);
|
|
10194
|
+
useEffect18(() => {
|
|
10310
10195
|
async function disconnect() {
|
|
10311
10196
|
const resolved = getResolvedApiKey();
|
|
10312
10197
|
if (!resolved?.key) {
|
|
@@ -10334,22 +10219,22 @@ function ZendeskDisconnectCommand() {
|
|
|
10334
10219
|
}
|
|
10335
10220
|
disconnect();
|
|
10336
10221
|
}, []);
|
|
10337
|
-
return /* @__PURE__ */
|
|
10338
|
-
/* @__PURE__ */
|
|
10339
|
-
loading && /* @__PURE__ */
|
|
10340
|
-
error && /* @__PURE__ */
|
|
10341
|
-
success && /* @__PURE__ */
|
|
10222
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
10223
|
+
/* @__PURE__ */ jsx34(Header, { compact: true }),
|
|
10224
|
+
loading && /* @__PURE__ */ jsx34(Spinner2, { label: "Disconnecting..." }),
|
|
10225
|
+
error && /* @__PURE__ */ jsx34(Alert, { type: "error", title: "Error", children: error }),
|
|
10226
|
+
success && /* @__PURE__ */ jsx34(Alert, { type: "success", title: "Disconnected", children: "Zendesk integration has been removed." })
|
|
10342
10227
|
] });
|
|
10343
10228
|
}
|
|
10344
10229
|
function runZendeskStatus() {
|
|
10345
|
-
|
|
10230
|
+
render17(/* @__PURE__ */ jsx34(ZendeskStatusCommand, {}));
|
|
10346
10231
|
}
|
|
10347
10232
|
function runZendeskConnect(options) {
|
|
10348
10233
|
if (!options.subdomain || !options.email || !options.token) {
|
|
10349
|
-
|
|
10350
|
-
/* @__PURE__ */
|
|
10351
|
-
/* @__PURE__ */
|
|
10352
|
-
/* @__PURE__ */
|
|
10234
|
+
render17(
|
|
10235
|
+
/* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
10236
|
+
/* @__PURE__ */ jsx34(Header, { compact: true }),
|
|
10237
|
+
/* @__PURE__ */ jsxs32(Alert, { type: "error", title: "Missing Options", children: [
|
|
10353
10238
|
"Required: --subdomain, --email, --token",
|
|
10354
10239
|
"\n",
|
|
10355
10240
|
"Example: npx @intlpullhq/cli zendesk connect --subdomain mycompany --email admin@example.com --token YOUR_API_TOKEN"
|
|
@@ -10358,12 +10243,12 @@ function runZendeskConnect(options) {
|
|
|
10358
10243
|
);
|
|
10359
10244
|
return;
|
|
10360
10245
|
}
|
|
10361
|
-
|
|
10246
|
+
render17(/* @__PURE__ */ jsx34(ZendeskConnectCommand, { subdomain: options.subdomain, email: options.email, token: options.token }));
|
|
10362
10247
|
}
|
|
10363
10248
|
function runZendeskSync(options) {
|
|
10364
10249
|
const direction = options.direction || "pull";
|
|
10365
|
-
|
|
10366
|
-
/* @__PURE__ */
|
|
10250
|
+
render17(
|
|
10251
|
+
/* @__PURE__ */ jsx34(
|
|
10367
10252
|
ZendeskSyncCommand,
|
|
10368
10253
|
{
|
|
10369
10254
|
direction,
|
|
@@ -10376,17 +10261,17 @@ function runZendeskSync(options) {
|
|
|
10376
10261
|
);
|
|
10377
10262
|
}
|
|
10378
10263
|
function runZendeskDisconnect() {
|
|
10379
|
-
|
|
10264
|
+
render17(/* @__PURE__ */ jsx34(ZendeskDisconnectCommand, {}));
|
|
10380
10265
|
}
|
|
10381
10266
|
|
|
10382
10267
|
// src/commands/documents/index.tsx
|
|
10383
10268
|
import { Command } from "commander";
|
|
10384
10269
|
|
|
10385
10270
|
// src/commands/documents/list.tsx
|
|
10386
|
-
import { useState as
|
|
10387
|
-
import { render as
|
|
10388
|
-
import
|
|
10389
|
-
import { jsx as
|
|
10271
|
+
import { useState as useState22, useEffect as useEffect19 } from "react";
|
|
10272
|
+
import { render as render18, Box as Box32, Text as Text33, Newline } from "ink";
|
|
10273
|
+
import Spinner15 from "ink-spinner";
|
|
10274
|
+
import { jsx as jsx35, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
10390
10275
|
function SimpleTable({ data }) {
|
|
10391
10276
|
if (data.length === 0) return null;
|
|
10392
10277
|
const headers = Object.keys(data[0]);
|
|
@@ -10394,27 +10279,27 @@ function SimpleTable({ data }) {
|
|
|
10394
10279
|
(h) => Math.max(h.length, ...data.map((row) => String(row[h] || "").length))
|
|
10395
10280
|
);
|
|
10396
10281
|
const separator = "\u2500".repeat(colWidths.reduce((a, b) => a + b + 3, 1));
|
|
10397
|
-
return /* @__PURE__ */
|
|
10398
|
-
/* @__PURE__ */
|
|
10399
|
-
/* @__PURE__ */
|
|
10282
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
10283
|
+
/* @__PURE__ */ jsx35(Text33, { children: separator }),
|
|
10284
|
+
/* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10400
10285
|
"\u2502 ",
|
|
10401
10286
|
headers.map((h, i) => h.padEnd(colWidths[i])).join(" \u2502 "),
|
|
10402
10287
|
" \u2502"
|
|
10403
10288
|
] }),
|
|
10404
|
-
/* @__PURE__ */
|
|
10405
|
-
data.map((row, rowIdx) => /* @__PURE__ */
|
|
10289
|
+
/* @__PURE__ */ jsx35(Text33, { children: separator }),
|
|
10290
|
+
data.map((row, rowIdx) => /* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10406
10291
|
"\u2502 ",
|
|
10407
10292
|
headers.map((h, i) => String(row[h] || "").padEnd(colWidths[i])).join(" \u2502 "),
|
|
10408
10293
|
" \u2502"
|
|
10409
10294
|
] }, rowIdx)),
|
|
10410
|
-
/* @__PURE__ */
|
|
10295
|
+
/* @__PURE__ */ jsx35(Text33, { children: separator })
|
|
10411
10296
|
] });
|
|
10412
10297
|
}
|
|
10413
10298
|
var DocumentsList = ({ project }) => {
|
|
10414
|
-
const [documents, setDocuments] =
|
|
10415
|
-
const [isLoading, setIsLoading] =
|
|
10416
|
-
const [error, setError] =
|
|
10417
|
-
|
|
10299
|
+
const [documents, setDocuments] = useState22([]);
|
|
10300
|
+
const [isLoading, setIsLoading] = useState22(true);
|
|
10301
|
+
const [error, setError] = useState22(null);
|
|
10302
|
+
useEffect19(() => {
|
|
10418
10303
|
const fetchDocuments = async () => {
|
|
10419
10304
|
try {
|
|
10420
10305
|
const config = getProjectConfig();
|
|
@@ -10436,19 +10321,19 @@ var DocumentsList = ({ project }) => {
|
|
|
10436
10321
|
fetchDocuments();
|
|
10437
10322
|
}, [project]);
|
|
10438
10323
|
if (error) {
|
|
10439
|
-
return /* @__PURE__ */
|
|
10324
|
+
return /* @__PURE__ */ jsxs33(Text33, { color: "red", children: [
|
|
10440
10325
|
"Error: ",
|
|
10441
10326
|
error
|
|
10442
10327
|
] });
|
|
10443
10328
|
}
|
|
10444
10329
|
if (isLoading) {
|
|
10445
|
-
return /* @__PURE__ */
|
|
10446
|
-
/* @__PURE__ */
|
|
10330
|
+
return /* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10331
|
+
/* @__PURE__ */ jsx35(Text33, { color: "green", children: /* @__PURE__ */ jsx35(Spinner15, { type: "dots" }) }),
|
|
10447
10332
|
" Loading documents..."
|
|
10448
10333
|
] });
|
|
10449
10334
|
}
|
|
10450
10335
|
if (documents.length === 0) {
|
|
10451
|
-
return /* @__PURE__ */
|
|
10336
|
+
return /* @__PURE__ */ jsx35(Text33, { children: "No documents found." });
|
|
10452
10337
|
}
|
|
10453
10338
|
const data = documents.map((doc) => ({
|
|
10454
10339
|
ID: doc.id.substring(0, 8) + "...",
|
|
@@ -10456,30 +10341,30 @@ var DocumentsList = ({ project }) => {
|
|
|
10456
10341
|
Status: doc.status,
|
|
10457
10342
|
Created: new Date(doc.created_at).toLocaleDateString()
|
|
10458
10343
|
}));
|
|
10459
|
-
return /* @__PURE__ */
|
|
10460
|
-
/* @__PURE__ */
|
|
10344
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
10345
|
+
/* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10461
10346
|
"Target Project: ",
|
|
10462
10347
|
project || "Current"
|
|
10463
10348
|
] }),
|
|
10464
|
-
/* @__PURE__ */
|
|
10465
|
-
/* @__PURE__ */
|
|
10349
|
+
/* @__PURE__ */ jsx35(Newline, {}),
|
|
10350
|
+
/* @__PURE__ */ jsx35(SimpleTable, { data })
|
|
10466
10351
|
] });
|
|
10467
10352
|
};
|
|
10468
10353
|
function runDocumentsList(options) {
|
|
10469
|
-
|
|
10354
|
+
render18(/* @__PURE__ */ jsx35(DocumentsList, { ...options }));
|
|
10470
10355
|
}
|
|
10471
10356
|
|
|
10472
10357
|
// src/commands/documents/upload.tsx
|
|
10473
|
-
import { useState as
|
|
10474
|
-
import { render as
|
|
10475
|
-
import
|
|
10358
|
+
import { useState as useState23, useEffect as useEffect20 } from "react";
|
|
10359
|
+
import { render as render19, Text as Text34 } from "ink";
|
|
10360
|
+
import Spinner16 from "ink-spinner";
|
|
10476
10361
|
import fs2 from "fs";
|
|
10477
10362
|
import path2 from "path";
|
|
10478
|
-
import { jsx as
|
|
10363
|
+
import { jsx as jsx36, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
10479
10364
|
var DocumentsUpload = ({ file, project, source, target }) => {
|
|
10480
|
-
const [status, setStatus] =
|
|
10481
|
-
const [message, setMessage] =
|
|
10482
|
-
|
|
10365
|
+
const [status, setStatus] = useState23("uploading");
|
|
10366
|
+
const [message, setMessage] = useState23("Uploading document...");
|
|
10367
|
+
useEffect20(() => {
|
|
10483
10368
|
const upload = async () => {
|
|
10484
10369
|
try {
|
|
10485
10370
|
const config = getProjectConfig();
|
|
@@ -10510,38 +10395,38 @@ var DocumentsUpload = ({ file, project, source, target }) => {
|
|
|
10510
10395
|
upload();
|
|
10511
10396
|
}, [file, project, source, target]);
|
|
10512
10397
|
if (status === "error") {
|
|
10513
|
-
return /* @__PURE__ */
|
|
10398
|
+
return /* @__PURE__ */ jsxs34(Text34, { color: "red", children: [
|
|
10514
10399
|
"Error: ",
|
|
10515
10400
|
message
|
|
10516
10401
|
] });
|
|
10517
10402
|
}
|
|
10518
10403
|
if (status === "success") {
|
|
10519
|
-
return /* @__PURE__ */
|
|
10404
|
+
return /* @__PURE__ */ jsxs34(Text34, { color: "green", children: [
|
|
10520
10405
|
"\u2713 ",
|
|
10521
10406
|
message
|
|
10522
10407
|
] });
|
|
10523
10408
|
}
|
|
10524
|
-
return /* @__PURE__ */
|
|
10525
|
-
/* @__PURE__ */
|
|
10409
|
+
return /* @__PURE__ */ jsxs34(Text34, { children: [
|
|
10410
|
+
/* @__PURE__ */ jsx36(Text34, { color: "green", children: /* @__PURE__ */ jsx36(Spinner16, { type: "dots" }) }),
|
|
10526
10411
|
" ",
|
|
10527
10412
|
message
|
|
10528
10413
|
] });
|
|
10529
10414
|
};
|
|
10530
10415
|
function runDocumentsUpload(options) {
|
|
10531
|
-
|
|
10416
|
+
render19(/* @__PURE__ */ jsx36(DocumentsUpload, { ...options }));
|
|
10532
10417
|
}
|
|
10533
10418
|
|
|
10534
10419
|
// src/commands/documents/download.tsx
|
|
10535
|
-
import { useState as
|
|
10536
|
-
import { render as
|
|
10537
|
-
import
|
|
10420
|
+
import { useState as useState24, useEffect as useEffect21 } from "react";
|
|
10421
|
+
import { render as render20, Text as Text35 } from "ink";
|
|
10422
|
+
import Spinner17 from "ink-spinner";
|
|
10538
10423
|
import fs3 from "fs";
|
|
10539
10424
|
import path3 from "path";
|
|
10540
|
-
import { jsx as
|
|
10425
|
+
import { jsx as jsx37, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
10541
10426
|
var DocumentsDownload = ({ documentId, language, project, output }) => {
|
|
10542
|
-
const [status, setStatus] =
|
|
10543
|
-
const [message, setMessage] =
|
|
10544
|
-
|
|
10427
|
+
const [status, setStatus] = useState24("downloading");
|
|
10428
|
+
const [message, setMessage] = useState24("Downloading document...");
|
|
10429
|
+
useEffect21(() => {
|
|
10545
10430
|
const download = async () => {
|
|
10546
10431
|
try {
|
|
10547
10432
|
const config = getProjectConfig();
|
|
@@ -10571,25 +10456,25 @@ var DocumentsDownload = ({ documentId, language, project, output }) => {
|
|
|
10571
10456
|
download();
|
|
10572
10457
|
}, [documentId, language, project, output]);
|
|
10573
10458
|
if (status === "error") {
|
|
10574
|
-
return /* @__PURE__ */
|
|
10459
|
+
return /* @__PURE__ */ jsxs35(Text35, { color: "red", children: [
|
|
10575
10460
|
"Error: ",
|
|
10576
10461
|
message
|
|
10577
10462
|
] });
|
|
10578
10463
|
}
|
|
10579
10464
|
if (status === "success") {
|
|
10580
|
-
return /* @__PURE__ */
|
|
10465
|
+
return /* @__PURE__ */ jsxs35(Text35, { color: "green", children: [
|
|
10581
10466
|
"\u2713 ",
|
|
10582
10467
|
message
|
|
10583
10468
|
] });
|
|
10584
10469
|
}
|
|
10585
|
-
return /* @__PURE__ */
|
|
10586
|
-
/* @__PURE__ */
|
|
10470
|
+
return /* @__PURE__ */ jsxs35(Text35, { children: [
|
|
10471
|
+
/* @__PURE__ */ jsx37(Text35, { color: "green", children: /* @__PURE__ */ jsx37(Spinner17, { type: "dots" }) }),
|
|
10587
10472
|
" ",
|
|
10588
10473
|
message
|
|
10589
10474
|
] });
|
|
10590
10475
|
};
|
|
10591
10476
|
function runDocumentsDownload(options) {
|
|
10592
|
-
|
|
10477
|
+
render20(/* @__PURE__ */ jsx37(DocumentsDownload, { ...options }));
|
|
10593
10478
|
}
|
|
10594
10479
|
|
|
10595
10480
|
// src/commands/documents/index.tsx
|
|
@@ -10797,15 +10682,6 @@ migrate.command("from <provider>").description("Migrate from Lokalise, Crowdin,
|
|
|
10797
10682
|
dryRun: options.dryRun
|
|
10798
10683
|
});
|
|
10799
10684
|
});
|
|
10800
|
-
program.command("compare").description("Compare pricing with Lokalise, Crowdin, Phrase").option("--from <provider>", "Specific competitor to compare (lokalise, crowdin, phrase)").option("--keys <n>", "Number of translation keys", "5000").option("--languages <n>", "Number of languages", "3").option("--users <n>", "Number of team members", "5").option("--json", "Output as JSON", false).action((options) => {
|
|
10801
|
-
runCompare({
|
|
10802
|
-
from: options.from,
|
|
10803
|
-
keys: parseInt(options.keys, 10),
|
|
10804
|
-
languages: parseInt(options.languages, 10),
|
|
10805
|
-
users: parseInt(options.users, 10),
|
|
10806
|
-
json: options.json
|
|
10807
|
-
});
|
|
10808
|
-
});
|
|
10809
10685
|
program.command("check").description("Check for missing translations").option("--source <lang>", "Source language", "en").option("--target <langs>", "Target languages (comma-separated)").option("--output <file>", "Output report file").option("--fix", "Auto-fix missing translations", false).action((options) => {
|
|
10810
10686
|
runCheck({
|
|
10811
10687
|
source: options.source,
|