@radicalstorage-lingo/cli 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli.cjs +94 -16
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -52,6 +52,12 @@ npx lingo pull
|
|
|
52
52
|
|
|
53
53
|
Untranslated (missing, empty) values are never written — a key is either
|
|
54
54
|
pulled with real text or omitted entirely.
|
|
55
|
+
- `split` — `none` (default): `{outDir}/{lang}.json`. `namespace`:
|
|
56
|
+
`{outDir}/{lang}/{namespace}.json` per top-level namespace, keys relative to
|
|
57
|
+
it, `_root.json` for keys without one (next-i18next's `public/locales`
|
|
58
|
+
layout).
|
|
59
|
+
- `languageMap` — file/folder names instead of Lingo's codes, e.g.
|
|
60
|
+
`{ "zh-CN": "zh_cn", "pt-PT": "pt" }`.
|
|
55
61
|
|
|
56
62
|
## Commands
|
|
57
63
|
|
package/dist/cli.cjs
CHANGED
|
@@ -21067,6 +21067,31 @@ function getCodec(id) {
|
|
|
21067
21067
|
}
|
|
21068
21068
|
var FORMAT_LABELS = Object.fromEntries(codecs.map((c2) => [c2.id, c2.label]));
|
|
21069
21069
|
|
|
21070
|
+
// ../formats/src/util/bundle.ts
|
|
21071
|
+
var ROOT_NAMESPACE = "_root";
|
|
21072
|
+
function bundleMessages(rows, namespace) {
|
|
21073
|
+
const out = {};
|
|
21074
|
+
for (const row of rows) {
|
|
21075
|
+
if (row.value.trim().length === 0) continue;
|
|
21076
|
+
const path = row.namespacePath ?? null;
|
|
21077
|
+
if (namespace === void 0) {
|
|
21078
|
+
out[path ? `${path}.${row.key}` : row.key] = row.value;
|
|
21079
|
+
} else if (namespace === ROOT_NAMESPACE) {
|
|
21080
|
+
if (!path) out[row.key] = row.value;
|
|
21081
|
+
} else if (path === namespace) {
|
|
21082
|
+
out[row.key] = row.value;
|
|
21083
|
+
} else if (path?.startsWith(`${namespace}.`)) {
|
|
21084
|
+
out[`${path.slice(namespace.length + 1)}.${row.key}`] = row.value;
|
|
21085
|
+
}
|
|
21086
|
+
}
|
|
21087
|
+
return out;
|
|
21088
|
+
}
|
|
21089
|
+
function topLevelNamespaces(rows) {
|
|
21090
|
+
const names = /* @__PURE__ */ new Set();
|
|
21091
|
+
for (const row of rows) names.add(row.namespacePath ? row.namespacePath.split(".")[0] : ROOT_NAMESPACE);
|
|
21092
|
+
return [...names].sort();
|
|
21093
|
+
}
|
|
21094
|
+
|
|
21070
21095
|
// src/api.ts
|
|
21071
21096
|
var import_node_fs = require("fs");
|
|
21072
21097
|
var import_node_os = require("os");
|
|
@@ -25268,8 +25293,19 @@ var configSchema = external_exports.object({
|
|
|
25268
25293
|
languages: external_exports.array(external_exports.string().min(2)).default([]),
|
|
25269
25294
|
/** Format the pulled files are written in. */
|
|
25270
25295
|
format: external_exports.enum(["json-nested", "json-flat"]).default("json-nested"),
|
|
25271
|
-
/** Directory for pulled bundles
|
|
25296
|
+
/** Directory for pulled bundles. */
|
|
25272
25297
|
outDir: external_exports.string().default("messages"),
|
|
25298
|
+
/**
|
|
25299
|
+
* File layout of `lingo pull`:
|
|
25300
|
+
* - `none` (default): `{outDir}/{lang}.json`, every key under its full path;
|
|
25301
|
+
* - `namespace`: `{outDir}/{lang}/{namespace}.json` per top-level namespace,
|
|
25302
|
+
* keys relative to it (`_root.json` for keys without one), the layout of
|
|
25303
|
+
* next-i18next's `public/locales` and i18next namespace loading. Same content
|
|
25304
|
+
* as the API's `/bundles/{lang}/{namespace}`.
|
|
25305
|
+
*/
|
|
25306
|
+
split: external_exports.enum(["none", "namespace"]).default("none"),
|
|
25307
|
+
/** Names to use for language files/folders instead of Lingo's codes, e.g. `{ "zh-CN": "zh_cn", "pt-PT": "pt" }`. */
|
|
25308
|
+
languageMap: external_exports.record(external_exports.string()).default({}),
|
|
25273
25309
|
/** Where the generated types module is written. */
|
|
25274
25310
|
typesOut: external_exports.string().default(".lingo/types.ts"),
|
|
25275
25311
|
/**
|
|
@@ -25289,7 +25325,7 @@ function parseConfig(raw) {
|
|
|
25289
25325
|
}
|
|
25290
25326
|
function bundlePath(config, language) {
|
|
25291
25327
|
const ext = config.format === "json-flat" || config.format === "json-nested" ? ".json" : ".json";
|
|
25292
|
-
return `${config.outDir}/${language}${ext}`;
|
|
25328
|
+
return `${config.outDir}/${config.languageMap[language] ?? language}${ext}`;
|
|
25293
25329
|
}
|
|
25294
25330
|
function resolveOutDir(flagOut, config) {
|
|
25295
25331
|
return flagOut ?? config.outDir;
|
|
@@ -26683,6 +26719,31 @@ async function runInitWizard(opts) {
|
|
|
26683
26719
|
);
|
|
26684
26720
|
}
|
|
26685
26721
|
|
|
26722
|
+
// src/layout.ts
|
|
26723
|
+
function localLanguage(config, language) {
|
|
26724
|
+
return config.languageMap[language] ?? language;
|
|
26725
|
+
}
|
|
26726
|
+
function bundleFiles(config, language, rows, namespaces = topLevelNamespaces(rows)) {
|
|
26727
|
+
const lang = localLanguage(config, language);
|
|
26728
|
+
if (config.split === "namespace") {
|
|
26729
|
+
return namespaces.map((namespace) => ({
|
|
26730
|
+
file: `${config.outDir}/${lang}/${namespace}.json`,
|
|
26731
|
+
namespace,
|
|
26732
|
+
messages: bundleMessages(rows, namespace)
|
|
26733
|
+
}));
|
|
26734
|
+
}
|
|
26735
|
+
return [{ file: bundlePath(config, language), namespace: null, messages: bundleMessages(rows) }];
|
|
26736
|
+
}
|
|
26737
|
+
function joinNamespaceFiles(files) {
|
|
26738
|
+
const out = {};
|
|
26739
|
+
for (const { namespace, messages } of files) {
|
|
26740
|
+
for (const [key, value] of Object.entries(messages)) {
|
|
26741
|
+
out[namespace && namespace !== ROOT_NAMESPACE ? `${namespace}.${key}` : key] = value;
|
|
26742
|
+
}
|
|
26743
|
+
}
|
|
26744
|
+
return out;
|
|
26745
|
+
}
|
|
26746
|
+
|
|
26686
26747
|
// src/messages.ts
|
|
26687
26748
|
function fullKey(row) {
|
|
26688
26749
|
return row.namespacePath ? `${row.namespacePath}.${row.key}` : row.key;
|
|
@@ -26718,12 +26779,21 @@ async function resolveProjectLanguages(api, config) {
|
|
|
26718
26779
|
const project = await api.getProject(config.projectId);
|
|
26719
26780
|
return resolveLanguages(config.languages, project);
|
|
26720
26781
|
}
|
|
26721
|
-
function
|
|
26722
|
-
const file = bundlePath(config, language);
|
|
26723
|
-
if (!(0, import_node_fs4.existsSync)(file)) return {};
|
|
26782
|
+
function readBundleFile(config, file) {
|
|
26724
26783
|
const resource = getCodec(config.format).parse((0, import_node_fs4.readFileSync)(file, "utf-8"));
|
|
26725
26784
|
return Object.fromEntries(resource.messages.map((m) => [m.key, m.value]));
|
|
26726
26785
|
}
|
|
26786
|
+
function readLocalMap(config, language) {
|
|
26787
|
+
if (config.split === "namespace") {
|
|
26788
|
+
const dir = `${config.outDir}/${localLanguage(config, language)}`;
|
|
26789
|
+
if (!(0, import_node_fs4.existsSync)(dir)) return {};
|
|
26790
|
+
return joinNamespaceFiles(
|
|
26791
|
+
(0, import_node_fs4.readdirSync)(dir).filter((f) => f.endsWith(".json")).map((f) => ({ namespace: (0, import_node_path3.basename)(f, ".json"), messages: readBundleFile(config, `${dir}/${f}`) }))
|
|
26792
|
+
);
|
|
26793
|
+
}
|
|
26794
|
+
const file = bundlePath(config, language);
|
|
26795
|
+
return (0, import_node_fs4.existsSync)(file) ? readBundleFile(config, file) : {};
|
|
26796
|
+
}
|
|
26727
26797
|
async function readRemoteMap(api, config, language, status) {
|
|
26728
26798
|
const rows = await api.listTranslations(config.projectId, language, status);
|
|
26729
26799
|
return Object.fromEntries(rowsToMessages(rows).map((m) => [m.key, m.value]));
|
|
@@ -26793,19 +26863,25 @@ async function pullCommand(opts = {}) {
|
|
|
26793
26863
|
if (cfg.languages.length === 0) {
|
|
26794
26864
|
console.log(import_picocolors.default.dim(`\u2192 pulling all project languages: ${languages.join(", ")}`));
|
|
26795
26865
|
}
|
|
26866
|
+
const fetched = [];
|
|
26796
26867
|
for (const language of languages) {
|
|
26797
|
-
const
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
|
|
26801
|
-
|
|
26868
|
+
const status = cfg.pull === "approved" ? "approved" : void 0;
|
|
26869
|
+
fetched.push({ language, rows: await api.listTranslations(cfg.projectId, language, status) });
|
|
26870
|
+
}
|
|
26871
|
+
const namespaces = [...new Set(fetched.flatMap((f) => topLevelNamespaces(f.rows)))].sort();
|
|
26872
|
+
for (const { language, rows } of fetched) {
|
|
26802
26873
|
const messages = rowsToMessages(rows);
|
|
26803
26874
|
const skipped = rows.length - messages.length;
|
|
26804
|
-
const
|
|
26805
|
-
|
|
26806
|
-
|
|
26875
|
+
const files = bundleFiles(cfg, language, rows, namespaces);
|
|
26876
|
+
for (const { file, messages: map } of files) {
|
|
26877
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path3.dirname)(file), { recursive: true });
|
|
26878
|
+
const list = Object.entries(map).map(([key, value]) => ({ key, value }));
|
|
26879
|
+
(0, import_node_fs4.writeFileSync)(file, codec.serialize({ messages: list, language }));
|
|
26880
|
+
}
|
|
26807
26881
|
const skippedNote = skipped > 0 ? `, ${skipped} untranslated skipped` : "";
|
|
26808
|
-
|
|
26882
|
+
const summary = `${messages.length} keys${skippedNote}`;
|
|
26883
|
+
const line = cfg.split === "namespace" ? `${cfg.outDir}/${localLanguage(cfg, language)}/ \u2014 ${files.length} namespace file${files.length === 1 ? "" : "s"}, ${summary}` : `${files[0].file} (${summary})`;
|
|
26884
|
+
console.log(import_picocolors.default.green(`\u2713 ${line}`));
|
|
26809
26885
|
if (typeEntries.length === 0) {
|
|
26810
26886
|
typeEntries = messages.map((m) => ({ key: m.key, sample: m.value }));
|
|
26811
26887
|
}
|
|
@@ -26891,12 +26967,14 @@ async function pushCommand(opts) {
|
|
|
26891
26967
|
// package.json
|
|
26892
26968
|
var package_default = {
|
|
26893
26969
|
name: "@radicalstorage-lingo/cli",
|
|
26894
|
-
version: "0.
|
|
26970
|
+
version: "0.4.0",
|
|
26895
26971
|
type: "module",
|
|
26896
26972
|
description: "Lingo CLI \u2014 pull/push translations as JSON at build time.",
|
|
26897
26973
|
license: "MIT",
|
|
26974
|
+
repository: { type: "git", url: "git+https://github.com/CashcallPro/lingo.git", directory: "packages/cli" },
|
|
26975
|
+
homepage: "https://github.com/CashcallPro/lingo#readme",
|
|
26976
|
+
bugs: { url: "https://github.com/CashcallPro/lingo/issues" },
|
|
26898
26977
|
author: "Amin Ahrabian",
|
|
26899
|
-
homepage: "https://lingo-app-amin.fly.dev",
|
|
26900
26978
|
keywords: [
|
|
26901
26979
|
"i18n",
|
|
26902
26980
|
"internationalization",
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radicalstorage-lingo/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lingo CLI — pull/push translations as JSON at build time.",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/CashcallPro/lingo.git",
|
|
10
|
+
"directory": "packages/cli"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/CashcallPro/lingo#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/CashcallPro/lingo/issues"
|
|
15
|
+
},
|
|
7
16
|
"author": "Amin Ahrabian",
|
|
8
|
-
"homepage": "https://lingo-app-amin.fly.dev",
|
|
9
17
|
"keywords": [
|
|
10
18
|
"i18n",
|
|
11
19
|
"internationalization",
|