langctl 0.2.0 → 0.3.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/CHANGELOG.md +42 -0
- package/README.md +155 -1188
- package/dist/commands/auth.js +85 -19
- package/dist/commands/config.js +36 -26
- package/dist/commands/export.js +11 -122
- package/dist/commands/import.js +32 -143
- package/dist/commands/init.js +61 -72
- package/dist/commands/keys.js +145 -279
- package/dist/commands/org.js +45 -111
- package/dist/commands/projects.js +124 -312
- package/dist/commands/pull.js +80 -130
- package/dist/commands/push.js +125 -0
- package/dist/commands/team.js +68 -257
- package/dist/core/config.js +130 -0
- package/dist/core/errors.js +67 -0
- package/dist/core/files.js +38 -0
- package/dist/core/http.js +121 -0
- package/dist/core/output.js +96 -0
- package/dist/core/project.js +58 -0
- package/dist/core/prompts.js +56 -0
- package/dist/formats/index.js +285 -0
- package/dist/index.js +261 -508
- package/dist/version.js +4 -0
- package/package.json +22 -15
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -26
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/CONTRIBUTING.md +0 -33
- package/dist/api.d.ts +0 -20
- package/dist/api.js +0 -75
- package/dist/auth.d.ts +0 -38
- package/dist/auth.js +0 -106
- package/dist/commands/auth.d.ts +0 -3
- package/dist/commands/config.d.ts +0 -2
- package/dist/commands/debug.d.ts +0 -3
- package/dist/commands/debug.js +0 -61
- package/dist/commands/export.d.ts +0 -13
- package/dist/commands/import.d.ts +0 -12
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/keys.d.ts +0 -25
- package/dist/commands/org.d.ts +0 -13
- package/dist/commands/projects.d.ts +0 -33
- package/dist/commands/pull.d.ts +0 -9
- package/dist/commands/team.d.ts +0 -29
- package/dist/config.d.ts +0 -48
- package/dist/config.js +0 -69
- package/dist/exporters/index.d.ts +0 -36
- package/dist/exporters/index.js +0 -214
- package/dist/index.d.ts +0 -3
- package/dist/utils/banner.d.ts +0 -9
- package/dist/utils/banner.js +0 -24
- package/translations/en.json +0 -5
- package/translations/en.xml +0 -9
- package/translations/fr.json +0 -4
- package/translations/fr.xml +0 -7
- package/translations/hi.json +0 -4
- package/translations/hi.xml +0 -7
- package/translations/ios/en.strings +0 -9
- package/translations/ios/fr.strings +0 -6
- package/translations/ios/hi.strings +0 -6
- package/translations/ios/ja.strings +0 -6
- package/translations/ja.json +0 -4
- package/translations/ja.xml +0 -7
|
@@ -1,325 +1,137 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
async function resolveProject(orgId, slug) {
|
|
10
|
-
const api = getApiClient();
|
|
11
|
-
return api.get(`/orgs/${orgId}/projects/by-slug/${slug}`);
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* List all projects
|
|
15
|
-
*/
|
|
2
|
+
import { CliError, ExitCode, usageError } from '../core/errors.js';
|
|
3
|
+
import { getSession } from '../core/http.js';
|
|
4
|
+
import { log, printJson, runtime, table } from '../core/output.js';
|
|
5
|
+
import { confirm } from '../core/prompts.js';
|
|
6
|
+
import { getProject } from '../core/project.js';
|
|
7
|
+
import { splitList } from './pull.js';
|
|
16
8
|
export async function projectsListCommand() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
|
|
25
|
-
process.exitCode = 1;
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const spinner = ora('Fetching projects...').start();
|
|
29
|
-
try {
|
|
30
|
-
const api = getApiClient();
|
|
31
|
-
const projects = await api.get(`/orgs/${orgId}/projects`);
|
|
32
|
-
spinner.stop();
|
|
33
|
-
if (!projects || projects.length === 0) {
|
|
34
|
-
console.log(chalk.yellow('\nNo projects found. Create one in the dashboard first.\n'));
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
console.log(chalk.blue.bold(`\n📦 Projects (${projects.length})\n`));
|
|
38
|
-
projects.forEach((project) => {
|
|
39
|
-
console.log(chalk.white.bold(project.name));
|
|
40
|
-
console.log(chalk.gray(` Slug: ${project.slug}`));
|
|
41
|
-
if (project.description) {
|
|
42
|
-
console.log(chalk.gray(` Description: ${project.description}`));
|
|
43
|
-
}
|
|
44
|
-
console.log(chalk.gray(` Languages: ${project.languages.join(', ')}`));
|
|
45
|
-
console.log(chalk.gray(` Default: ${project.defaultLanguage}`));
|
|
46
|
-
if (project.modules && project.modules.length > 0) {
|
|
47
|
-
console.log(chalk.gray(` Modules: ${project.modules.join(', ')}`));
|
|
48
|
-
}
|
|
49
|
-
console.log('');
|
|
50
|
-
});
|
|
51
|
-
console.log(chalk.blue('To export translations, run:'));
|
|
52
|
-
console.log(chalk.white(` langctl export ${projects[0].slug} -l en\n`));
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
spinner.fail(chalk.red('Failed to fetch projects'));
|
|
56
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
57
|
-
process.exitCode = 1;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Create new project
|
|
62
|
-
*/
|
|
63
|
-
export async function projectsCreateCommand(name, options) {
|
|
64
|
-
if (!isAuthenticated()) {
|
|
65
|
-
console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
66
|
-
process.exitCode = 1;
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const orgId = config.get('organizationId');
|
|
70
|
-
if (!orgId) {
|
|
71
|
-
console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
|
|
72
|
-
process.exitCode = 1;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const spinner = ora('Creating project...').start();
|
|
76
|
-
try {
|
|
77
|
-
const api = getApiClient();
|
|
78
|
-
const languages = options.languages ? options.languages.split(',') : ['en'];
|
|
79
|
-
const defaultLanguage = options.defaultLanguage || languages[0];
|
|
80
|
-
const project = await api.post(`/orgs/${orgId}/projects`, {
|
|
81
|
-
name,
|
|
82
|
-
description: options.description,
|
|
83
|
-
languages,
|
|
84
|
-
defaultLanguage
|
|
85
|
-
});
|
|
86
|
-
spinner.succeed(chalk.green(`Created project: ${name} (${project.slug})`));
|
|
87
|
-
console.log('');
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
spinner.fail(chalk.red('Failed to create project'));
|
|
91
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
92
|
-
process.exitCode = 1;
|
|
93
|
-
}
|
|
9
|
+
const session = await getSession();
|
|
10
|
+
const projects = await session.api.get(`/orgs/${session.orgId}/projects`);
|
|
11
|
+
if (runtime.json)
|
|
12
|
+
return printJson(projects);
|
|
13
|
+
if (projects.length === 0)
|
|
14
|
+
return log.info('No projects yet. Create one with "langctl projects create <name>".');
|
|
15
|
+
table(projects.map(p => [p.slug, p.name, p.languages.join(','), p.defaultLanguage]), ['SLUG', 'NAME', 'LANGUAGES', 'DEFAULT']);
|
|
94
16
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Get project details
|
|
97
|
-
*/
|
|
98
17
|
export async function projectsGetCommand(slug) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
const spinner = ora('Fetching project...').start();
|
|
111
|
-
try {
|
|
112
|
-
const project = await resolveProject(orgId, slug);
|
|
113
|
-
spinner.stop();
|
|
114
|
-
console.log(chalk.blue.bold('\n📦 Project Details\n'));
|
|
115
|
-
console.log(chalk.white.bold(project.name));
|
|
116
|
-
console.log(chalk.gray(`Slug: ${project.slug}`));
|
|
117
|
-
console.log(chalk.gray(`ID: ${project.id}`));
|
|
118
|
-
if (project.description) {
|
|
119
|
-
console.log(chalk.gray(`Description: ${project.description}`));
|
|
120
|
-
}
|
|
121
|
-
console.log(chalk.gray(`Languages: ${project.languages.join(', ')}`));
|
|
122
|
-
console.log(chalk.gray(`Default Language: ${project.defaultLanguage}`));
|
|
123
|
-
if (project.modules && project.modules.length > 0) {
|
|
124
|
-
console.log(chalk.gray(`Modules: ${project.modules.join(', ')}`));
|
|
125
|
-
}
|
|
126
|
-
console.log('');
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
spinner.fail(chalk.red(`Failed to fetch project "${slug}"`));
|
|
130
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
131
|
-
process.exitCode = 1;
|
|
132
|
-
}
|
|
18
|
+
const session = await getSession();
|
|
19
|
+
const project = await getProject(session, slug);
|
|
20
|
+
if (runtime.json)
|
|
21
|
+
return printJson(project);
|
|
22
|
+
log.out(`${chalk.bold(project.name)} ${chalk.dim(`(${project.slug})`)}`);
|
|
23
|
+
if (project.description)
|
|
24
|
+
log.out(chalk.dim(project.description));
|
|
25
|
+
log.out(` languages ${project.languages.join(', ')} ${chalk.dim(`default ${project.defaultLanguage}`)}`);
|
|
26
|
+
if (project.modules?.length)
|
|
27
|
+
log.out(` modules ${project.modules.join(', ')}`);
|
|
28
|
+
log.out(` id ${project.id}`);
|
|
133
29
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const currentLanguages = project.languages || [];
|
|
182
|
-
const toAdd = desiredLanguages.filter((lang) => !currentLanguages.includes(lang));
|
|
183
|
-
const toRemove = currentLanguages.filter((lang) => !desiredLanguages.includes(lang));
|
|
184
|
-
for (const lang of toAdd) {
|
|
185
|
-
await api.post(`/orgs/${orgId}/projects/${project.id}/languages`, { code: lang });
|
|
186
|
-
}
|
|
187
|
-
for (const lang of toRemove) {
|
|
188
|
-
await api.delete(`/orgs/${orgId}/projects/${project.id}/languages/${lang}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
spinner.succeed(chalk.green(`Updated project: ${slug}`));
|
|
192
|
-
console.log('');
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
spinner.fail(chalk.red('Failed to update project'));
|
|
196
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
197
|
-
process.exitCode = 1;
|
|
198
|
-
}
|
|
30
|
+
export async function projectsCreateCommand(name, opts) {
|
|
31
|
+
const session = await getSession();
|
|
32
|
+
const languages = splitList(opts.languages ?? 'en');
|
|
33
|
+
const defaultLanguage = opts.defaultLanguage ?? languages[0];
|
|
34
|
+
if (!languages.includes(defaultLanguage))
|
|
35
|
+
throw usageError(`Default language "${defaultLanguage}" must be one of --languages (${languages.join(',')}).`);
|
|
36
|
+
const project = await session.api.post(`/orgs/${session.orgId}/projects`, { name, description: opts.description, languages, defaultLanguage });
|
|
37
|
+
if (runtime.json)
|
|
38
|
+
return printJson(project);
|
|
39
|
+
log.success(`Created ${chalk.bold(project.name)} — slug ${chalk.cyan(project.slug)}`);
|
|
40
|
+
}
|
|
41
|
+
export async function projectsUpdateCommand(slug, opts) {
|
|
42
|
+
const session = await getSession();
|
|
43
|
+
const project = await getProject(session, slug);
|
|
44
|
+
const body = {};
|
|
45
|
+
if (opts.name)
|
|
46
|
+
body.name = opts.name;
|
|
47
|
+
if (opts.description !== undefined)
|
|
48
|
+
body.description = opts.description;
|
|
49
|
+
if (opts.defaultLanguage)
|
|
50
|
+
body.defaultLanguage = opts.defaultLanguage;
|
|
51
|
+
let toAdd = [];
|
|
52
|
+
let toRemove = [];
|
|
53
|
+
if (opts.languages) {
|
|
54
|
+
const desired = splitList(opts.languages);
|
|
55
|
+
const effectiveDefault = opts.defaultLanguage ?? project.defaultLanguage;
|
|
56
|
+
if (!desired.includes(effectiveDefault))
|
|
57
|
+
throw usageError(`--languages must include the default language "${effectiveDefault}".`);
|
|
58
|
+
toAdd = desired.filter(l => !project.languages.includes(l));
|
|
59
|
+
toRemove = project.languages.filter(l => !desired.includes(l));
|
|
60
|
+
}
|
|
61
|
+
if (Object.keys(body).length === 0 && !toAdd.length && !toRemove.length)
|
|
62
|
+
throw usageError('Nothing to update.');
|
|
63
|
+
if (toRemove.length && !(await confirm(`Remove ${toRemove.join(', ')} from ${project.slug} and delete all of their translations?`))) {
|
|
64
|
+
throw new CliError('Cancelled.', ExitCode.Error);
|
|
65
|
+
}
|
|
66
|
+
// Add new languages first so a new default language exists before it is set
|
|
67
|
+
for (const code of toAdd)
|
|
68
|
+
await session.api.post(`/orgs/${session.orgId}/projects/${project.id}/languages`, { code });
|
|
69
|
+
if (Object.keys(body).length)
|
|
70
|
+
await session.api.patch(`/orgs/${session.orgId}/projects/${project.id}`, body);
|
|
71
|
+
for (const code of toRemove)
|
|
72
|
+
await session.api.delete(`/orgs/${session.orgId}/projects/${project.id}/languages/${encodeURIComponent(code)}`);
|
|
73
|
+
const updated = await getProject(session, slug);
|
|
74
|
+
if (runtime.json)
|
|
75
|
+
return printJson(updated);
|
|
76
|
+
log.success(`Updated ${chalk.bold(updated.slug)}${toAdd.length ? ` +${toAdd.join(',')}` : ''}${toRemove.length ? ` -${toRemove.join(',')}` : ''}`);
|
|
199
77
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Delete project
|
|
202
|
-
*/
|
|
203
78
|
export async function projectsDeleteCommand(slug) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
process.exitCode = 1;
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const spinner = ora('Deleting project...').start();
|
|
216
|
-
try {
|
|
217
|
-
const project = await resolveProject(orgId, slug);
|
|
218
|
-
const api = getApiClient();
|
|
219
|
-
await api.delete(`/orgs/${orgId}/projects/${project.id}`);
|
|
220
|
-
spinner.succeed(chalk.green(`Deleted project: ${slug}`));
|
|
221
|
-
console.log('');
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
spinner.fail(chalk.red('Failed to delete project'));
|
|
225
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
226
|
-
process.exitCode = 1;
|
|
227
|
-
}
|
|
79
|
+
const session = await getSession();
|
|
80
|
+
const project = await getProject(session, slug);
|
|
81
|
+
if (!(await confirm(`Delete project "${project.slug}" and all of its translation keys?`)))
|
|
82
|
+
throw new CliError('Cancelled.', ExitCode.Error);
|
|
83
|
+
await session.api.delete(`/orgs/${session.orgId}/projects/${project.id}`);
|
|
84
|
+
if (runtime.json)
|
|
85
|
+
return printJson({ deleted: project.slug });
|
|
86
|
+
log.success(`Deleted project ${chalk.bold(project.slug)}`);
|
|
228
87
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
|
|
241
|
-
process.exitCode = 1;
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const spinner = ora(`Adding language ${language}...`).start();
|
|
245
|
-
try {
|
|
246
|
-
const project = await resolveProject(orgId, slug);
|
|
247
|
-
const api = getApiClient();
|
|
248
|
-
await api.post(`/orgs/${orgId}/projects/${project.id}/languages`, { code: language });
|
|
249
|
-
spinner.succeed(chalk.green(`Added language "${language}" to project "${slug}"`));
|
|
250
|
-
console.log('');
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
spinner.fail(chalk.red('Failed to add language'));
|
|
254
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
255
|
-
process.exitCode = 1;
|
|
88
|
+
export async function projectsAddLanguageCommand(slug, codes) {
|
|
89
|
+
const session = await getSession();
|
|
90
|
+
const project = await getProject(session, slug);
|
|
91
|
+
const added = [];
|
|
92
|
+
for (const code of codes) {
|
|
93
|
+
if (project.languages.includes(code)) {
|
|
94
|
+
log.info(`${code} is already in ${project.slug}`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
await session.api.post(`/orgs/${session.orgId}/projects/${project.id}/languages`, { code });
|
|
98
|
+
added.push(code);
|
|
256
99
|
}
|
|
100
|
+
if (runtime.json)
|
|
101
|
+
return printJson({ project: project.slug, added });
|
|
102
|
+
if (added.length)
|
|
103
|
+
log.success(`Added ${added.join(', ')} to ${chalk.bold(project.slug)}`);
|
|
257
104
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const spinner = ora(`Removing language ${language}...`).start();
|
|
274
|
-
try {
|
|
275
|
-
const project = await resolveProject(orgId, slug);
|
|
276
|
-
const api = getApiClient();
|
|
277
|
-
await api.delete(`/orgs/${orgId}/projects/${project.id}/languages/${language}`);
|
|
278
|
-
spinner.succeed(chalk.green(`Removed language "${language}" from project "${slug}"`));
|
|
279
|
-
console.log('');
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
spinner.fail(chalk.red('Failed to remove language'));
|
|
283
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
284
|
-
process.exitCode = 1;
|
|
285
|
-
}
|
|
105
|
+
export async function projectsRemoveLanguageCommand(slug, codes) {
|
|
106
|
+
const session = await getSession();
|
|
107
|
+
const project = await getProject(session, slug);
|
|
108
|
+
const missing = codes.filter(c => !project.languages.includes(c));
|
|
109
|
+
if (missing.length)
|
|
110
|
+
throw usageError(`Not in ${project.slug}: ${missing.join(', ')}`);
|
|
111
|
+
if (codes.includes(project.defaultLanguage))
|
|
112
|
+
throw usageError(`"${project.defaultLanguage}" is the default language and can't be removed.`);
|
|
113
|
+
if (!(await confirm(`Remove ${codes.join(', ')} from ${project.slug} and delete all of their translations?`)))
|
|
114
|
+
throw new CliError('Cancelled.', ExitCode.Error);
|
|
115
|
+
for (const code of codes)
|
|
116
|
+
await session.api.delete(`/orgs/${session.orgId}/projects/${project.id}/languages/${encodeURIComponent(code)}`);
|
|
117
|
+
if (runtime.json)
|
|
118
|
+
return printJson({ project: project.slug, removed: codes });
|
|
119
|
+
log.success(`Removed ${codes.join(', ')} from ${chalk.bold(project.slug)}`);
|
|
286
120
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Get project statistics
|
|
289
|
-
*/
|
|
290
121
|
export async function projectsStatsCommand(slug) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const stats = await api.get(`/orgs/${orgId}/projects/${project.id}/stats`);
|
|
307
|
-
spinner.stop();
|
|
308
|
-
console.log(chalk.blue.bold('\n📊 Project Statistics\n'));
|
|
309
|
-
console.log(chalk.white(`Total Keys: ${stats.totalKeys}`));
|
|
310
|
-
console.log(chalk.green(`Published Keys: ${stats.publishedKeys}`));
|
|
311
|
-
console.log(chalk.yellow(`Unpublished Keys: ${stats.unpublishedKeys}`));
|
|
312
|
-
console.log(chalk.white(`Language Count: ${stats.languageCount}`));
|
|
313
|
-
if (stats.modules && stats.modules.length > 0) {
|
|
314
|
-
console.log(chalk.white(`Modules: ${stats.modules.length}`));
|
|
315
|
-
console.log(chalk.gray(` ${stats.modules.join(', ')}`));
|
|
316
|
-
}
|
|
317
|
-
console.log('');
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
spinner.fail(chalk.red('Failed to fetch statistics'));
|
|
321
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
322
|
-
process.exitCode = 1;
|
|
323
|
-
}
|
|
122
|
+
const session = await getSession();
|
|
123
|
+
const project = await getProject(session, slug);
|
|
124
|
+
const [stats, snapshot] = await Promise.all([
|
|
125
|
+
session.api.get(`/orgs/${session.orgId}/projects/${project.id}/stats`),
|
|
126
|
+
session.api.get(`/orgs/${session.orgId}/projects/${project.id}/export`, { publishedOnly: 'false' }),
|
|
127
|
+
]);
|
|
128
|
+
const total = snapshot.keys.length;
|
|
129
|
+
const coverage = project.languages.map(lang => {
|
|
130
|
+
const translated = snapshot.keys.filter(k => k.translations[lang]).length;
|
|
131
|
+
return { language: lang, translated, missing: total - translated, percent: total ? Math.round((translated / total) * 100) : 100 };
|
|
132
|
+
});
|
|
133
|
+
if (runtime.json)
|
|
134
|
+
return printJson({ ...stats, coverage });
|
|
135
|
+
log.out(`${chalk.bold(project.slug)} ${stats.totalKeys} keys ${chalk.green(`${stats.publishedKeys} published`)} ${chalk.yellow(`${stats.unpublishedKeys} draft`)}`);
|
|
136
|
+
table(coverage.map(c => [c.language, `${c.translated}/${total}`, c.missing ? chalk.yellow(`${c.percent}%`) : chalk.green('100%')]), ['LANG', 'TRANSLATED', 'DONE']);
|
|
324
137
|
}
|
|
325
|
-
//# sourceMappingURL=projects.js.map
|