@p6t/cli 0.1.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 +136 -0
- package/dist/index.js +1029 -0
- package/dist/lingui-VSOT4HY4.js +222 -0
- package/dist/react-intl-EIVHSW57.js +222 -0
- package/package.json +53 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/catalog/lingui.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { po } from "gettext-parser";
|
|
7
|
+
import { getConfig } from "@lingui/conf";
|
|
8
|
+
function loadLinguiConfig(cwd) {
|
|
9
|
+
try {
|
|
10
|
+
const config = getConfig({ cwd, skipValidation: true });
|
|
11
|
+
return {
|
|
12
|
+
rootDir: config.rootDir,
|
|
13
|
+
locales: config.locales,
|
|
14
|
+
catalogPaths: config.catalogs.map((catalog) => ({
|
|
15
|
+
path: catalog.path
|
|
16
|
+
}))
|
|
17
|
+
};
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function resolveCatalogPath(template, locale, rootDir) {
|
|
23
|
+
const resolved = template.replace("<rootDir>", rootDir).replace("{locale}", locale);
|
|
24
|
+
return path.resolve(resolved + ".po");
|
|
25
|
+
}
|
|
26
|
+
function makeId(msgctxt, msgid) {
|
|
27
|
+
return msgctxt ? `${msgctxt}${msgid}` : msgid;
|
|
28
|
+
}
|
|
29
|
+
function isFuzzy(entry) {
|
|
30
|
+
return entry.comments?.flag?.includes("fuzzy") ?? false;
|
|
31
|
+
}
|
|
32
|
+
function parsePo(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
return po.parse(fs.readFileSync(filePath));
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function discoverPoFiles(cwd) {
|
|
40
|
+
const candidates = [];
|
|
41
|
+
for (const dir of ["locale", "locales", "src/locales", "src/locale"]) {
|
|
42
|
+
const full = path.join(cwd, dir);
|
|
43
|
+
if (!fs.existsSync(full)) continue;
|
|
44
|
+
for (const entry of fs.readdirSync(full, {
|
|
45
|
+
recursive: true
|
|
46
|
+
})) {
|
|
47
|
+
if (entry.endsWith(".po")) candidates.push(path.join(full, entry));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return candidates;
|
|
51
|
+
}
|
|
52
|
+
function refFile(ref) {
|
|
53
|
+
const idx = ref.lastIndexOf(":");
|
|
54
|
+
return idx >= 0 ? ref.slice(0, idx) : ref;
|
|
55
|
+
}
|
|
56
|
+
function createLinguiAdapter(configDir, _catalogs) {
|
|
57
|
+
let linguiProject = null;
|
|
58
|
+
function ensureLinguiProject() {
|
|
59
|
+
if (!linguiProject) {
|
|
60
|
+
linguiProject = loadLinguiConfig(configDir);
|
|
61
|
+
if (!linguiProject) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"No lingui config found. Make sure lingui.config.ts (or equivalent) exists."
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return linguiProject;
|
|
68
|
+
}
|
|
69
|
+
function getCatalogPathsForLocale(locale) {
|
|
70
|
+
const project = ensureLinguiProject();
|
|
71
|
+
return project.catalogPaths.map(
|
|
72
|
+
(catalog) => resolveCatalogPath(catalog.path, locale, project.rootDir)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
getCatalogPathsForLocale,
|
|
77
|
+
readPendingTranslations(targetPath, sourcePath) {
|
|
78
|
+
let targetPo = null;
|
|
79
|
+
if (fs.existsSync(targetPath)) {
|
|
80
|
+
try {
|
|
81
|
+
targetPo = po.parse(fs.readFileSync(targetPath));
|
|
82
|
+
} catch {
|
|
83
|
+
targetPo = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!fs.existsSync(sourcePath)) return { groups: [] };
|
|
87
|
+
const sourcePo = po.parse(fs.readFileSync(sourcePath));
|
|
88
|
+
const unitsByFile = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const [ctx, entries] of Object.entries(sourcePo.translations)) {
|
|
90
|
+
for (const [msgid, entry] of Object.entries(entries)) {
|
|
91
|
+
if (!msgid) continue;
|
|
92
|
+
const id = makeId(ctx || void 0, msgid);
|
|
93
|
+
const targetEntry = targetPo?.translations[ctx]?.[msgid];
|
|
94
|
+
const existingTranslation = targetEntry?.msgstr?.[0];
|
|
95
|
+
if (existingTranslation && existingTranslation.trim() !== "" && targetEntry && !isFuzzy(targetEntry)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const unit = {
|
|
99
|
+
id,
|
|
100
|
+
source: entry.msgid
|
|
101
|
+
};
|
|
102
|
+
if (entry.msgid_plural) unit.sourcePlural = entry.msgid_plural;
|
|
103
|
+
const extracted = entry.comments?.extracted;
|
|
104
|
+
if (extracted) unit.context = extracted;
|
|
105
|
+
const refs = entry.comments?.reference;
|
|
106
|
+
if (refs) {
|
|
107
|
+
unit.references = refs.split("\n").filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
const file = unit.references?.[0] ? refFile(unit.references[0]) : sourcePath;
|
|
110
|
+
const group = unitsByFile.get(file);
|
|
111
|
+
if (group) {
|
|
112
|
+
group.push(unit);
|
|
113
|
+
} else {
|
|
114
|
+
unitsByFile.set(file, [unit]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const hasReferences = unitsByFile.size > 0 && Array.from(unitsByFile.values()).some(
|
|
119
|
+
(units) => units.some((u) => u.references != null && u.references.length > 0)
|
|
120
|
+
);
|
|
121
|
+
const groups = [];
|
|
122
|
+
for (const [sourceFile, units] of unitsByFile) {
|
|
123
|
+
groups.push({ sourceFile, hasSourceRefs: hasReferences, units });
|
|
124
|
+
}
|
|
125
|
+
return { groups };
|
|
126
|
+
},
|
|
127
|
+
writeTranslations(filePath, sourceFilePath, results) {
|
|
128
|
+
const resultMap = new Map(results.map((r) => [r.id, r.translation]));
|
|
129
|
+
let catalog;
|
|
130
|
+
if (fs.existsSync(filePath)) {
|
|
131
|
+
catalog = po.parse(fs.readFileSync(filePath));
|
|
132
|
+
} else {
|
|
133
|
+
const source = po.parse(fs.readFileSync(sourceFilePath));
|
|
134
|
+
catalog = {
|
|
135
|
+
charset: source.charset,
|
|
136
|
+
headers: { ...source.headers },
|
|
137
|
+
translations: {}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
for (const result of results) {
|
|
141
|
+
const sepIdx = result.id.indexOf("");
|
|
142
|
+
const ctx = sepIdx >= 0 ? result.id.slice(0, sepIdx) : "";
|
|
143
|
+
const msgid = sepIdx >= 0 ? result.id.slice(sepIdx + 1) : result.id;
|
|
144
|
+
if (!catalog.translations[ctx]) catalog.translations[ctx] = {};
|
|
145
|
+
const existing = catalog.translations[ctx][msgid];
|
|
146
|
+
const { flag: _flag, ...commentsWithoutFlag } = existing?.comments ?? {};
|
|
147
|
+
catalog.translations[ctx][msgid] = {
|
|
148
|
+
...existing ?? { msgid },
|
|
149
|
+
msgstr: [resultMap.get(result.id) ?? ""],
|
|
150
|
+
comments: commentsWithoutFlag
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
154
|
+
fs.writeFileSync(filePath, po.compile(catalog));
|
|
155
|
+
},
|
|
156
|
+
removeStaleKeys(filePath, sourceFilePath) {
|
|
157
|
+
if (!fs.existsSync(filePath) || !fs.existsSync(sourceFilePath)) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const catalog = po.parse(fs.readFileSync(filePath));
|
|
161
|
+
const source = po.parse(fs.readFileSync(sourceFilePath));
|
|
162
|
+
const orphans = [];
|
|
163
|
+
let modified = false;
|
|
164
|
+
for (const ctx of Object.keys(catalog.translations)) {
|
|
165
|
+
for (const msgid of Object.keys(catalog.translations[ctx])) {
|
|
166
|
+
if (!msgid) continue;
|
|
167
|
+
if (!source.translations[ctx]?.[msgid]) {
|
|
168
|
+
const entry = catalog.translations[ctx][msgid];
|
|
169
|
+
orphans.push({
|
|
170
|
+
id: makeId(ctx || void 0, msgid),
|
|
171
|
+
source: entry.msgid,
|
|
172
|
+
translation: entry.msgstr?.[0] ?? "",
|
|
173
|
+
context: entry.comments?.extracted
|
|
174
|
+
});
|
|
175
|
+
delete catalog.translations[ctx][msgid];
|
|
176
|
+
modified = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (Object.keys(catalog.translations[ctx]).length === 0) {
|
|
180
|
+
delete catalog.translations[ctx];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (modified) {
|
|
184
|
+
fs.writeFileSync(filePath, po.compile(catalog));
|
|
185
|
+
}
|
|
186
|
+
return orphans;
|
|
187
|
+
},
|
|
188
|
+
discoverCatalogs(cwd) {
|
|
189
|
+
return discoverPoFiles(cwd);
|
|
190
|
+
},
|
|
191
|
+
extractSourceStrings(filePath) {
|
|
192
|
+
const catalog = parsePo(filePath);
|
|
193
|
+
if (!catalog) return [];
|
|
194
|
+
const strings = [];
|
|
195
|
+
for (const entries of Object.values(catalog.translations)) {
|
|
196
|
+
for (const [msgid, entry] of Object.entries(entries)) {
|
|
197
|
+
if (!msgid) continue;
|
|
198
|
+
strings.push(entry.msgid);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return [...new Set(strings)];
|
|
202
|
+
},
|
|
203
|
+
extractTranslatedPairs(filePath, _sourceFilePath) {
|
|
204
|
+
const catalog = parsePo(filePath);
|
|
205
|
+
if (!catalog) return [];
|
|
206
|
+
const pairs = [];
|
|
207
|
+
for (const entries of Object.values(catalog.translations)) {
|
|
208
|
+
for (const [msgid, entry] of Object.entries(entries)) {
|
|
209
|
+
if (!msgid) continue;
|
|
210
|
+
const translation = entry.msgstr?.[0];
|
|
211
|
+
if (translation && translation.trim()) {
|
|
212
|
+
pairs.push({ source: entry.msgid, translation });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return pairs;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
export {
|
|
221
|
+
createLinguiAdapter
|
|
222
|
+
};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/catalog/react-intl.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
function resolveCatalogPath(template, locale, configDir) {
|
|
7
|
+
return path.resolve(configDir, template.replace("{locale}", locale));
|
|
8
|
+
}
|
|
9
|
+
function readJsonSafe(filePath) {
|
|
10
|
+
try {
|
|
11
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
12
|
+
return JSON.parse(raw);
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function isRichCatalog(obj) {
|
|
18
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
19
|
+
const values = Object.values(obj);
|
|
20
|
+
if (values.length === 0) return false;
|
|
21
|
+
return values.every(
|
|
22
|
+
(entry) => typeof entry === "object" && entry !== null && "defaultMessage" in entry && typeof entry.defaultMessage === "string"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
function discoverJsonFiles(cwd) {
|
|
26
|
+
const candidates = [];
|
|
27
|
+
const dirs = [
|
|
28
|
+
"lang",
|
|
29
|
+
"src/lang",
|
|
30
|
+
"messages",
|
|
31
|
+
"src/messages",
|
|
32
|
+
"i18n",
|
|
33
|
+
"locales",
|
|
34
|
+
"src/locales",
|
|
35
|
+
"src/i18n"
|
|
36
|
+
];
|
|
37
|
+
for (const dir of dirs) {
|
|
38
|
+
const full = path.join(cwd, dir);
|
|
39
|
+
if (!fs.existsSync(full)) continue;
|
|
40
|
+
for (const entry of fs.readdirSync(full, { recursive: true })) {
|
|
41
|
+
if (entry.endsWith(".json")) {
|
|
42
|
+
candidates.push(path.join(full, entry));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return candidates;
|
|
47
|
+
}
|
|
48
|
+
function createReactIntlAdapter(configDir, catalogs) {
|
|
49
|
+
if (!catalogs || catalogs.length === 0) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
"react-intl adapter requires catalogs configured in i18n.json"
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function getCatalogPathsForLocale(locale) {
|
|
55
|
+
return catalogs.map((c) => resolveCatalogPath(c.path, locale, configDir));
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
getCatalogPathsForLocale,
|
|
59
|
+
readPendingTranslations(targetPath, sourcePath) {
|
|
60
|
+
const sourceCatalog = readJsonSafe(sourcePath);
|
|
61
|
+
if (!sourceCatalog) return { groups: [] };
|
|
62
|
+
const targetCatalog = readJsonSafe(targetPath) ?? {};
|
|
63
|
+
const isRich = isRichCatalog(sourceCatalog);
|
|
64
|
+
const unitsByFile = /* @__PURE__ */ new Map();
|
|
65
|
+
let hasReferences = false;
|
|
66
|
+
for (const [id, entry] of Object.entries(sourceCatalog)) {
|
|
67
|
+
const targetValue = targetCatalog[id];
|
|
68
|
+
if (typeof targetValue?.defaultMessage === "string" && targetValue.defaultMessage.trim() !== "") {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const unit = { id, source: "" };
|
|
72
|
+
if (isRich) {
|
|
73
|
+
const richEntry = entry;
|
|
74
|
+
unit.source = richEntry.defaultMessage;
|
|
75
|
+
if (richEntry.description) unit.context = richEntry.description;
|
|
76
|
+
if (richEntry.file != null && richEntry.line != null) {
|
|
77
|
+
unit.references = [`${richEntry.file}:${richEntry.line}`];
|
|
78
|
+
hasReferences = true;
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
unit.source = entry;
|
|
82
|
+
}
|
|
83
|
+
if (!unit.source) continue;
|
|
84
|
+
const file = unit.references?.[0]?.split(":")[0] ?? sourcePath;
|
|
85
|
+
const group = unitsByFile.get(file);
|
|
86
|
+
if (group) {
|
|
87
|
+
group.push(unit);
|
|
88
|
+
} else {
|
|
89
|
+
unitsByFile.set(file, [unit]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!hasReferences && unitsByFile.size > 0) {
|
|
93
|
+
console.warn(
|
|
94
|
+
"[p6t] Source file references not found in react-intl catalog. Translations will be grouped by catalog file instead of by component. For better results, run `formatjs extract` with `--extract-source-location`."
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const groups = [];
|
|
98
|
+
for (const [sourceFile, units] of unitsByFile) {
|
|
99
|
+
groups.push({ sourceFile, hasSourceRefs: hasReferences, units });
|
|
100
|
+
}
|
|
101
|
+
return { groups };
|
|
102
|
+
},
|
|
103
|
+
writeTranslations(targetPath, sourcePath, results) {
|
|
104
|
+
const sourceCatalog = readJsonSafe(
|
|
105
|
+
sourcePath
|
|
106
|
+
);
|
|
107
|
+
const isRich = sourceCatalog ? isRichCatalog(sourceCatalog) : false;
|
|
108
|
+
const sourceMap = sourceCatalog ? new Map(Object.entries(sourceCatalog)) : /* @__PURE__ */ new Map();
|
|
109
|
+
let catalog;
|
|
110
|
+
if (fs.existsSync(targetPath)) {
|
|
111
|
+
catalog = readJsonSafe(targetPath) ?? {};
|
|
112
|
+
} else {
|
|
113
|
+
catalog = {};
|
|
114
|
+
}
|
|
115
|
+
for (const result of results) {
|
|
116
|
+
const sourceEntry = sourceMap.get(result.id);
|
|
117
|
+
const entry = {
|
|
118
|
+
defaultMessage: result.translation
|
|
119
|
+
};
|
|
120
|
+
if (isRich && sourceEntry && typeof sourceEntry === "object") {
|
|
121
|
+
const rich = sourceEntry;
|
|
122
|
+
if (rich.description) entry._p6t_description = rich.description;
|
|
123
|
+
entry._p6t_source = rich.defaultMessage;
|
|
124
|
+
} else if (typeof sourceEntry === "string") {
|
|
125
|
+
entry._p6t_source = sourceEntry;
|
|
126
|
+
}
|
|
127
|
+
catalog[result.id] = entry;
|
|
128
|
+
}
|
|
129
|
+
const ordered = {};
|
|
130
|
+
if (sourceCatalog) {
|
|
131
|
+
for (const id of Object.keys(sourceCatalog)) {
|
|
132
|
+
if (id in catalog) {
|
|
133
|
+
ordered[id] = catalog[id];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const [id, entry] of Object.entries(catalog)) {
|
|
138
|
+
if (!(id in ordered)) {
|
|
139
|
+
ordered[id] = entry;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
143
|
+
fs.writeFileSync(targetPath, JSON.stringify(ordered, null, 2) + "\n");
|
|
144
|
+
},
|
|
145
|
+
removeStaleKeys(targetPath, sourcePath) {
|
|
146
|
+
if (!fs.existsSync(targetPath)) return [];
|
|
147
|
+
const catalog = readJsonSafe(targetPath) ?? {};
|
|
148
|
+
const sourceCatalog = readJsonSafe(
|
|
149
|
+
sourcePath
|
|
150
|
+
);
|
|
151
|
+
if (!sourceCatalog) return [];
|
|
152
|
+
const orphans = [];
|
|
153
|
+
let modified = false;
|
|
154
|
+
for (const id of Object.keys(catalog)) {
|
|
155
|
+
if (!(id in sourceCatalog)) {
|
|
156
|
+
const entry = catalog[id];
|
|
157
|
+
orphans.push({
|
|
158
|
+
id,
|
|
159
|
+
source: entry._p6t_source,
|
|
160
|
+
translation: entry.defaultMessage,
|
|
161
|
+
context: entry._p6t_description
|
|
162
|
+
});
|
|
163
|
+
delete catalog[id];
|
|
164
|
+
modified = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (modified) {
|
|
168
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
169
|
+
fs.writeFileSync(targetPath, JSON.stringify(catalog, null, 2) + "\n");
|
|
170
|
+
}
|
|
171
|
+
return orphans;
|
|
172
|
+
},
|
|
173
|
+
discoverCatalogs(cwd) {
|
|
174
|
+
return discoverJsonFiles(cwd);
|
|
175
|
+
},
|
|
176
|
+
extractSourceStrings(filePath) {
|
|
177
|
+
const catalog = readJsonSafe(filePath);
|
|
178
|
+
if (!catalog) return [];
|
|
179
|
+
const isRich = isRichCatalog(catalog);
|
|
180
|
+
const strings = [];
|
|
181
|
+
for (const [id, entry] of Object.entries(catalog)) {
|
|
182
|
+
if (!id) continue;
|
|
183
|
+
if (isRich) {
|
|
184
|
+
strings.push(entry.defaultMessage);
|
|
185
|
+
} else {
|
|
186
|
+
strings.push(entry);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return [...new Set(strings)];
|
|
190
|
+
},
|
|
191
|
+
extractTranslatedPairs(filePath, sourceFilePath) {
|
|
192
|
+
const catalog = readJsonSafe(filePath);
|
|
193
|
+
if (!catalog) return [];
|
|
194
|
+
let sourceCatalog = null;
|
|
195
|
+
if (sourceFilePath) {
|
|
196
|
+
sourceCatalog = readJsonSafe(
|
|
197
|
+
sourceFilePath
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const pairs = [];
|
|
201
|
+
const isSourceRich = sourceCatalog ? isRichCatalog(sourceCatalog) : false;
|
|
202
|
+
for (const [id, entry] of Object.entries(catalog)) {
|
|
203
|
+
const translation = entry.defaultMessage;
|
|
204
|
+
if (!translation?.trim()) continue;
|
|
205
|
+
let source = id;
|
|
206
|
+
if (sourceCatalog) {
|
|
207
|
+
const sourceEntry = sourceCatalog[id];
|
|
208
|
+
if (isSourceRich && sourceEntry && typeof sourceEntry === "object" && sourceEntry !== null) {
|
|
209
|
+
source = sourceEntry.defaultMessage;
|
|
210
|
+
} else if (typeof sourceEntry === "string") {
|
|
211
|
+
source = sourceEntry;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
pairs.push({ source, translation });
|
|
215
|
+
}
|
|
216
|
+
return pairs;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
export {
|
|
221
|
+
createReactIntlAdapter
|
|
222
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@p6t/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Developer CLI for p6t — extract, push, pull, and check translation coverage",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"p6t": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"i18n",
|
|
14
|
+
"translation",
|
|
15
|
+
"cli",
|
|
16
|
+
"polyglot"
|
|
17
|
+
],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/gettext-parser": "^9.0.0",
|
|
25
|
+
"@types/node": "^25.6.0",
|
|
26
|
+
"tsup": "^8.0.0",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.1.6"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@ai-sdk/anthropic": "^3.0.72",
|
|
32
|
+
"@ai-sdk/google": "^3.0.65",
|
|
33
|
+
"@ai-sdk/mistral": "^3.0.31",
|
|
34
|
+
"@ai-sdk/openai": "^3.0.54",
|
|
35
|
+
"@clack/prompts": "^1.2.0",
|
|
36
|
+
"@lingui/conf": "^6.0.0",
|
|
37
|
+
"@openrouter/ai-sdk-provider": "^2.9.0",
|
|
38
|
+
"ai": "^6.0.169",
|
|
39
|
+
"ai-sdk-ollama": "^3.8.3",
|
|
40
|
+
"citty": "^0.2.2",
|
|
41
|
+
"gettext-parser": "^9.0.2",
|
|
42
|
+
"zod": "^4.4.3",
|
|
43
|
+
"@p6t/config": "0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsup",
|
|
47
|
+
"build:dev": "tsup --watch",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"lint": "tsc --noEmit",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"clean": "rm -rf dist"
|
|
52
|
+
}
|
|
53
|
+
}
|