@intlpullhq/cli 0.1.6 → 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 +680 -922
- 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
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,20 +7332,20 @@ 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";
|
|
7582
7349
|
var LANG_CODE_REGEX = /^[a-z]{2}([-_][a-zA-Z]{2})?$/i;
|
|
7583
7350
|
function CheckCommand({ options }) {
|
|
7584
7351
|
const baseTasks = [
|
|
@@ -7593,17 +7360,17 @@ function CheckCommand({ options }) {
|
|
|
7593
7360
|
if (options.output) {
|
|
7594
7361
|
baseTasks.push({ id: "output", label: "Writing report file", status: "pending" });
|
|
7595
7362
|
}
|
|
7596
|
-
const [tasks, setTasks] =
|
|
7597
|
-
const [result, setResult] =
|
|
7598
|
-
const [fixResult, setFixResult] =
|
|
7599
|
-
const [error, setError] =
|
|
7600
|
-
const [done, setDone] =
|
|
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);
|
|
7601
7368
|
const updateTask = (id, update) => {
|
|
7602
7369
|
setTasks(
|
|
7603
7370
|
(prev) => prev.map((t) => t.id === id ? { ...t, ...update } : t)
|
|
7604
7371
|
);
|
|
7605
7372
|
};
|
|
7606
|
-
|
|
7373
|
+
useEffect11(() => {
|
|
7607
7374
|
async function run() {
|
|
7608
7375
|
try {
|
|
7609
7376
|
updateTask("config", { status: "running" });
|
|
@@ -7815,50 +7582,50 @@ ${parseErrors.join("\n")}`);
|
|
|
7815
7582
|
}
|
|
7816
7583
|
run();
|
|
7817
7584
|
}, [options]);
|
|
7818
|
-
return /* @__PURE__ */
|
|
7819
|
-
/* @__PURE__ */
|
|
7820
|
-
/* @__PURE__ */
|
|
7821
|
-
error && /* @__PURE__ */
|
|
7822
|
-
done && result && /* @__PURE__ */
|
|
7823
|
-
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: [
|
|
7824
7591
|
result.totalKeys,
|
|
7825
7592
|
" keys are translated in all ",
|
|
7826
7593
|
result.targetLanguages.length,
|
|
7827
7594
|
" language(s)"
|
|
7828
|
-
] }) : /* @__PURE__ */
|
|
7595
|
+
] }) : /* @__PURE__ */ jsxs25(Alert, { type: "warning", title: "Missing translations found", children: [
|
|
7829
7596
|
result.missingKeys.length,
|
|
7830
7597
|
" key(s) missing across target languages"
|
|
7831
7598
|
] }),
|
|
7832
|
-
/* @__PURE__ */
|
|
7833
|
-
/* @__PURE__ */
|
|
7834
|
-
Object.entries(result.coveragePercentage).map(([lang, pct]) => /* @__PURE__ */
|
|
7835
|
-
/* @__PURE__ */
|
|
7836
|
-
/* @__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: [
|
|
7837
7604
|
" ",
|
|
7838
7605
|
lang,
|
|
7839
7606
|
": ",
|
|
7840
|
-
/* @__PURE__ */
|
|
7607
|
+
/* @__PURE__ */ jsxs25(Text25, { color: pct === 100 ? colors.success : colors.text, children: [
|
|
7841
7608
|
pct,
|
|
7842
7609
|
"%"
|
|
7843
7610
|
] })
|
|
7844
7611
|
] })
|
|
7845
7612
|
] }, lang))
|
|
7846
7613
|
] }),
|
|
7847
|
-
result.missingKeys.length > 0 && /* @__PURE__ */
|
|
7848
|
-
/* @__PURE__ */
|
|
7849
|
-
result.missingKeys.map((mk, i) => /* @__PURE__ */
|
|
7850
|
-
/* @__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: [
|
|
7851
7618
|
"\u2022 ",
|
|
7852
7619
|
mk.key
|
|
7853
7620
|
] }),
|
|
7854
|
-
/* @__PURE__ */
|
|
7621
|
+
/* @__PURE__ */ jsx27(Box24, { marginLeft: 2, children: /* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7855
7622
|
"Missing in: ",
|
|
7856
7623
|
mk.missingIn.join(", ")
|
|
7857
7624
|
] }) })
|
|
7858
7625
|
] }, i))
|
|
7859
7626
|
] }),
|
|
7860
|
-
fixResult && fixResult.keysAdded > 0 && /* @__PURE__ */
|
|
7861
|
-
/* @__PURE__ */
|
|
7627
|
+
fixResult && fixResult.keysAdded > 0 && /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", marginTop: 1, children: [
|
|
7628
|
+
/* @__PURE__ */ jsxs25(Text25, { bold: true, color: colors.success, children: [
|
|
7862
7629
|
icons.success,
|
|
7863
7630
|
" Fixed ",
|
|
7864
7631
|
fixResult.keysAdded,
|
|
@@ -7866,22 +7633,22 @@ ${parseErrors.join("\n")}`);
|
|
|
7866
7633
|
fixResult.filesModified,
|
|
7867
7634
|
" file(s)"
|
|
7868
7635
|
] }),
|
|
7869
|
-
/* @__PURE__ */
|
|
7870
|
-
/* @__PURE__ */
|
|
7636
|
+
/* @__PURE__ */ jsxs25(Box24, { marginTop: 1, children: [
|
|
7637
|
+
/* @__PURE__ */ jsxs25(Text25, { color: colors.warning, children: [
|
|
7871
7638
|
icons.warning,
|
|
7872
7639
|
" "
|
|
7873
7640
|
] }),
|
|
7874
|
-
/* @__PURE__ */
|
|
7641
|
+
/* @__PURE__ */ jsx27(Text25, { color: colors.textMuted, children: "Missing keys have been added with [LANG] prefix. Review and translate them." })
|
|
7875
7642
|
] })
|
|
7876
7643
|
] }),
|
|
7877
|
-
!options.fix && result.missingKeys.length > 0 && /* @__PURE__ */
|
|
7644
|
+
!options.fix && result.missingKeys.length > 0 && /* @__PURE__ */ jsx27(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7878
7645
|
"Run ",
|
|
7879
|
-
/* @__PURE__ */
|
|
7646
|
+
/* @__PURE__ */ jsx27(Text25, { color: colors.primary, children: "npx @intlpullhq/cli fix" }),
|
|
7880
7647
|
" to auto-generate missing translations"
|
|
7881
7648
|
] }) }),
|
|
7882
|
-
options.output && /* @__PURE__ */
|
|
7649
|
+
options.output && /* @__PURE__ */ jsx27(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs25(Text25, { color: colors.textMuted, children: [
|
|
7883
7650
|
"Report saved to ",
|
|
7884
|
-
/* @__PURE__ */
|
|
7651
|
+
/* @__PURE__ */ jsx27(Text25, { color: colors.primary, children: options.output })
|
|
7885
7652
|
] }) })
|
|
7886
7653
|
] })
|
|
7887
7654
|
] });
|
|
@@ -7920,14 +7687,14 @@ function setNestedValue(obj, key, value) {
|
|
|
7920
7687
|
current[parts[parts.length - 1]] = value;
|
|
7921
7688
|
}
|
|
7922
7689
|
function runCheck(options) {
|
|
7923
|
-
|
|
7690
|
+
render10(/* @__PURE__ */ jsx27(CheckCommand, { options }));
|
|
7924
7691
|
}
|
|
7925
7692
|
|
|
7926
7693
|
// src/commands/diff.tsx
|
|
7927
|
-
import { useState as
|
|
7928
|
-
import { render as
|
|
7929
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
7930
|
-
import { jsx as
|
|
7694
|
+
import { useState as useState15, useEffect as useEffect12 } from "react";
|
|
7695
|
+
import { render as render11, Box as Box25, Text as Text26 } from "ink";
|
|
7696
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
7697
|
+
import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
7931
7698
|
async function fetchProjects5(apiUrl, apiKey) {
|
|
7932
7699
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
7933
7700
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -7944,21 +7711,21 @@ async function fetchProjects5(apiUrl, apiKey) {
|
|
|
7944
7711
|
}));
|
|
7945
7712
|
}
|
|
7946
7713
|
function DiffCommand({ options }) {
|
|
7947
|
-
const [tasks, setTasks] =
|
|
7714
|
+
const [tasks, setTasks] = useState15([
|
|
7948
7715
|
{ id: "config", label: "Loading configuration", status: "pending" },
|
|
7949
7716
|
{ id: "local", label: "Reading local files", status: "pending" },
|
|
7950
7717
|
{ id: "remote", label: "Fetching from IntlPull", status: "pending" },
|
|
7951
7718
|
{ id: "compare", label: "Comparing changes", status: "pending" }
|
|
7952
7719
|
]);
|
|
7953
|
-
const [result, setResult] =
|
|
7954
|
-
const [error, setError] =
|
|
7955
|
-
const [done, setDone] =
|
|
7720
|
+
const [result, setResult] = useState15(null);
|
|
7721
|
+
const [error, setError] = useState15(null);
|
|
7722
|
+
const [done, setDone] = useState15(false);
|
|
7956
7723
|
const updateTask = (id, update) => {
|
|
7957
7724
|
setTasks(
|
|
7958
7725
|
(prev) => prev.map((t) => t.id === id ? { ...t, ...update } : t)
|
|
7959
7726
|
);
|
|
7960
7727
|
};
|
|
7961
|
-
|
|
7728
|
+
useEffect12(() => {
|
|
7962
7729
|
async function run() {
|
|
7963
7730
|
try {
|
|
7964
7731
|
updateTask("config", { status: "running" });
|
|
@@ -8006,7 +7773,7 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
8006
7773
|
];
|
|
8007
7774
|
let localFile = null;
|
|
8008
7775
|
for (const path4 of possiblePaths) {
|
|
8009
|
-
if (
|
|
7776
|
+
if (existsSync7(path4)) {
|
|
8010
7777
|
localFile = path4;
|
|
8011
7778
|
break;
|
|
8012
7779
|
}
|
|
@@ -8079,12 +7846,12 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
8079
7846
|
}
|
|
8080
7847
|
run();
|
|
8081
7848
|
}, [options.source, options.target]);
|
|
8082
|
-
return /* @__PURE__ */
|
|
8083
|
-
/* @__PURE__ */
|
|
8084
|
-
/* @__PURE__ */
|
|
8085
|
-
error && /* @__PURE__ */
|
|
8086
|
-
done && result && /* @__PURE__ */
|
|
8087
|
-
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: [
|
|
8088
7855
|
result.added.length,
|
|
8089
7856
|
" new locally, ",
|
|
8090
7857
|
result.removed.length,
|
|
@@ -8092,79 +7859,79 @@ Or use a project-scoped API key for automatic selection.`
|
|
|
8092
7859
|
result.changed.length,
|
|
8093
7860
|
" modified"
|
|
8094
7861
|
] }),
|
|
8095
|
-
result.added.length > 0 && /* @__PURE__ */
|
|
8096
|
-
/* @__PURE__ */
|
|
7862
|
+
result.added.length > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7863
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.success, children: [
|
|
8097
7864
|
"+ New in local (",
|
|
8098
7865
|
result.added.length,
|
|
8099
7866
|
"):"
|
|
8100
7867
|
] }),
|
|
8101
|
-
result.added.slice(0, 5).map((entry, i) => /* @__PURE__ */
|
|
8102
|
-
/* @__PURE__ */
|
|
8103
|
-
/* @__PURE__ */
|
|
8104
|
-
/* @__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: [
|
|
8105
7872
|
' = "',
|
|
8106
7873
|
truncate(entry.newValue || "", 40),
|
|
8107
7874
|
'"'
|
|
8108
7875
|
] })
|
|
8109
7876
|
] }, i)),
|
|
8110
|
-
result.added.length > 5 && /* @__PURE__ */
|
|
7877
|
+
result.added.length > 5 && /* @__PURE__ */ jsx28(Box25, { marginLeft: 2, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8111
7878
|
"... and ",
|
|
8112
7879
|
result.added.length - 5,
|
|
8113
7880
|
" more"
|
|
8114
7881
|
] }) })
|
|
8115
7882
|
] }),
|
|
8116
|
-
result.removed.length > 0 && /* @__PURE__ */
|
|
8117
|
-
/* @__PURE__ */
|
|
7883
|
+
result.removed.length > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7884
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.error, children: [
|
|
8118
7885
|
"- Missing locally (",
|
|
8119
7886
|
result.removed.length,
|
|
8120
7887
|
"):"
|
|
8121
7888
|
] }),
|
|
8122
|
-
result.removed.slice(0, 5).map((entry, i) => /* @__PURE__ */
|
|
8123
|
-
/* @__PURE__ */
|
|
8124
|
-
/* @__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 })
|
|
8125
7892
|
] }, i)),
|
|
8126
|
-
result.removed.length > 5 && /* @__PURE__ */
|
|
7893
|
+
result.removed.length > 5 && /* @__PURE__ */ jsx28(Box25, { marginLeft: 2, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8127
7894
|
"... and ",
|
|
8128
7895
|
result.removed.length - 5,
|
|
8129
7896
|
" more"
|
|
8130
7897
|
] }) })
|
|
8131
7898
|
] }),
|
|
8132
|
-
result.changed.length > 0 && /* @__PURE__ */
|
|
8133
|
-
/* @__PURE__ */
|
|
7899
|
+
result.changed.length > 0 && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginTop: 1, children: [
|
|
7900
|
+
/* @__PURE__ */ jsxs26(Text26, { bold: true, color: colors.warning, children: [
|
|
8134
7901
|
"~ Modified (",
|
|
8135
7902
|
result.changed.length,
|
|
8136
7903
|
"):"
|
|
8137
7904
|
] }),
|
|
8138
|
-
result.changed.slice(0, 5).map((entry, i) => /* @__PURE__ */
|
|
8139
|
-
/* @__PURE__ */
|
|
8140
|
-
/* @__PURE__ */
|
|
8141
|
-
/* @__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 })
|
|
8142
7909
|
] }),
|
|
8143
|
-
/* @__PURE__ */
|
|
7910
|
+
/* @__PURE__ */ jsx28(Box25, { marginLeft: 4, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textDim, children: [
|
|
8144
7911
|
'remote: "',
|
|
8145
7912
|
truncate(entry.oldValue || "", 30),
|
|
8146
7913
|
'"'
|
|
8147
7914
|
] }) }),
|
|
8148
|
-
/* @__PURE__ */
|
|
7915
|
+
/* @__PURE__ */ jsx28(Box25, { marginLeft: 4, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textDim, children: [
|
|
8149
7916
|
'local: "',
|
|
8150
7917
|
truncate(entry.newValue || "", 30),
|
|
8151
7918
|
'"'
|
|
8152
7919
|
] }) })
|
|
8153
7920
|
] }, i)),
|
|
8154
|
-
result.changed.length > 5 && /* @__PURE__ */
|
|
7921
|
+
result.changed.length > 5 && /* @__PURE__ */ jsx28(Box25, { marginLeft: 2, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8155
7922
|
"... and ",
|
|
8156
7923
|
result.changed.length - 5,
|
|
8157
7924
|
" more"
|
|
8158
7925
|
] }) })
|
|
8159
7926
|
] }),
|
|
8160
|
-
result.added.length > 0 && /* @__PURE__ */
|
|
7927
|
+
result.added.length > 0 && /* @__PURE__ */ jsx28(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8161
7928
|
"Run ",
|
|
8162
|
-
/* @__PURE__ */
|
|
7929
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.primary, children: "npx @intlpullhq/cli upload" }),
|
|
8163
7930
|
" to upload new keys to IntlPull"
|
|
8164
7931
|
] }) }),
|
|
8165
|
-
result.removed.length > 0 && /* @__PURE__ */
|
|
7932
|
+
result.removed.length > 0 && /* @__PURE__ */ jsx28(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: colors.textMuted, children: [
|
|
8166
7933
|
"Run ",
|
|
8167
|
-
/* @__PURE__ */
|
|
7934
|
+
/* @__PURE__ */ jsx28(Text26, { color: colors.primary, children: "npx @intlpullhq/cli download" }),
|
|
8168
7935
|
" to download missing keys from IntlPull"
|
|
8169
7936
|
] }) })
|
|
8170
7937
|
] })
|
|
@@ -8184,30 +7951,30 @@ function truncate(str, length) {
|
|
|
8184
7951
|
return str.length > length ? str.substring(0, length) + "..." : str;
|
|
8185
7952
|
}
|
|
8186
7953
|
function runDiff(options) {
|
|
8187
|
-
|
|
7954
|
+
render11(/* @__PURE__ */ jsx28(DiffCommand, { options }));
|
|
8188
7955
|
}
|
|
8189
7956
|
|
|
8190
7957
|
// src/commands/fix.tsx
|
|
8191
|
-
import { useState as
|
|
8192
|
-
import { render as
|
|
7958
|
+
import { useState as useState16, useEffect as useEffect13 } from "react";
|
|
7959
|
+
import { render as render12, Box as Box26, Text as Text27 } from "ink";
|
|
8193
7960
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
8194
7961
|
import { glob as glob2 } from "glob";
|
|
8195
|
-
import { jsx as
|
|
7962
|
+
import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
8196
7963
|
function FixCommand({ options }) {
|
|
8197
|
-
const [tasks, setTasks] =
|
|
7964
|
+
const [tasks, setTasks] = useState16([
|
|
8198
7965
|
{ id: "config", label: "Loading configuration", status: "pending" },
|
|
8199
7966
|
{ id: "scan", label: "Scanning for missing keys", status: "pending" },
|
|
8200
7967
|
{ id: "fix", label: "Adding missing keys", status: "pending" }
|
|
8201
7968
|
]);
|
|
8202
|
-
const [result, setResult] =
|
|
8203
|
-
const [error, setError] =
|
|
8204
|
-
const [done, setDone] =
|
|
7969
|
+
const [result, setResult] = useState16(null);
|
|
7970
|
+
const [error, setError] = useState16(null);
|
|
7971
|
+
const [done, setDone] = useState16(false);
|
|
8205
7972
|
const updateTask = (id, update) => {
|
|
8206
7973
|
setTasks(
|
|
8207
7974
|
(prev) => prev.map((t) => t.id === id ? { ...t, ...update } : t)
|
|
8208
7975
|
);
|
|
8209
7976
|
};
|
|
8210
|
-
|
|
7977
|
+
useEffect13(() => {
|
|
8211
7978
|
async function run() {
|
|
8212
7979
|
try {
|
|
8213
7980
|
updateTask("config", { status: "running" });
|
|
@@ -8328,44 +8095,44 @@ ${parseErrors.join("\n")}`);
|
|
|
8328
8095
|
}
|
|
8329
8096
|
run();
|
|
8330
8097
|
}, [options]);
|
|
8331
|
-
return /* @__PURE__ */
|
|
8332
|
-
/* @__PURE__ */
|
|
8333
|
-
/* @__PURE__ */
|
|
8334
|
-
error && /* @__PURE__ */
|
|
8335
|
-
done && result && /* @__PURE__ */
|
|
8336
|
-
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: [
|
|
8337
8104
|
"Would add ",
|
|
8338
8105
|
result.keysAdded,
|
|
8339
8106
|
" missing key(s) to ",
|
|
8340
8107
|
result.filesModified,
|
|
8341
8108
|
" file(s)"
|
|
8342
|
-
] }) : /* @__PURE__ */
|
|
8109
|
+
] }) : /* @__PURE__ */ jsxs27(Alert, { type: "success", title: "Fix complete", children: [
|
|
8343
8110
|
"Added ",
|
|
8344
8111
|
result.keysAdded,
|
|
8345
8112
|
" missing key(s) to ",
|
|
8346
8113
|
result.filesModified,
|
|
8347
8114
|
" file(s)"
|
|
8348
8115
|
] }),
|
|
8349
|
-
result.keysAdded > 0 && /* @__PURE__ */
|
|
8350
|
-
/* @__PURE__ */
|
|
8351
|
-
result.languages.map((lang) => /* @__PURE__ */
|
|
8352
|
-
/* @__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: [
|
|
8353
8120
|
icons.success,
|
|
8354
8121
|
" "
|
|
8355
8122
|
] }),
|
|
8356
|
-
/* @__PURE__ */
|
|
8123
|
+
/* @__PURE__ */ jsx29(Text27, { color: colors.text, children: lang })
|
|
8357
8124
|
] }, lang))
|
|
8358
8125
|
] }),
|
|
8359
|
-
result.keysAdded > 0 && !options.dryRun && /* @__PURE__ */
|
|
8360
|
-
/* @__PURE__ */
|
|
8126
|
+
result.keysAdded > 0 && !options.dryRun && /* @__PURE__ */ jsxs27(Box26, { marginTop: 1, children: [
|
|
8127
|
+
/* @__PURE__ */ jsxs27(Text27, { color: colors.warning, children: [
|
|
8361
8128
|
icons.warning,
|
|
8362
8129
|
" "
|
|
8363
8130
|
] }),
|
|
8364
|
-
/* @__PURE__ */
|
|
8131
|
+
/* @__PURE__ */ jsx29(Text27, { color: colors.textMuted, children: "Missing keys have been added with [LANG] prefix. Review and translate them." })
|
|
8365
8132
|
] }),
|
|
8366
|
-
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: [
|
|
8367
8134
|
"Run without ",
|
|
8368
|
-
/* @__PURE__ */
|
|
8135
|
+
/* @__PURE__ */ jsx29(Text27, { color: colors.primary, children: "--dry-run" }),
|
|
8369
8136
|
" to apply fixes"
|
|
8370
8137
|
] }) })
|
|
8371
8138
|
] })
|
|
@@ -8395,16 +8162,16 @@ function setNestedValue2(obj, key, value) {
|
|
|
8395
8162
|
current[parts[parts.length - 1]] = value;
|
|
8396
8163
|
}
|
|
8397
8164
|
function runFix(options) {
|
|
8398
|
-
|
|
8165
|
+
render12(/* @__PURE__ */ jsx29(FixCommand, { options }));
|
|
8399
8166
|
}
|
|
8400
8167
|
|
|
8401
8168
|
// src/commands/listen.tsx
|
|
8402
|
-
import { useState as
|
|
8403
|
-
import { render as
|
|
8404
|
-
import
|
|
8405
|
-
import { writeFileSync as writeFileSync6, mkdirSync as
|
|
8406
|
-
import { join as
|
|
8407
|
-
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";
|
|
8408
8175
|
async function fetchProjects6(apiUrl, apiKey) {
|
|
8409
8176
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
8410
8177
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -8541,9 +8308,9 @@ function writeTranslationFiles(bundle, outputDir, format, languages) {
|
|
|
8541
8308
|
const targetLanguages = languages || Object.keys(bundle);
|
|
8542
8309
|
for (const locale of targetLanguages) {
|
|
8543
8310
|
if (!bundle[locale]) continue;
|
|
8544
|
-
const localeDir =
|
|
8545
|
-
if (!
|
|
8546
|
-
|
|
8311
|
+
const localeDir = join8(outputDir, locale);
|
|
8312
|
+
if (!existsSync9(localeDir)) {
|
|
8313
|
+
mkdirSync4(localeDir, { recursive: true });
|
|
8547
8314
|
}
|
|
8548
8315
|
let content;
|
|
8549
8316
|
let filename;
|
|
@@ -8563,7 +8330,7 @@ function writeTranslationFiles(bundle, outputDir, format, languages) {
|
|
|
8563
8330
|
filename = "translations.json";
|
|
8564
8331
|
break;
|
|
8565
8332
|
}
|
|
8566
|
-
const filePath =
|
|
8333
|
+
const filePath = join8(localeDir, filename);
|
|
8567
8334
|
writeFileSync6(filePath, content);
|
|
8568
8335
|
writtenFiles.push(filePath);
|
|
8569
8336
|
}
|
|
@@ -8586,7 +8353,7 @@ function formatRelativeTime(date) {
|
|
|
8586
8353
|
}
|
|
8587
8354
|
function ListenComponent({ options }) {
|
|
8588
8355
|
const { exit } = useApp6();
|
|
8589
|
-
const [state, setState] =
|
|
8356
|
+
const [state, setState] = useState17({
|
|
8590
8357
|
status: "connecting",
|
|
8591
8358
|
message: "Connecting to IntlPull...",
|
|
8592
8359
|
lastSync: null,
|
|
@@ -8710,7 +8477,7 @@ function ListenComponent({ options }) {
|
|
|
8710
8477
|
}));
|
|
8711
8478
|
}
|
|
8712
8479
|
}, [options, state.version, state.syncCount]);
|
|
8713
|
-
|
|
8480
|
+
useEffect14(() => {
|
|
8714
8481
|
sync();
|
|
8715
8482
|
if (options.once) {
|
|
8716
8483
|
return;
|
|
@@ -8722,7 +8489,7 @@ function ListenComponent({ options }) {
|
|
|
8722
8489
|
}, intervalMs);
|
|
8723
8490
|
return () => clearInterval(interval);
|
|
8724
8491
|
}, [sync, intervalMs, options.once]);
|
|
8725
|
-
|
|
8492
|
+
useEffect14(() => {
|
|
8726
8493
|
if (!options.timeout || options.once) return;
|
|
8727
8494
|
const timeoutMs = options.timeout * 1e3;
|
|
8728
8495
|
const timer = setTimeout(() => {
|
|
@@ -8735,7 +8502,7 @@ function ListenComponent({ options }) {
|
|
|
8735
8502
|
}, timeoutMs);
|
|
8736
8503
|
return () => clearTimeout(timer);
|
|
8737
8504
|
}, [options.timeout, options.once, exit]);
|
|
8738
|
-
|
|
8505
|
+
useEffect14(() => {
|
|
8739
8506
|
if (options.once && state.status === "watching") {
|
|
8740
8507
|
setTimeout(() => exit(), 100);
|
|
8741
8508
|
}
|
|
@@ -8754,57 +8521,57 @@ function ListenComponent({ options }) {
|
|
|
8754
8521
|
error: "\u2717",
|
|
8755
8522
|
stopped: "\u25FB"
|
|
8756
8523
|
}[state.status];
|
|
8757
|
-
return /* @__PURE__ */
|
|
8758
|
-
/* @__PURE__ */
|
|
8759
|
-
/* @__PURE__ */
|
|
8760
|
-
/* @__PURE__ */
|
|
8761
|
-
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)" })
|
|
8762
8529
|
] }),
|
|
8763
|
-
/* @__PURE__ */
|
|
8764
|
-
state.status === "syncing" ? /* @__PURE__ */
|
|
8765
|
-
/* @__PURE__ */
|
|
8766
|
-
/* @__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 })
|
|
8767
8534
|
] }),
|
|
8768
|
-
state.error && /* @__PURE__ */
|
|
8769
|
-
/* @__PURE__ */
|
|
8770
|
-
/* @__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: [
|
|
8771
8538
|
"Retrying in ",
|
|
8772
8539
|
intervalMs / 1e3,
|
|
8773
8540
|
"s..."
|
|
8774
8541
|
] })
|
|
8775
8542
|
] }),
|
|
8776
|
-
state.lastSync && !options.quiet && /* @__PURE__ */
|
|
8777
|
-
/* @__PURE__ */
|
|
8778
|
-
/* @__PURE__ */
|
|
8779
|
-
/* @__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 })
|
|
8780
8547
|
] }),
|
|
8781
|
-
/* @__PURE__ */
|
|
8782
|
-
/* @__PURE__ */
|
|
8783
|
-
/* @__PURE__ */
|
|
8548
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8549
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Version: " }),
|
|
8550
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.version })
|
|
8784
8551
|
] }),
|
|
8785
|
-
/* @__PURE__ */
|
|
8786
|
-
/* @__PURE__ */
|
|
8787
|
-
/* @__PURE__ */
|
|
8552
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8553
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Languages: " }),
|
|
8554
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.languages.join(", ") })
|
|
8788
8555
|
] }),
|
|
8789
|
-
/* @__PURE__ */
|
|
8790
|
-
/* @__PURE__ */
|
|
8791
|
-
/* @__PURE__ */
|
|
8556
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8557
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Keys: " }),
|
|
8558
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.keyCount })
|
|
8792
8559
|
] }),
|
|
8793
|
-
/* @__PURE__ */
|
|
8794
|
-
/* @__PURE__ */
|
|
8795
|
-
/* @__PURE__ */
|
|
8560
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8561
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Last sync: " }),
|
|
8562
|
+
/* @__PURE__ */ jsxs28(Text28, { children: [
|
|
8796
8563
|
formatTime(state.lastSync),
|
|
8797
8564
|
" (",
|
|
8798
8565
|
formatRelativeTime(state.lastSync),
|
|
8799
8566
|
")"
|
|
8800
8567
|
] })
|
|
8801
8568
|
] }),
|
|
8802
|
-
/* @__PURE__ */
|
|
8803
|
-
/* @__PURE__ */
|
|
8804
|
-
/* @__PURE__ */
|
|
8569
|
+
/* @__PURE__ */ jsxs28(Box27, { children: [
|
|
8570
|
+
/* @__PURE__ */ jsx30(Text28, { dimColor: true, children: "Syncs: " }),
|
|
8571
|
+
/* @__PURE__ */ jsx30(Text28, { children: state.syncCount })
|
|
8805
8572
|
] })
|
|
8806
8573
|
] }),
|
|
8807
|
-
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: [
|
|
8808
8575
|
"Polling every ",
|
|
8809
8576
|
intervalMs / 1e3,
|
|
8810
8577
|
"s for changes..."
|
|
@@ -8927,14 +8694,14 @@ function runListen(options) {
|
|
|
8927
8694
|
runOnceSync(options);
|
|
8928
8695
|
return;
|
|
8929
8696
|
}
|
|
8930
|
-
|
|
8697
|
+
render13(/* @__PURE__ */ jsx30(ListenComponent, { options }));
|
|
8931
8698
|
}
|
|
8932
8699
|
|
|
8933
8700
|
// src/commands/publish.tsx
|
|
8934
|
-
import { useState as
|
|
8935
|
-
import { render as
|
|
8936
|
-
import
|
|
8937
|
-
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";
|
|
8938
8705
|
async function fetchProjects7(apiUrl, apiKey) {
|
|
8939
8706
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
8940
8707
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -9104,11 +8871,11 @@ function generateVersion() {
|
|
|
9104
8871
|
return `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}-${now.getHours()}${now.getMinutes()}`;
|
|
9105
8872
|
}
|
|
9106
8873
|
function PublishComponent({ options }) {
|
|
9107
|
-
const [state, setState] =
|
|
8874
|
+
const [state, setState] = useState18({
|
|
9108
8875
|
status: "detecting",
|
|
9109
8876
|
message: "Detecting project..."
|
|
9110
8877
|
});
|
|
9111
|
-
|
|
8878
|
+
useEffect15(() => {
|
|
9112
8879
|
async function run() {
|
|
9113
8880
|
try {
|
|
9114
8881
|
const resolved = getResolvedApiKey();
|
|
@@ -9192,96 +8959,96 @@ function PublishComponent({ options }) {
|
|
|
9192
8959
|
}
|
|
9193
8960
|
return null;
|
|
9194
8961
|
}
|
|
9195
|
-
return /* @__PURE__ */
|
|
9196
|
-
/* @__PURE__ */
|
|
9197
|
-
/* @__PURE__ */
|
|
9198
|
-
/* @__PURE__ */
|
|
9199
|
-
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)" })
|
|
9200
8967
|
] }),
|
|
9201
|
-
(state.status === "detecting" || state.status === "publishing") && /* @__PURE__ */
|
|
9202
|
-
/* @__PURE__ */
|
|
9203
|
-
/* @__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: [
|
|
9204
8971
|
" ",
|
|
9205
8972
|
state.message
|
|
9206
8973
|
] })
|
|
9207
8974
|
] }),
|
|
9208
|
-
state.status === "success" && /* @__PURE__ */
|
|
9209
|
-
/* @__PURE__ */
|
|
8975
|
+
state.status === "success" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
|
|
8976
|
+
/* @__PURE__ */ jsxs29(Text29, { color: "green", children: [
|
|
9210
8977
|
"\u2713 ",
|
|
9211
8978
|
state.message
|
|
9212
8979
|
] }),
|
|
9213
|
-
state.projectName && /* @__PURE__ */
|
|
8980
|
+
state.projectName && /* @__PURE__ */ jsx31(Box28, { marginTop: 1, children: /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9214
8981
|
"Project: ",
|
|
9215
|
-
/* @__PURE__ */
|
|
8982
|
+
/* @__PURE__ */ jsx31(Text29, { color: "white", children: state.projectName })
|
|
9216
8983
|
] }) }),
|
|
9217
|
-
state.branch && /* @__PURE__ */
|
|
8984
|
+
state.branch && /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9218
8985
|
"Branch: ",
|
|
9219
|
-
/* @__PURE__ */
|
|
8986
|
+
/* @__PURE__ */ jsx31(Text29, { color: "white", children: state.branch })
|
|
9220
8987
|
] }),
|
|
9221
|
-
state.release && /* @__PURE__ */
|
|
9222
|
-
/* @__PURE__ */
|
|
8988
|
+
state.release && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 1, children: [
|
|
8989
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9223
8990
|
"Version: ",
|
|
9224
|
-
/* @__PURE__ */
|
|
8991
|
+
/* @__PURE__ */ jsx31(Text29, { color: "green", bold: true, children: state.release.version })
|
|
9225
8992
|
] }),
|
|
9226
|
-
/* @__PURE__ */
|
|
8993
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9227
8994
|
"Languages: ",
|
|
9228
8995
|
state.release.languages.join(", ")
|
|
9229
8996
|
] }),
|
|
9230
|
-
/* @__PURE__ */
|
|
8997
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9231
8998
|
"Keys: ",
|
|
9232
8999
|
state.release.keyCount
|
|
9233
9000
|
] }),
|
|
9234
|
-
/* @__PURE__ */
|
|
9001
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9235
9002
|
"Bundle size: ",
|
|
9236
9003
|
formatBytes(state.release.bundleSize)
|
|
9237
9004
|
] }),
|
|
9238
|
-
/* @__PURE__ */
|
|
9239
|
-
/* @__PURE__ */
|
|
9240
|
-
/* @__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: [
|
|
9241
9008
|
" ",
|
|
9242
9009
|
state.release.bundleUrl.replace(/\/[^/]+\.json$/, "/manifest")
|
|
9243
9010
|
] })
|
|
9244
9011
|
] })
|
|
9245
9012
|
] }),
|
|
9246
|
-
!state.release && state.summary && /* @__PURE__ */
|
|
9247
|
-
/* @__PURE__ */
|
|
9248
|
-
/* @__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: [
|
|
9249
9016
|
" Languages: ",
|
|
9250
9017
|
state.summary.languages.join(", ")
|
|
9251
9018
|
] }),
|
|
9252
|
-
/* @__PURE__ */
|
|
9019
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9253
9020
|
" Keys: ",
|
|
9254
9021
|
state.summary.keyCount
|
|
9255
9022
|
] }),
|
|
9256
|
-
/* @__PURE__ */
|
|
9023
|
+
/* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
|
|
9257
9024
|
" Estimated size: ",
|
|
9258
9025
|
formatBytes(state.summary.estimatedSize)
|
|
9259
9026
|
] })
|
|
9260
9027
|
] })
|
|
9261
9028
|
] }),
|
|
9262
|
-
state.status === "error" && /* @__PURE__ */
|
|
9263
|
-
/* @__PURE__ */
|
|
9029
|
+
state.status === "error" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
|
|
9030
|
+
/* @__PURE__ */ jsxs29(Text29, { color: "red", children: [
|
|
9264
9031
|
"\u2717 ",
|
|
9265
9032
|
state.message
|
|
9266
9033
|
] }),
|
|
9267
|
-
/* @__PURE__ */
|
|
9268
|
-
/* @__PURE__ */
|
|
9269
|
-
/* @__PURE__ */
|
|
9270
|
-
/* @__PURE__ */
|
|
9271
|
-
/* @__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" })
|
|
9272
9039
|
] })
|
|
9273
9040
|
] })
|
|
9274
9041
|
] });
|
|
9275
9042
|
}
|
|
9276
9043
|
function runPublish(options) {
|
|
9277
|
-
|
|
9044
|
+
render14(/* @__PURE__ */ jsx31(PublishComponent, { options }));
|
|
9278
9045
|
}
|
|
9279
9046
|
|
|
9280
9047
|
// src/commands/releases.tsx
|
|
9281
|
-
import { useState as
|
|
9282
|
-
import { render as
|
|
9283
|
-
import
|
|
9284
|
-
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";
|
|
9285
9052
|
async function fetchProjects8(apiUrl, apiKey) {
|
|
9286
9053
|
const response = await fetch(`${apiUrl}/api/v1/projects`, {
|
|
9287
9054
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -9355,11 +9122,11 @@ function formatDate(dateStr) {
|
|
|
9355
9122
|
});
|
|
9356
9123
|
}
|
|
9357
9124
|
function ReleasesComponent({ options }) {
|
|
9358
|
-
const [state, setState] =
|
|
9125
|
+
const [state, setState] = useState19({
|
|
9359
9126
|
status: "loading",
|
|
9360
9127
|
message: "Loading releases..."
|
|
9361
9128
|
});
|
|
9362
|
-
|
|
9129
|
+
useEffect16(() => {
|
|
9363
9130
|
async function run() {
|
|
9364
9131
|
try {
|
|
9365
9132
|
const resolved = getResolvedApiKey();
|
|
@@ -9424,41 +9191,41 @@ function ReleasesComponent({ options }) {
|
|
|
9424
9191
|
}
|
|
9425
9192
|
return null;
|
|
9426
9193
|
}
|
|
9427
|
-
return /* @__PURE__ */
|
|
9428
|
-
/* @__PURE__ */
|
|
9429
|
-
/* @__PURE__ */
|
|
9430
|
-
/* @__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" })
|
|
9431
9198
|
] }),
|
|
9432
|
-
state.status === "loading" && /* @__PURE__ */
|
|
9433
|
-
/* @__PURE__ */
|
|
9434
|
-
/* @__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: [
|
|
9435
9202
|
" ",
|
|
9436
9203
|
state.message
|
|
9437
9204
|
] })
|
|
9438
9205
|
] }),
|
|
9439
|
-
state.status === "success" && state.deleted && /* @__PURE__ */
|
|
9206
|
+
state.status === "success" && state.deleted && /* @__PURE__ */ jsxs30(Text30, { color: "green", children: [
|
|
9440
9207
|
"\u2713 ",
|
|
9441
9208
|
state.message
|
|
9442
9209
|
] }),
|
|
9443
|
-
state.status === "success" && state.releases && /* @__PURE__ */
|
|
9444
|
-
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: [
|
|
9445
9212
|
"Project: ",
|
|
9446
|
-
/* @__PURE__ */
|
|
9213
|
+
/* @__PURE__ */ jsx32(Text30, { color: "white", children: state.projectName })
|
|
9447
9214
|
] }) }),
|
|
9448
|
-
state.releases.length === 0 ? /* @__PURE__ */
|
|
9449
|
-
/* @__PURE__ */
|
|
9450
|
-
/* @__PURE__ */
|
|
9451
|
-
] }) : /* @__PURE__ */
|
|
9452
|
-
state.releases.map((release, i) => /* @__PURE__ */
|
|
9453
|
-
/* @__PURE__ */
|
|
9454
|
-
/* @__PURE__ */
|
|
9455
|
-
/* @__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: [
|
|
9456
9223
|
" \u2022 ",
|
|
9457
9224
|
formatDate(release.publishedAt)
|
|
9458
9225
|
] }),
|
|
9459
|
-
i === 0 && /* @__PURE__ */
|
|
9226
|
+
i === 0 && /* @__PURE__ */ jsx32(Text30, { color: "cyan", children: " (latest)" })
|
|
9460
9227
|
] }),
|
|
9461
|
-
/* @__PURE__ */
|
|
9228
|
+
/* @__PURE__ */ jsx32(Box29, { paddingLeft: 2, children: /* @__PURE__ */ jsxs30(Text30, { dimColor: true, children: [
|
|
9462
9229
|
release.keyCount,
|
|
9463
9230
|
" keys \u2022 ",
|
|
9464
9231
|
release.languages.length,
|
|
@@ -9466,44 +9233,44 @@ function ReleasesComponent({ options }) {
|
|
|
9466
9233
|
formatBytes2(release.bundleSize),
|
|
9467
9234
|
release.downloadCount > 0 && ` \u2022 ${release.downloadCount} downloads`
|
|
9468
9235
|
] }) }),
|
|
9469
|
-
/* @__PURE__ */
|
|
9236
|
+
/* @__PURE__ */ jsx32(Box29, { paddingLeft: 2, children: /* @__PURE__ */ jsxs30(Text30, { color: "gray", children: [
|
|
9470
9237
|
"ID: ",
|
|
9471
9238
|
release.id
|
|
9472
9239
|
] }) })
|
|
9473
9240
|
] }, release.id)),
|
|
9474
|
-
/* @__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." }) })
|
|
9475
9242
|
] })
|
|
9476
9243
|
] }),
|
|
9477
|
-
state.status === "error" && /* @__PURE__ */
|
|
9478
|
-
/* @__PURE__ */
|
|
9244
|
+
state.status === "error" && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
|
|
9245
|
+
/* @__PURE__ */ jsxs30(Text30, { color: "red", children: [
|
|
9479
9246
|
"\u2717 ",
|
|
9480
9247
|
state.message
|
|
9481
9248
|
] }),
|
|
9482
|
-
/* @__PURE__ */
|
|
9483
|
-
/* @__PURE__ */
|
|
9484
|
-
/* @__PURE__ */
|
|
9485
|
-
/* @__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" })
|
|
9486
9253
|
] })
|
|
9487
9254
|
] })
|
|
9488
9255
|
] });
|
|
9489
9256
|
}
|
|
9490
9257
|
function runReleases(options) {
|
|
9491
|
-
|
|
9258
|
+
render15(/* @__PURE__ */ jsx32(ReleasesComponent, { options }));
|
|
9492
9259
|
}
|
|
9493
9260
|
|
|
9494
9261
|
// src/commands/workflow.tsx
|
|
9495
|
-
import { useState as
|
|
9496
|
-
import { render as
|
|
9497
|
-
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";
|
|
9498
9265
|
function WorkflowStatusCommand({ projectId }) {
|
|
9499
|
-
const [loading, setLoading] =
|
|
9500
|
-
const [error, setError] =
|
|
9501
|
-
const [projectName, setProjectName] =
|
|
9502
|
-
const [workflowsEnabled, setWorkflowsEnabled] =
|
|
9503
|
-
const [workflows, setWorkflows] =
|
|
9504
|
-
const [pending, setPending] =
|
|
9505
|
-
const [locked, setLocked] =
|
|
9506
|
-
|
|
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(() => {
|
|
9507
9274
|
async function fetchData() {
|
|
9508
9275
|
const resolved = getResolvedApiKey();
|
|
9509
9276
|
if (!resolved?.key) {
|
|
@@ -9543,36 +9310,36 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9543
9310
|
}
|
|
9544
9311
|
fetchData();
|
|
9545
9312
|
}, [projectId]);
|
|
9546
|
-
return /* @__PURE__ */
|
|
9547
|
-
/* @__PURE__ */
|
|
9548
|
-
loading && /* @__PURE__ */
|
|
9549
|
-
error && /* @__PURE__ */
|
|
9550
|
-
!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: [
|
|
9551
9318
|
'Workflows are not enabled for project "',
|
|
9552
9319
|
projectName,
|
|
9553
9320
|
'".',
|
|
9554
9321
|
"\n",
|
|
9555
9322
|
"Enable them in the project settings at app.intlpull.com"
|
|
9556
9323
|
] }),
|
|
9557
|
-
!loading && !error && workflowsEnabled && /* @__PURE__ */
|
|
9558
|
-
/* @__PURE__ */
|
|
9559
|
-
|
|
9324
|
+
!loading && !error && workflowsEnabled && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
9325
|
+
/* @__PURE__ */ jsx33(
|
|
9326
|
+
Box30,
|
|
9560
9327
|
{
|
|
9561
9328
|
borderStyle: "round",
|
|
9562
9329
|
borderColor: colors.primary,
|
|
9563
9330
|
paddingX: 2,
|
|
9564
9331
|
paddingY: 1,
|
|
9565
9332
|
marginY: 1,
|
|
9566
|
-
children: /* @__PURE__ */
|
|
9567
|
-
/* @__PURE__ */
|
|
9568
|
-
/* @__PURE__ */
|
|
9569
|
-
/* @__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: [
|
|
9570
9337
|
" ",
|
|
9571
9338
|
icons.check,
|
|
9572
9339
|
" Workflows Enabled"
|
|
9573
9340
|
] })
|
|
9574
9341
|
] }),
|
|
9575
|
-
/* @__PURE__ */
|
|
9342
|
+
/* @__PURE__ */ jsx33(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9576
9343
|
icons.bullet,
|
|
9577
9344
|
" ",
|
|
9578
9345
|
workflows.length,
|
|
@@ -9580,14 +9347,14 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9580
9347
|
workflows.length !== 1 ? "s" : "",
|
|
9581
9348
|
" configured"
|
|
9582
9349
|
] }) }),
|
|
9583
|
-
/* @__PURE__ */
|
|
9350
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: pending.length > 0 ? colors.warning : colors.textMuted, children: [
|
|
9584
9351
|
icons.bullet,
|
|
9585
9352
|
" ",
|
|
9586
9353
|
pending.length,
|
|
9587
9354
|
" pending approval",
|
|
9588
9355
|
pending.length !== 1 ? "s" : ""
|
|
9589
9356
|
] }) }),
|
|
9590
|
-
/* @__PURE__ */
|
|
9357
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: locked.length > 0 ? colors.info : colors.textMuted, children: [
|
|
9591
9358
|
icons.bullet,
|
|
9592
9359
|
" ",
|
|
9593
9360
|
locked.length,
|
|
@@ -9597,15 +9364,15 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9597
9364
|
] })
|
|
9598
9365
|
}
|
|
9599
9366
|
),
|
|
9600
|
-
workflows.filter((w) => w.is_active).length > 0 && /* @__PURE__ */
|
|
9601
|
-
/* @__PURE__ */
|
|
9602
|
-
workflows.filter((w) => w.is_active).map((w) => /* @__PURE__ */
|
|
9603
|
-
/* @__PURE__ */
|
|
9604
|
-
/* @__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: [
|
|
9605
9372
|
i + 1,
|
|
9606
9373
|
". ",
|
|
9607
9374
|
stage.name,
|
|
9608
|
-
stage.required_role && /* @__PURE__ */
|
|
9375
|
+
stage.required_role && /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9609
9376
|
" (requires: ",
|
|
9610
9377
|
stage.required_role,
|
|
9611
9378
|
")"
|
|
@@ -9613,56 +9380,56 @@ function WorkflowStatusCommand({ projectId }) {
|
|
|
9613
9380
|
] }) }, stage.id)) })
|
|
9614
9381
|
] }, w.id))
|
|
9615
9382
|
] }),
|
|
9616
|
-
pending.length > 0 && /* @__PURE__ */
|
|
9617
|
-
/* @__PURE__ */
|
|
9383
|
+
pending.length > 0 && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 2, children: [
|
|
9384
|
+
/* @__PURE__ */ jsxs31(Text31, { bold: true, color: colors.warning, children: [
|
|
9618
9385
|
"Pending Approvals (",
|
|
9619
9386
|
pending.length,
|
|
9620
9387
|
")"
|
|
9621
9388
|
] }),
|
|
9622
|
-
pending.slice(0, 10).map((p) => /* @__PURE__ */
|
|
9623
|
-
/* @__PURE__ */
|
|
9624
|
-
/* @__PURE__ */
|
|
9625
|
-
/* @__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: [
|
|
9626
9393
|
" [",
|
|
9627
9394
|
p.language,
|
|
9628
9395
|
"]"
|
|
9629
9396
|
] })
|
|
9630
9397
|
] }),
|
|
9631
|
-
/* @__PURE__ */
|
|
9398
|
+
/* @__PURE__ */ jsx33(Box30, { marginLeft: 2, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9632
9399
|
"Stage: ",
|
|
9633
9400
|
p.stage_name,
|
|
9634
9401
|
p.required_role && ` (requires: ${p.required_role})`
|
|
9635
9402
|
] }) }),
|
|
9636
|
-
/* @__PURE__ */
|
|
9403
|
+
/* @__PURE__ */ jsx33(Box30, { marginLeft: 2, children: /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9637
9404
|
"ID: ",
|
|
9638
9405
|
p.translation_id
|
|
9639
9406
|
] }) })
|
|
9640
9407
|
] }, p.id)),
|
|
9641
|
-
pending.length > 10 && /* @__PURE__ */
|
|
9408
|
+
pending.length > 10 && /* @__PURE__ */ jsx33(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9642
9409
|
"... and ",
|
|
9643
9410
|
pending.length - 10,
|
|
9644
9411
|
" more"
|
|
9645
9412
|
] }) })
|
|
9646
9413
|
] }),
|
|
9647
|
-
pending.length === 0 && /* @__PURE__ */
|
|
9414
|
+
pending.length === 0 && /* @__PURE__ */ jsx33(Box30, { marginTop: 2, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.success, children: [
|
|
9648
9415
|
icons.check,
|
|
9649
9416
|
" No pending approvals"
|
|
9650
9417
|
] }) }),
|
|
9651
|
-
/* @__PURE__ */
|
|
9418
|
+
/* @__PURE__ */ jsx33(Box30, { marginTop: 2, children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9652
9419
|
icons.info,
|
|
9653
9420
|
" Use",
|
|
9654
9421
|
" ",
|
|
9655
|
-
/* @__PURE__ */
|
|
9422
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.primary, children: "npx @intlpullhq/cli workflow approve <id>" }),
|
|
9656
9423
|
" to approve"
|
|
9657
9424
|
] }) })
|
|
9658
9425
|
] })
|
|
9659
9426
|
] });
|
|
9660
9427
|
}
|
|
9661
9428
|
function WorkflowPendingCommand({ projectId }) {
|
|
9662
|
-
const [loading, setLoading] =
|
|
9663
|
-
const [error, setError] =
|
|
9664
|
-
const [pending, setPending] =
|
|
9665
|
-
|
|
9429
|
+
const [loading, setLoading] = useState20(true);
|
|
9430
|
+
const [error, setError] = useState20(null);
|
|
9431
|
+
const [pending, setPending] = useState20([]);
|
|
9432
|
+
useEffect17(() => {
|
|
9666
9433
|
async function fetchData() {
|
|
9667
9434
|
const resolved = getResolvedApiKey();
|
|
9668
9435
|
if (!resolved?.key) {
|
|
@@ -9689,19 +9456,19 @@ function WorkflowPendingCommand({ projectId }) {
|
|
|
9689
9456
|
}
|
|
9690
9457
|
fetchData();
|
|
9691
9458
|
}, [projectId]);
|
|
9692
|
-
return /* @__PURE__ */
|
|
9693
|
-
/* @__PURE__ */
|
|
9694
|
-
loading && /* @__PURE__ */
|
|
9695
|
-
error && /* @__PURE__ */
|
|
9696
|
-
!loading && !error && pending.length === 0 && /* @__PURE__ */
|
|
9697
|
-
!loading && !error && pending.length > 0 && /* @__PURE__ */
|
|
9698
|
-
/* @__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: [
|
|
9699
9466
|
pending.length,
|
|
9700
9467
|
" Pending Approval",
|
|
9701
9468
|
pending.length !== 1 ? "s" : ""
|
|
9702
9469
|
] }) }),
|
|
9703
|
-
pending.map((p) => /* @__PURE__ */
|
|
9704
|
-
|
|
9470
|
+
pending.map((p) => /* @__PURE__ */ jsxs31(
|
|
9471
|
+
Box30,
|
|
9705
9472
|
{
|
|
9706
9473
|
borderStyle: "single",
|
|
9707
9474
|
borderColor: colors.textDim,
|
|
@@ -9710,33 +9477,33 @@ function WorkflowPendingCommand({ projectId }) {
|
|
|
9710
9477
|
marginBottom: 1,
|
|
9711
9478
|
flexDirection: "column",
|
|
9712
9479
|
children: [
|
|
9713
|
-
/* @__PURE__ */
|
|
9714
|
-
/* @__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: [
|
|
9715
9482
|
"Language: ",
|
|
9716
|
-
/* @__PURE__ */
|
|
9483
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.info, children: p.language }),
|
|
9717
9484
|
" | ",
|
|
9718
9485
|
"Namespace: ",
|
|
9719
|
-
/* @__PURE__ */
|
|
9486
|
+
/* @__PURE__ */ jsx33(Text31, { color: colors.info, children: p.namespace })
|
|
9720
9487
|
] }) }),
|
|
9721
|
-
/* @__PURE__ */
|
|
9488
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9722
9489
|
"Stage: ",
|
|
9723
|
-
/* @__PURE__ */
|
|
9724
|
-
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: [
|
|
9725
9492
|
" (requires: ",
|
|
9726
9493
|
p.required_role,
|
|
9727
9494
|
")"
|
|
9728
9495
|
] })
|
|
9729
9496
|
] }) }),
|
|
9730
|
-
/* @__PURE__ */
|
|
9497
|
+
/* @__PURE__ */ jsx33(Box30, { children: /* @__PURE__ */ jsxs31(Text31, { color: colors.textMuted, children: [
|
|
9731
9498
|
"Value: ",
|
|
9732
|
-
/* @__PURE__ */
|
|
9499
|
+
/* @__PURE__ */ jsxs31(Text31, { color: colors.text, children: [
|
|
9733
9500
|
'"',
|
|
9734
9501
|
p.value.substring(0, 50),
|
|
9735
9502
|
p.value.length > 50 ? "..." : "",
|
|
9736
9503
|
'"'
|
|
9737
9504
|
] })
|
|
9738
9505
|
] }) }),
|
|
9739
|
-
/* @__PURE__ */
|
|
9506
|
+
/* @__PURE__ */ jsx33(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { dimColor: true, children: [
|
|
9740
9507
|
"ID: ",
|
|
9741
9508
|
p.translation_id
|
|
9742
9509
|
] }) })
|
|
@@ -9767,11 +9534,11 @@ The translation is locked and cannot be modified.`;
|
|
|
9767
9534
|
return errorMessage;
|
|
9768
9535
|
}
|
|
9769
9536
|
function ApproveCommand({ projectId, translationId, comment }) {
|
|
9770
|
-
const [loading, setLoading] =
|
|
9771
|
-
const [error, setError] =
|
|
9772
|
-
const [success, setSuccess] =
|
|
9773
|
-
const [stageName, setStageName] =
|
|
9774
|
-
|
|
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(() => {
|
|
9775
9542
|
async function approve() {
|
|
9776
9543
|
const resolved = getResolvedApiKey();
|
|
9777
9544
|
if (!resolved?.key) {
|
|
@@ -9800,15 +9567,15 @@ function ApproveCommand({ projectId, translationId, comment }) {
|
|
|
9800
9567
|
}
|
|
9801
9568
|
approve();
|
|
9802
9569
|
}, [projectId, translationId, comment]);
|
|
9803
|
-
return /* @__PURE__ */
|
|
9804
|
-
/* @__PURE__ */
|
|
9805
|
-
loading && /* @__PURE__ */
|
|
9806
|
-
error && /* @__PURE__ */
|
|
9807
|
-
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: [
|
|
9808
9575
|
'Translation approved at stage "',
|
|
9809
9576
|
stageName,
|
|
9810
9577
|
'"',
|
|
9811
|
-
comment && /* @__PURE__ */
|
|
9578
|
+
comment && /* @__PURE__ */ jsxs31(Fragment5, { children: [
|
|
9812
9579
|
"\n",
|
|
9813
9580
|
"Comment: ",
|
|
9814
9581
|
comment
|
|
@@ -9817,10 +9584,10 @@ function ApproveCommand({ projectId, translationId, comment }) {
|
|
|
9817
9584
|
] });
|
|
9818
9585
|
}
|
|
9819
9586
|
function RejectCommand({ projectId, translationId, reason }) {
|
|
9820
|
-
const [loading, setLoading] =
|
|
9821
|
-
const [error, setError] =
|
|
9822
|
-
const [success, setSuccess] =
|
|
9823
|
-
|
|
9587
|
+
const [loading, setLoading] = useState20(true);
|
|
9588
|
+
const [error, setError] = useState20(null);
|
|
9589
|
+
const [success, setSuccess] = useState20(false);
|
|
9590
|
+
useEffect17(() => {
|
|
9824
9591
|
async function reject() {
|
|
9825
9592
|
const resolved = getResolvedApiKey();
|
|
9826
9593
|
if (!resolved?.key) {
|
|
@@ -9848,11 +9615,11 @@ function RejectCommand({ projectId, translationId, reason }) {
|
|
|
9848
9615
|
}
|
|
9849
9616
|
reject();
|
|
9850
9617
|
}, [projectId, translationId, reason]);
|
|
9851
|
-
return /* @__PURE__ */
|
|
9852
|
-
/* @__PURE__ */
|
|
9853
|
-
loading && /* @__PURE__ */
|
|
9854
|
-
error && /* @__PURE__ */
|
|
9855
|
-
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: [
|
|
9856
9623
|
'Translation rejected with reason: "',
|
|
9857
9624
|
reason,
|
|
9858
9625
|
'"'
|
|
@@ -9860,16 +9627,16 @@ function RejectCommand({ projectId, translationId, reason }) {
|
|
|
9860
9627
|
] });
|
|
9861
9628
|
}
|
|
9862
9629
|
function runWorkflowStatus(options) {
|
|
9863
|
-
|
|
9630
|
+
render16(/* @__PURE__ */ jsx33(WorkflowStatusCommand, { projectId: options.project }));
|
|
9864
9631
|
}
|
|
9865
9632
|
function runWorkflowPending(options) {
|
|
9866
|
-
|
|
9633
|
+
render16(/* @__PURE__ */ jsx33(WorkflowPendingCommand, { projectId: options.project }));
|
|
9867
9634
|
}
|
|
9868
9635
|
function runWorkflowApprove(translationId, options) {
|
|
9869
|
-
|
|
9636
|
+
render16(/* @__PURE__ */ jsx33(ApproveCommand, { projectId: options.project, translationId, comment: options.comment }));
|
|
9870
9637
|
}
|
|
9871
9638
|
function runWorkflowReject(translationId, options) {
|
|
9872
|
-
|
|
9639
|
+
render16(/* @__PURE__ */ jsx33(RejectCommand, { projectId: options.project, translationId, reason: options.reason }));
|
|
9873
9640
|
}
|
|
9874
9641
|
|
|
9875
9642
|
// src/commands/emails.tsx
|
|
@@ -10128,9 +9895,9 @@ async function runEmailsStatus(options) {
|
|
|
10128
9895
|
}
|
|
10129
9896
|
|
|
10130
9897
|
// src/commands/zendesk.tsx
|
|
10131
|
-
import { useState as
|
|
10132
|
-
import { render as
|
|
10133
|
-
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";
|
|
10134
9901
|
async function fetchZendeskIntegration(apiUrl, apiKey, projectId) {
|
|
10135
9902
|
const response = await fetch(`${apiUrl}/api/v1/projects/${projectId}/integrations/zendesk`, {
|
|
10136
9903
|
headers: { Accept: "application/json", "X-API-Key": apiKey }
|
|
@@ -10201,11 +9968,11 @@ async function disconnectZendesk(apiUrl, apiKey, projectId) {
|
|
|
10201
9968
|
if (!response.ok) throw new Error(`Failed to disconnect: ${response.status}`);
|
|
10202
9969
|
}
|
|
10203
9970
|
function ZendeskStatusCommand() {
|
|
10204
|
-
const [loading, setLoading] =
|
|
10205
|
-
const [integration, setIntegration] =
|
|
10206
|
-
const [status, setStatus] =
|
|
10207
|
-
const [error, setError] =
|
|
10208
|
-
|
|
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(() => {
|
|
10209
9976
|
async function fetch2() {
|
|
10210
9977
|
const resolved = getResolvedApiKey();
|
|
10211
9978
|
if (!resolved?.key) {
|
|
@@ -10237,54 +10004,54 @@ function ZendeskStatusCommand() {
|
|
|
10237
10004
|
}
|
|
10238
10005
|
fetch2();
|
|
10239
10006
|
}, []);
|
|
10240
|
-
return /* @__PURE__ */
|
|
10241
|
-
/* @__PURE__ */
|
|
10242
|
-
loading && /* @__PURE__ */
|
|
10243
|
-
error && /* @__PURE__ */
|
|
10244
|
-
integration && !integration.connected && /* @__PURE__ */
|
|
10245
|
-
integration?.connected && integration.integration && /* @__PURE__ */
|
|
10246
|
-
/* @__PURE__ */
|
|
10247
|
-
/* @__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: [
|
|
10248
10015
|
icons.success,
|
|
10249
10016
|
" Connected to Zendesk"
|
|
10250
10017
|
] }),
|
|
10251
|
-
/* @__PURE__ */
|
|
10018
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10252
10019
|
"Subdomain: ",
|
|
10253
10020
|
integration.integration.subdomain,
|
|
10254
10021
|
".zendesk.com"
|
|
10255
10022
|
] }),
|
|
10256
|
-
/* @__PURE__ */
|
|
10023
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10257
10024
|
"Source Locale: ",
|
|
10258
10025
|
integration.integration.source_locale
|
|
10259
10026
|
] }),
|
|
10260
|
-
integration.integration.last_sync_at && /* @__PURE__ */
|
|
10027
|
+
integration.integration.last_sync_at && /* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10261
10028
|
"Last Sync: ",
|
|
10262
10029
|
new Date(integration.integration.last_sync_at).toLocaleString()
|
|
10263
10030
|
] })
|
|
10264
10031
|
] }) }),
|
|
10265
|
-
status && /* @__PURE__ */
|
|
10266
|
-
/* @__PURE__ */
|
|
10267
|
-
/* @__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: [
|
|
10268
10035
|
icons.bullet,
|
|
10269
10036
|
" ",
|
|
10270
10037
|
status.total_articles,
|
|
10271
10038
|
" articles"
|
|
10272
10039
|
] }),
|
|
10273
|
-
/* @__PURE__ */
|
|
10040
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10274
10041
|
icons.bullet,
|
|
10275
10042
|
" ",
|
|
10276
10043
|
status.total_categories,
|
|
10277
10044
|
" categories"
|
|
10278
10045
|
] }),
|
|
10279
|
-
/* @__PURE__ */
|
|
10046
|
+
/* @__PURE__ */ jsxs32(Text32, { color: colors.textMuted, children: [
|
|
10280
10047
|
icons.bullet,
|
|
10281
10048
|
" ",
|
|
10282
10049
|
status.total_sections,
|
|
10283
10050
|
" sections"
|
|
10284
10051
|
] }),
|
|
10285
|
-
status.target_locales.length > 0 && /* @__PURE__ */
|
|
10286
|
-
/* @__PURE__ */
|
|
10287
|
-
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: [
|
|
10288
10055
|
icons.bullet,
|
|
10289
10056
|
" ",
|
|
10290
10057
|
locale,
|
|
@@ -10300,10 +10067,10 @@ function ZendeskStatusCommand() {
|
|
|
10300
10067
|
] });
|
|
10301
10068
|
}
|
|
10302
10069
|
function ZendeskConnectCommand({ subdomain, email, token }) {
|
|
10303
|
-
const [loading, setLoading] =
|
|
10304
|
-
const [result, setResult] =
|
|
10305
|
-
const [error, setError] =
|
|
10306
|
-
|
|
10070
|
+
const [loading, setLoading] = useState21(true);
|
|
10071
|
+
const [result, setResult] = useState21(null);
|
|
10072
|
+
const [error, setError] = useState21(null);
|
|
10073
|
+
useEffect18(() => {
|
|
10307
10074
|
async function connect() {
|
|
10308
10075
|
const resolved = getResolvedApiKey();
|
|
10309
10076
|
if (!resolved?.key) {
|
|
@@ -10331,11 +10098,11 @@ function ZendeskConnectCommand({ subdomain, email, token }) {
|
|
|
10331
10098
|
}
|
|
10332
10099
|
connect();
|
|
10333
10100
|
}, [subdomain, email, token]);
|
|
10334
|
-
return /* @__PURE__ */
|
|
10335
|
-
/* @__PURE__ */
|
|
10336
|
-
loading && /* @__PURE__ */
|
|
10337
|
-
error && /* @__PURE__ */
|
|
10338
|
-
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: [
|
|
10339
10106
|
"Successfully connected to ",
|
|
10340
10107
|
subdomain,
|
|
10341
10108
|
".zendesk.com",
|
|
@@ -10345,10 +10112,10 @@ function ZendeskConnectCommand({ subdomain, email, token }) {
|
|
|
10345
10112
|
] });
|
|
10346
10113
|
}
|
|
10347
10114
|
function ZendeskSyncCommand({ direction, locale, excludeDrafts, autoTranslate, translateProvider }) {
|
|
10348
|
-
const [loading, setLoading] =
|
|
10349
|
-
const [result, setResult] =
|
|
10350
|
-
const [error, setError] =
|
|
10351
|
-
|
|
10115
|
+
const [loading, setLoading] = useState21(true);
|
|
10116
|
+
const [result, setResult] = useState21(null);
|
|
10117
|
+
const [error, setError] = useState21(null);
|
|
10118
|
+
useEffect18(() => {
|
|
10352
10119
|
async function sync() {
|
|
10353
10120
|
const resolved = getResolvedApiKey();
|
|
10354
10121
|
if (!resolved?.key) {
|
|
@@ -10390,11 +10157,11 @@ function ZendeskSyncCommand({ direction, locale, excludeDrafts, autoTranslate, t
|
|
|
10390
10157
|
}
|
|
10391
10158
|
sync();
|
|
10392
10159
|
}, [direction, locale, excludeDrafts, autoTranslate, translateProvider]);
|
|
10393
|
-
return /* @__PURE__ */
|
|
10394
|
-
/* @__PURE__ */
|
|
10395
|
-
loading && /* @__PURE__ */
|
|
10396
|
-
error && /* @__PURE__ */
|
|
10397
|
-
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: [
|
|
10398
10165
|
"Imported ",
|
|
10399
10166
|
result.articles_synced,
|
|
10400
10167
|
" articles, ",
|
|
@@ -10407,24 +10174,24 @@ function ZendeskSyncCommand({ direction, locale, excludeDrafts, autoTranslate, t
|
|
|
10407
10174
|
result.keys_created,
|
|
10408
10175
|
", updated: ",
|
|
10409
10176
|
result.keys_updated,
|
|
10410
|
-
result.auto_translate_job && /* @__PURE__ */
|
|
10177
|
+
result.auto_translate_job && /* @__PURE__ */ jsxs32(Fragment6, { children: [
|
|
10411
10178
|
"\n",
|
|
10412
10179
|
"Auto-translation started: ",
|
|
10413
10180
|
result.auto_translate_job
|
|
10414
10181
|
] })
|
|
10415
|
-
] }) : /* @__PURE__ */
|
|
10182
|
+
] }) : /* @__PURE__ */ jsxs32(Fragment6, { children: [
|
|
10416
10183
|
"Pushed translations for ",
|
|
10417
10184
|
result.articles_synced,
|
|
10418
10185
|
" articles to Zendesk"
|
|
10419
10186
|
] }) }),
|
|
10420
|
-
result && !result.success && /* @__PURE__ */
|
|
10187
|
+
result && !result.success && /* @__PURE__ */ jsx34(Alert, { type: "error", title: "Sync Failed", children: result.error || "Unknown error" })
|
|
10421
10188
|
] });
|
|
10422
10189
|
}
|
|
10423
10190
|
function ZendeskDisconnectCommand() {
|
|
10424
|
-
const [loading, setLoading] =
|
|
10425
|
-
const [success, setSuccess] =
|
|
10426
|
-
const [error, setError] =
|
|
10427
|
-
|
|
10191
|
+
const [loading, setLoading] = useState21(true);
|
|
10192
|
+
const [success, setSuccess] = useState21(false);
|
|
10193
|
+
const [error, setError] = useState21(null);
|
|
10194
|
+
useEffect18(() => {
|
|
10428
10195
|
async function disconnect() {
|
|
10429
10196
|
const resolved = getResolvedApiKey();
|
|
10430
10197
|
if (!resolved?.key) {
|
|
@@ -10452,22 +10219,22 @@ function ZendeskDisconnectCommand() {
|
|
|
10452
10219
|
}
|
|
10453
10220
|
disconnect();
|
|
10454
10221
|
}, []);
|
|
10455
|
-
return /* @__PURE__ */
|
|
10456
|
-
/* @__PURE__ */
|
|
10457
|
-
loading && /* @__PURE__ */
|
|
10458
|
-
error && /* @__PURE__ */
|
|
10459
|
-
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." })
|
|
10460
10227
|
] });
|
|
10461
10228
|
}
|
|
10462
10229
|
function runZendeskStatus() {
|
|
10463
|
-
|
|
10230
|
+
render17(/* @__PURE__ */ jsx34(ZendeskStatusCommand, {}));
|
|
10464
10231
|
}
|
|
10465
10232
|
function runZendeskConnect(options) {
|
|
10466
10233
|
if (!options.subdomain || !options.email || !options.token) {
|
|
10467
|
-
|
|
10468
|
-
/* @__PURE__ */
|
|
10469
|
-
/* @__PURE__ */
|
|
10470
|
-
/* @__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: [
|
|
10471
10238
|
"Required: --subdomain, --email, --token",
|
|
10472
10239
|
"\n",
|
|
10473
10240
|
"Example: npx @intlpullhq/cli zendesk connect --subdomain mycompany --email admin@example.com --token YOUR_API_TOKEN"
|
|
@@ -10476,12 +10243,12 @@ function runZendeskConnect(options) {
|
|
|
10476
10243
|
);
|
|
10477
10244
|
return;
|
|
10478
10245
|
}
|
|
10479
|
-
|
|
10246
|
+
render17(/* @__PURE__ */ jsx34(ZendeskConnectCommand, { subdomain: options.subdomain, email: options.email, token: options.token }));
|
|
10480
10247
|
}
|
|
10481
10248
|
function runZendeskSync(options) {
|
|
10482
10249
|
const direction = options.direction || "pull";
|
|
10483
|
-
|
|
10484
|
-
/* @__PURE__ */
|
|
10250
|
+
render17(
|
|
10251
|
+
/* @__PURE__ */ jsx34(
|
|
10485
10252
|
ZendeskSyncCommand,
|
|
10486
10253
|
{
|
|
10487
10254
|
direction,
|
|
@@ -10494,17 +10261,17 @@ function runZendeskSync(options) {
|
|
|
10494
10261
|
);
|
|
10495
10262
|
}
|
|
10496
10263
|
function runZendeskDisconnect() {
|
|
10497
|
-
|
|
10264
|
+
render17(/* @__PURE__ */ jsx34(ZendeskDisconnectCommand, {}));
|
|
10498
10265
|
}
|
|
10499
10266
|
|
|
10500
10267
|
// src/commands/documents/index.tsx
|
|
10501
10268
|
import { Command } from "commander";
|
|
10502
10269
|
|
|
10503
10270
|
// src/commands/documents/list.tsx
|
|
10504
|
-
import { useState as
|
|
10505
|
-
import { render as
|
|
10506
|
-
import
|
|
10507
|
-
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";
|
|
10508
10275
|
function SimpleTable({ data }) {
|
|
10509
10276
|
if (data.length === 0) return null;
|
|
10510
10277
|
const headers = Object.keys(data[0]);
|
|
@@ -10512,27 +10279,27 @@ function SimpleTable({ data }) {
|
|
|
10512
10279
|
(h) => Math.max(h.length, ...data.map((row) => String(row[h] || "").length))
|
|
10513
10280
|
);
|
|
10514
10281
|
const separator = "\u2500".repeat(colWidths.reduce((a, b) => a + b + 3, 1));
|
|
10515
|
-
return /* @__PURE__ */
|
|
10516
|
-
/* @__PURE__ */
|
|
10517
|
-
/* @__PURE__ */
|
|
10282
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
10283
|
+
/* @__PURE__ */ jsx35(Text33, { children: separator }),
|
|
10284
|
+
/* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10518
10285
|
"\u2502 ",
|
|
10519
10286
|
headers.map((h, i) => h.padEnd(colWidths[i])).join(" \u2502 "),
|
|
10520
10287
|
" \u2502"
|
|
10521
10288
|
] }),
|
|
10522
|
-
/* @__PURE__ */
|
|
10523
|
-
data.map((row, rowIdx) => /* @__PURE__ */
|
|
10289
|
+
/* @__PURE__ */ jsx35(Text33, { children: separator }),
|
|
10290
|
+
data.map((row, rowIdx) => /* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10524
10291
|
"\u2502 ",
|
|
10525
10292
|
headers.map((h, i) => String(row[h] || "").padEnd(colWidths[i])).join(" \u2502 "),
|
|
10526
10293
|
" \u2502"
|
|
10527
10294
|
] }, rowIdx)),
|
|
10528
|
-
/* @__PURE__ */
|
|
10295
|
+
/* @__PURE__ */ jsx35(Text33, { children: separator })
|
|
10529
10296
|
] });
|
|
10530
10297
|
}
|
|
10531
10298
|
var DocumentsList = ({ project }) => {
|
|
10532
|
-
const [documents, setDocuments] =
|
|
10533
|
-
const [isLoading, setIsLoading] =
|
|
10534
|
-
const [error, setError] =
|
|
10535
|
-
|
|
10299
|
+
const [documents, setDocuments] = useState22([]);
|
|
10300
|
+
const [isLoading, setIsLoading] = useState22(true);
|
|
10301
|
+
const [error, setError] = useState22(null);
|
|
10302
|
+
useEffect19(() => {
|
|
10536
10303
|
const fetchDocuments = async () => {
|
|
10537
10304
|
try {
|
|
10538
10305
|
const config = getProjectConfig();
|
|
@@ -10554,19 +10321,19 @@ var DocumentsList = ({ project }) => {
|
|
|
10554
10321
|
fetchDocuments();
|
|
10555
10322
|
}, [project]);
|
|
10556
10323
|
if (error) {
|
|
10557
|
-
return /* @__PURE__ */
|
|
10324
|
+
return /* @__PURE__ */ jsxs33(Text33, { color: "red", children: [
|
|
10558
10325
|
"Error: ",
|
|
10559
10326
|
error
|
|
10560
10327
|
] });
|
|
10561
10328
|
}
|
|
10562
10329
|
if (isLoading) {
|
|
10563
|
-
return /* @__PURE__ */
|
|
10564
|
-
/* @__PURE__ */
|
|
10330
|
+
return /* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10331
|
+
/* @__PURE__ */ jsx35(Text33, { color: "green", children: /* @__PURE__ */ jsx35(Spinner15, { type: "dots" }) }),
|
|
10565
10332
|
" Loading documents..."
|
|
10566
10333
|
] });
|
|
10567
10334
|
}
|
|
10568
10335
|
if (documents.length === 0) {
|
|
10569
|
-
return /* @__PURE__ */
|
|
10336
|
+
return /* @__PURE__ */ jsx35(Text33, { children: "No documents found." });
|
|
10570
10337
|
}
|
|
10571
10338
|
const data = documents.map((doc) => ({
|
|
10572
10339
|
ID: doc.id.substring(0, 8) + "...",
|
|
@@ -10574,30 +10341,30 @@ var DocumentsList = ({ project }) => {
|
|
|
10574
10341
|
Status: doc.status,
|
|
10575
10342
|
Created: new Date(doc.created_at).toLocaleDateString()
|
|
10576
10343
|
}));
|
|
10577
|
-
return /* @__PURE__ */
|
|
10578
|
-
/* @__PURE__ */
|
|
10344
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
10345
|
+
/* @__PURE__ */ jsxs33(Text33, { children: [
|
|
10579
10346
|
"Target Project: ",
|
|
10580
10347
|
project || "Current"
|
|
10581
10348
|
] }),
|
|
10582
|
-
/* @__PURE__ */
|
|
10583
|
-
/* @__PURE__ */
|
|
10349
|
+
/* @__PURE__ */ jsx35(Newline, {}),
|
|
10350
|
+
/* @__PURE__ */ jsx35(SimpleTable, { data })
|
|
10584
10351
|
] });
|
|
10585
10352
|
};
|
|
10586
10353
|
function runDocumentsList(options) {
|
|
10587
|
-
|
|
10354
|
+
render18(/* @__PURE__ */ jsx35(DocumentsList, { ...options }));
|
|
10588
10355
|
}
|
|
10589
10356
|
|
|
10590
10357
|
// src/commands/documents/upload.tsx
|
|
10591
|
-
import { useState as
|
|
10592
|
-
import { render as
|
|
10593
|
-
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";
|
|
10594
10361
|
import fs2 from "fs";
|
|
10595
10362
|
import path2 from "path";
|
|
10596
|
-
import { jsx as
|
|
10363
|
+
import { jsx as jsx36, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
10597
10364
|
var DocumentsUpload = ({ file, project, source, target }) => {
|
|
10598
|
-
const [status, setStatus] =
|
|
10599
|
-
const [message, setMessage] =
|
|
10600
|
-
|
|
10365
|
+
const [status, setStatus] = useState23("uploading");
|
|
10366
|
+
const [message, setMessage] = useState23("Uploading document...");
|
|
10367
|
+
useEffect20(() => {
|
|
10601
10368
|
const upload = async () => {
|
|
10602
10369
|
try {
|
|
10603
10370
|
const config = getProjectConfig();
|
|
@@ -10628,38 +10395,38 @@ var DocumentsUpload = ({ file, project, source, target }) => {
|
|
|
10628
10395
|
upload();
|
|
10629
10396
|
}, [file, project, source, target]);
|
|
10630
10397
|
if (status === "error") {
|
|
10631
|
-
return /* @__PURE__ */
|
|
10398
|
+
return /* @__PURE__ */ jsxs34(Text34, { color: "red", children: [
|
|
10632
10399
|
"Error: ",
|
|
10633
10400
|
message
|
|
10634
10401
|
] });
|
|
10635
10402
|
}
|
|
10636
10403
|
if (status === "success") {
|
|
10637
|
-
return /* @__PURE__ */
|
|
10404
|
+
return /* @__PURE__ */ jsxs34(Text34, { color: "green", children: [
|
|
10638
10405
|
"\u2713 ",
|
|
10639
10406
|
message
|
|
10640
10407
|
] });
|
|
10641
10408
|
}
|
|
10642
|
-
return /* @__PURE__ */
|
|
10643
|
-
/* @__PURE__ */
|
|
10409
|
+
return /* @__PURE__ */ jsxs34(Text34, { children: [
|
|
10410
|
+
/* @__PURE__ */ jsx36(Text34, { color: "green", children: /* @__PURE__ */ jsx36(Spinner16, { type: "dots" }) }),
|
|
10644
10411
|
" ",
|
|
10645
10412
|
message
|
|
10646
10413
|
] });
|
|
10647
10414
|
};
|
|
10648
10415
|
function runDocumentsUpload(options) {
|
|
10649
|
-
|
|
10416
|
+
render19(/* @__PURE__ */ jsx36(DocumentsUpload, { ...options }));
|
|
10650
10417
|
}
|
|
10651
10418
|
|
|
10652
10419
|
// src/commands/documents/download.tsx
|
|
10653
|
-
import { useState as
|
|
10654
|
-
import { render as
|
|
10655
|
-
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";
|
|
10656
10423
|
import fs3 from "fs";
|
|
10657
10424
|
import path3 from "path";
|
|
10658
|
-
import { jsx as
|
|
10425
|
+
import { jsx as jsx37, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
10659
10426
|
var DocumentsDownload = ({ documentId, language, project, output }) => {
|
|
10660
|
-
const [status, setStatus] =
|
|
10661
|
-
const [message, setMessage] =
|
|
10662
|
-
|
|
10427
|
+
const [status, setStatus] = useState24("downloading");
|
|
10428
|
+
const [message, setMessage] = useState24("Downloading document...");
|
|
10429
|
+
useEffect21(() => {
|
|
10663
10430
|
const download = async () => {
|
|
10664
10431
|
try {
|
|
10665
10432
|
const config = getProjectConfig();
|
|
@@ -10689,25 +10456,25 @@ var DocumentsDownload = ({ documentId, language, project, output }) => {
|
|
|
10689
10456
|
download();
|
|
10690
10457
|
}, [documentId, language, project, output]);
|
|
10691
10458
|
if (status === "error") {
|
|
10692
|
-
return /* @__PURE__ */
|
|
10459
|
+
return /* @__PURE__ */ jsxs35(Text35, { color: "red", children: [
|
|
10693
10460
|
"Error: ",
|
|
10694
10461
|
message
|
|
10695
10462
|
] });
|
|
10696
10463
|
}
|
|
10697
10464
|
if (status === "success") {
|
|
10698
|
-
return /* @__PURE__ */
|
|
10465
|
+
return /* @__PURE__ */ jsxs35(Text35, { color: "green", children: [
|
|
10699
10466
|
"\u2713 ",
|
|
10700
10467
|
message
|
|
10701
10468
|
] });
|
|
10702
10469
|
}
|
|
10703
|
-
return /* @__PURE__ */
|
|
10704
|
-
/* @__PURE__ */
|
|
10470
|
+
return /* @__PURE__ */ jsxs35(Text35, { children: [
|
|
10471
|
+
/* @__PURE__ */ jsx37(Text35, { color: "green", children: /* @__PURE__ */ jsx37(Spinner17, { type: "dots" }) }),
|
|
10705
10472
|
" ",
|
|
10706
10473
|
message
|
|
10707
10474
|
] });
|
|
10708
10475
|
};
|
|
10709
10476
|
function runDocumentsDownload(options) {
|
|
10710
|
-
|
|
10477
|
+
render20(/* @__PURE__ */ jsx37(DocumentsDownload, { ...options }));
|
|
10711
10478
|
}
|
|
10712
10479
|
|
|
10713
10480
|
// src/commands/documents/index.tsx
|
|
@@ -10915,15 +10682,6 @@ migrate.command("from <provider>").description("Migrate from Lokalise, Crowdin,
|
|
|
10915
10682
|
dryRun: options.dryRun
|
|
10916
10683
|
});
|
|
10917
10684
|
});
|
|
10918
|
-
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) => {
|
|
10919
|
-
runCompare({
|
|
10920
|
-
from: options.from,
|
|
10921
|
-
keys: parseInt(options.keys, 10),
|
|
10922
|
-
languages: parseInt(options.languages, 10),
|
|
10923
|
-
users: parseInt(options.users, 10),
|
|
10924
|
-
json: options.json
|
|
10925
|
-
});
|
|
10926
|
-
});
|
|
10927
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) => {
|
|
10928
10686
|
runCheck({
|
|
10929
10687
|
source: options.source,
|