lingo.dev 0.93.5 → 0.93.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 +95 -42
- package/build/cli.cjs +2083 -2033
- package/build/cli.cjs.map +1 -1
- package/build/cli.mjs +2073 -2023
- package/build/cli.mjs.map +1 -1
- package/package.json +1 -1
package/build/cli.mjs
CHANGED
|
@@ -5374,7 +5374,7 @@ var mcp_default = new Command13().command("mcp").description("Use Lingo.dev mode
|
|
|
5374
5374
|
});
|
|
5375
5375
|
|
|
5376
5376
|
// src/cli/cmd/ci/index.ts
|
|
5377
|
-
import { Command as
|
|
5377
|
+
import { Command as Command15 } from "interactive-commander";
|
|
5378
5378
|
import createOra from "ora";
|
|
5379
5379
|
|
|
5380
5380
|
// src/cli/cmd/ci/flows/pull-request.ts
|
|
@@ -5400,2039 +5400,1961 @@ function escapeShellArg(arg) {
|
|
|
5400
5400
|
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
5401
5401
|
}
|
|
5402
5402
|
|
|
5403
|
-
// src/cli/cmd/
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
if (hasChanges) {
|
|
5420
|
-
this.ora.start("Committing changes");
|
|
5421
|
-
execSync(`git add .`, { stdio: "inherit" });
|
|
5422
|
-
execSync(`git status --porcelain`, { stdio: "inherit" });
|
|
5423
|
-
execSync(
|
|
5424
|
-
`git commit -m ${escapeShellArg(this.platformKit.config.commitMessage)} --no-verify`,
|
|
5425
|
-
{
|
|
5426
|
-
stdio: "inherit"
|
|
5427
|
-
}
|
|
5428
|
-
);
|
|
5429
|
-
this.ora.succeed("Changes committed");
|
|
5430
|
-
this.ora.start("Pushing changes to remote");
|
|
5431
|
-
const currentBranch = this.i18nBranchName ?? this.platformKit.platformConfig.baseBranchName;
|
|
5432
|
-
execSync(
|
|
5433
|
-
`git push origin ${currentBranch} ${forcePush ? "--force" : ""}`,
|
|
5434
|
-
{
|
|
5435
|
-
stdio: "inherit"
|
|
5436
|
-
}
|
|
5437
|
-
);
|
|
5438
|
-
this.ora.succeed("Changes pushed to remote");
|
|
5439
|
-
}
|
|
5440
|
-
return hasChanges;
|
|
5441
|
-
}
|
|
5442
|
-
checkCommitableChanges() {
|
|
5443
|
-
return execSync('git status --porcelain || echo "has_changes"', {
|
|
5444
|
-
encoding: "utf8"
|
|
5445
|
-
}).length > 0;
|
|
5446
|
-
}
|
|
5447
|
-
async runLingoDotDev() {
|
|
5448
|
-
try {
|
|
5449
|
-
await i18n_default.exitOverride().parseAsync(["--api-key", this.platformKit.config.replexicaApiKey], {
|
|
5450
|
-
from: "user"
|
|
5451
|
-
});
|
|
5452
|
-
} catch (err) {
|
|
5453
|
-
if (err.code === "commander.helpDisplayed") return;
|
|
5454
|
-
throw err;
|
|
5455
|
-
}
|
|
5456
|
-
}
|
|
5457
|
-
configureGit() {
|
|
5458
|
-
const { processOwnCommits } = this.platformKit.config;
|
|
5459
|
-
const { baseBranchName } = this.platformKit.platformConfig;
|
|
5460
|
-
this.ora.info(`Current working directory:`);
|
|
5461
|
-
execSync(`pwd`, { stdio: "inherit" });
|
|
5462
|
-
execSync(`ls -la`, { stdio: "inherit" });
|
|
5463
|
-
execSync(`git config --global safe.directory ${process.cwd()}`);
|
|
5464
|
-
execSync(`git config user.name "${gitConfig.userName}"`);
|
|
5465
|
-
execSync(`git config user.email "${gitConfig.userEmail}"`);
|
|
5466
|
-
this.platformKit?.gitConfig();
|
|
5467
|
-
execSync(`git fetch origin ${baseBranchName}`, { stdio: "inherit" });
|
|
5468
|
-
execSync(`git checkout ${baseBranchName} --`, { stdio: "inherit" });
|
|
5469
|
-
if (!processOwnCommits) {
|
|
5470
|
-
const currentAuthor = `${gitConfig.userName} <${gitConfig.userEmail}>`;
|
|
5471
|
-
const authorOfLastCommit = execSync(
|
|
5472
|
-
`git log -1 --pretty=format:'%an <%ae>'`
|
|
5473
|
-
).toString();
|
|
5474
|
-
if (authorOfLastCommit === currentAuthor) {
|
|
5475
|
-
this.ora.warn(
|
|
5476
|
-
`The last commit was already made by ${currentAuthor}, so this run will be skipped, as running again would have no effect. See docs: https://docs.lingo.dev/ci-action/overview`
|
|
5477
|
-
);
|
|
5478
|
-
return false;
|
|
5479
|
-
}
|
|
5480
|
-
}
|
|
5481
|
-
const workingDir = path15.resolve(
|
|
5482
|
-
process.cwd(),
|
|
5483
|
-
this.platformKit.config.workingDir
|
|
5484
|
-
);
|
|
5485
|
-
if (workingDir !== process.cwd()) {
|
|
5486
|
-
this.ora.info(
|
|
5487
|
-
`Changing to working directory: ${this.platformKit.config.workingDir}`
|
|
5488
|
-
);
|
|
5489
|
-
process.chdir(workingDir);
|
|
5490
|
-
}
|
|
5491
|
-
return true;
|
|
5403
|
+
// src/cli/cmd/run/index.ts
|
|
5404
|
+
import { Command as Command14 } from "interactive-commander";
|
|
5405
|
+
|
|
5406
|
+
// src/cli/cmd/run/setup.ts
|
|
5407
|
+
import chalk9 from "chalk";
|
|
5408
|
+
import { Listr } from "listr2";
|
|
5409
|
+
|
|
5410
|
+
// src/cli/cmd/run/_const.ts
|
|
5411
|
+
import chalk6 from "chalk";
|
|
5412
|
+
import { ListrDefaultRendererLogLevels } from "listr2";
|
|
5413
|
+
var commonTaskRendererOptions = {
|
|
5414
|
+
color: {
|
|
5415
|
+
[ListrDefaultRendererLogLevels.COMPLETED]: (msg) => msg ? chalk6.hex(colors.green)(msg) : chalk6.hex(colors.green)("")
|
|
5416
|
+
},
|
|
5417
|
+
icon: {
|
|
5418
|
+
[ListrDefaultRendererLogLevels.COMPLETED]: chalk6.hex(colors.green)("\u2713")
|
|
5492
5419
|
}
|
|
5493
5420
|
};
|
|
5494
5421
|
|
|
5495
|
-
// src/cli/
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5422
|
+
// src/cli/localizer/lingodotdev.ts
|
|
5423
|
+
import dedent5 from "dedent";
|
|
5424
|
+
import chalk7 from "chalk";
|
|
5425
|
+
import { LingoDotDevEngine as LingoDotDevEngine2 } from "@lingo.dev/_sdk";
|
|
5426
|
+
function createLingoDotDevLocalizer(explicitApiKey) {
|
|
5427
|
+
const { auth } = getSettings(explicitApiKey);
|
|
5428
|
+
if (!auth) {
|
|
5429
|
+
throw new Error(
|
|
5430
|
+
dedent5`
|
|
5431
|
+
You're trying to use ${chalk7.hex(colors.green)("Lingo.dev")} provider, however, you are not authenticated.
|
|
5432
|
+
|
|
5433
|
+
To fix this issue:
|
|
5434
|
+
1. Run ${chalk7.dim("lingo.dev login")} to authenticate, or
|
|
5435
|
+
2. Use the ${chalk7.dim("--api-key")} flag to provide an API key.
|
|
5436
|
+
3. Set ${chalk7.dim("LINGODOTDEV_API_KEY")} environment variable.
|
|
5437
|
+
`
|
|
5506
5438
|
);
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5439
|
+
}
|
|
5440
|
+
const engine = new LingoDotDevEngine2({
|
|
5441
|
+
apiKey: auth.apiKey,
|
|
5442
|
+
apiUrl: auth.apiUrl
|
|
5443
|
+
});
|
|
5444
|
+
return {
|
|
5445
|
+
id: "Lingo.dev",
|
|
5446
|
+
checkAuth: async () => {
|
|
5447
|
+
try {
|
|
5448
|
+
const response = await engine.whoami();
|
|
5449
|
+
return {
|
|
5450
|
+
authenticated: !!response,
|
|
5451
|
+
username: response?.email
|
|
5452
|
+
};
|
|
5453
|
+
} catch {
|
|
5454
|
+
return { authenticated: false };
|
|
5455
|
+
}
|
|
5456
|
+
},
|
|
5457
|
+
localize: async (input2, onProgress) => {
|
|
5458
|
+
if (!Object.keys(input2.processableData).length) {
|
|
5459
|
+
return input2;
|
|
5460
|
+
}
|
|
5461
|
+
const processedData = await engine.localizeObject(
|
|
5462
|
+
input2.processableData,
|
|
5463
|
+
{
|
|
5464
|
+
sourceLocale: input2.sourceLocale,
|
|
5465
|
+
targetLocale: input2.targetLocale,
|
|
5466
|
+
reference: {
|
|
5467
|
+
[input2.sourceLocale]: input2.sourceData,
|
|
5468
|
+
[input2.targetLocale]: input2.targetData
|
|
5469
|
+
}
|
|
5470
|
+
},
|
|
5471
|
+
onProgress
|
|
5516
5472
|
);
|
|
5517
|
-
|
|
5518
|
-
this.ora.succeed(`Checked out and synced branch ${this.i18nBranchName}`);
|
|
5519
|
-
} else {
|
|
5520
|
-
this.ora.start(`Creating branch ${this.i18nBranchName}`);
|
|
5521
|
-
this.createI18nBranch(this.i18nBranchName);
|
|
5522
|
-
this.ora.succeed(`Created branch ${this.i18nBranchName}`);
|
|
5473
|
+
return processedData;
|
|
5523
5474
|
}
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5475
|
+
};
|
|
5476
|
+
}
|
|
5477
|
+
|
|
5478
|
+
// src/cli/localizer/explicit.ts
|
|
5479
|
+
import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
|
|
5480
|
+
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
5481
|
+
import chalk8 from "chalk";
|
|
5482
|
+
import dedent6 from "dedent";
|
|
5483
|
+
import { generateText as generateText2 } from "ai";
|
|
5484
|
+
import { jsonrepair as jsonrepair3 } from "jsonrepair";
|
|
5485
|
+
function createExplicitLocalizer(provider) {
|
|
5486
|
+
switch (provider.id) {
|
|
5487
|
+
default:
|
|
5531
5488
|
throw new Error(
|
|
5532
|
-
|
|
5489
|
+
dedent6`
|
|
5490
|
+
You're trying to use unsupported provider: ${chalk8.dim(provider.id)}.
|
|
5491
|
+
|
|
5492
|
+
To fix this issue:
|
|
5493
|
+
1. Switch to one of the supported providers, or
|
|
5494
|
+
2. Remove the ${chalk8.italic("provider")} node from your i18n.json configuration to switch to ${chalk8.hex(colors.green)("Lingo.dev")}
|
|
5495
|
+
|
|
5496
|
+
${chalk8.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
|
|
5497
|
+
`
|
|
5533
5498
|
);
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5499
|
+
case "openai":
|
|
5500
|
+
return createAiSdkLocalizer({
|
|
5501
|
+
factory: (params) => createOpenAI2(params).languageModel(provider.model),
|
|
5502
|
+
id: provider.id,
|
|
5503
|
+
prompt: provider.prompt,
|
|
5504
|
+
apiKeyName: "OPENAI_API_KEY",
|
|
5505
|
+
baseUrl: provider.baseUrl
|
|
5506
|
+
});
|
|
5507
|
+
case "anthropic":
|
|
5508
|
+
return createAiSdkLocalizer({
|
|
5509
|
+
factory: (params) => createAnthropic2(params).languageModel(provider.model),
|
|
5510
|
+
id: provider.id,
|
|
5511
|
+
prompt: provider.prompt,
|
|
5512
|
+
apiKeyName: "ANTHROPIC_API_KEY",
|
|
5513
|
+
baseUrl: provider.baseUrl
|
|
5514
|
+
});
|
|
5548
5515
|
}
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5516
|
+
}
|
|
5517
|
+
function createAiSdkLocalizer(params) {
|
|
5518
|
+
const apiKey = process.env[params.apiKeyName];
|
|
5519
|
+
if (!apiKey) {
|
|
5520
|
+
throw new Error(
|
|
5521
|
+
dedent6`
|
|
5522
|
+
You're trying to use raw ${chalk8.dim(params.id)} API for translation, however, ${chalk8.dim(params.apiKeyName)} environment variable is not set.
|
|
5523
|
+
|
|
5524
|
+
To fix this issue:
|
|
5525
|
+
1. Set ${chalk8.dim(params.apiKeyName)} in your environment variables, or
|
|
5526
|
+
2. Remove the ${chalk8.italic("provider")} node from your i18n.json configuration to switch to ${chalk8.hex(colors.green)("Lingo.dev")}
|
|
5527
|
+
|
|
5528
|
+
${chalk8.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
|
|
5529
|
+
`
|
|
5552
5530
|
);
|
|
5553
|
-
let prNumber = await this.platformKit.getOpenPullRequestNumber({
|
|
5554
|
-
branch: i18nBranchName
|
|
5555
|
-
});
|
|
5556
|
-
if (prNumber) {
|
|
5557
|
-
this.ora.succeed(`Existing PR found: #${prNumber}`);
|
|
5558
|
-
} else {
|
|
5559
|
-
this.ora.start(`Creating new PR`);
|
|
5560
|
-
prNumber = await this.platformKit.createPullRequest({
|
|
5561
|
-
head: i18nBranchName,
|
|
5562
|
-
title: this.platformKit.config.pullRequestTitle,
|
|
5563
|
-
body: this.getPrBodyContent()
|
|
5564
|
-
});
|
|
5565
|
-
this.ora.succeed(`Created new PR: #${prNumber}`);
|
|
5566
|
-
}
|
|
5567
|
-
return prNumber;
|
|
5568
5531
|
}
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5532
|
+
const model = params.factory({
|
|
5533
|
+
apiKey,
|
|
5534
|
+
baseUrl: params.baseUrl
|
|
5535
|
+
});
|
|
5536
|
+
return {
|
|
5537
|
+
id: params.id,
|
|
5538
|
+
checkAuth: async () => {
|
|
5539
|
+
try {
|
|
5540
|
+
await generateText2({
|
|
5541
|
+
model,
|
|
5542
|
+
messages: [
|
|
5543
|
+
{ role: "system", content: "You are an echo server" },
|
|
5544
|
+
{ role: "user", content: "OK" },
|
|
5545
|
+
{ role: "assistant", content: "OK" },
|
|
5546
|
+
{ role: "user", content: "OK" }
|
|
5547
|
+
]
|
|
5548
|
+
});
|
|
5549
|
+
return { authenticated: true, username: "anonymous" };
|
|
5550
|
+
} catch (error) {
|
|
5551
|
+
return { authenticated: false };
|
|
5552
|
+
}
|
|
5553
|
+
},
|
|
5554
|
+
localize: async (input2) => {
|
|
5555
|
+
const systemPrompt = params.prompt.replaceAll("{source}", input2.sourceLocale).replaceAll("{target}", input2.targetLocale);
|
|
5556
|
+
const shots = [
|
|
5557
|
+
[
|
|
5558
|
+
{
|
|
5559
|
+
sourceLocale: "en",
|
|
5560
|
+
targetLocale: "es",
|
|
5561
|
+
data: {
|
|
5562
|
+
message: "Hello, world!"
|
|
5563
|
+
}
|
|
5564
|
+
},
|
|
5565
|
+
{
|
|
5566
|
+
sourceLocale: "en",
|
|
5567
|
+
targetLocale: "es",
|
|
5568
|
+
data: {
|
|
5569
|
+
message: "Hola, mundo!"
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
]
|
|
5573
|
+
];
|
|
5574
|
+
const payload = {
|
|
5575
|
+
sourceLocale: input2.sourceLocale,
|
|
5576
|
+
targetLocale: input2.targetLocale,
|
|
5577
|
+
data: input2.processableData
|
|
5578
|
+
};
|
|
5579
|
+
const response = await generateText2({
|
|
5580
|
+
model,
|
|
5581
|
+
messages: [
|
|
5582
|
+
{ role: "system", content: systemPrompt },
|
|
5583
|
+
{ role: "user", content: "OK" },
|
|
5584
|
+
...shots.flatMap(
|
|
5585
|
+
([userShot, assistantShot]) => [
|
|
5586
|
+
{ role: "user", content: JSON.stringify(userShot) },
|
|
5587
|
+
{ role: "assistant", content: JSON.stringify(assistantShot) }
|
|
5588
|
+
]
|
|
5589
|
+
),
|
|
5590
|
+
{ role: "user", content: JSON.stringify(payload) }
|
|
5591
|
+
]
|
|
5592
|
+
});
|
|
5593
|
+
const result = JSON.parse(response.text);
|
|
5594
|
+
const index = result.data.indexOf("{");
|
|
5595
|
+
const lastIndex = result.data.lastIndexOf("}");
|
|
5596
|
+
const trimmed = result.data.slice(index, lastIndex + 1);
|
|
5597
|
+
const repaired = jsonrepair3(trimmed);
|
|
5598
|
+
const finalResult = JSON.parse(repaired);
|
|
5599
|
+
return finalResult.data;
|
|
5600
|
+
}
|
|
5601
|
+
};
|
|
5602
|
+
}
|
|
5603
|
+
|
|
5604
|
+
// src/cli/localizer/index.ts
|
|
5605
|
+
function createLocalizer(provider, apiKey) {
|
|
5606
|
+
if (!provider) {
|
|
5607
|
+
return createLingoDotDevLocalizer(apiKey);
|
|
5608
|
+
} else {
|
|
5609
|
+
return createExplicitLocalizer(provider);
|
|
5574
5610
|
}
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5611
|
+
}
|
|
5612
|
+
|
|
5613
|
+
// src/cli/cmd/run/setup.ts
|
|
5614
|
+
async function setup(input2) {
|
|
5615
|
+
console.log(chalk9.hex(colors.orange)("[Setup]"));
|
|
5616
|
+
return new Listr(
|
|
5617
|
+
[
|
|
5618
|
+
{
|
|
5619
|
+
title: "Setting up the environment",
|
|
5620
|
+
task: async (ctx, task) => {
|
|
5621
|
+
task.title = `Environment setup completed`;
|
|
5585
5622
|
}
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5623
|
+
},
|
|
5624
|
+
{
|
|
5625
|
+
title: "Loading i18n configuration",
|
|
5626
|
+
task: async (ctx, task) => {
|
|
5627
|
+
ctx.config = getConfig(true);
|
|
5628
|
+
if (!ctx.config) {
|
|
5629
|
+
throw new Error(
|
|
5630
|
+
"i18n.json not found. Please run `lingo.dev init` to initialize the project."
|
|
5631
|
+
);
|
|
5632
|
+
} else if (!ctx.config.buckets || !Object.keys(ctx.config.buckets).length) {
|
|
5633
|
+
throw new Error(
|
|
5634
|
+
"No buckets found in i18n.json. Please add at least one bucket containing i18n content."
|
|
5635
|
+
);
|
|
5636
|
+
} else if (ctx.flags.bucket?.some(
|
|
5637
|
+
(bucket) => !ctx.config?.buckets[bucket]
|
|
5638
|
+
)) {
|
|
5639
|
+
throw new Error(
|
|
5640
|
+
`One or more specified buckets do not exist in i18n.json. Please add them to the list first and try again.`
|
|
5641
|
+
);
|
|
5642
|
+
}
|
|
5643
|
+
task.title = `Loaded i18n configuration`;
|
|
5644
|
+
}
|
|
5645
|
+
},
|
|
5646
|
+
{
|
|
5647
|
+
title: "Selecting localization provider",
|
|
5648
|
+
task: async (ctx, task) => {
|
|
5649
|
+
ctx.localizer = createLocalizer(
|
|
5650
|
+
ctx.config?.provider,
|
|
5651
|
+
ctx.flags.apiKey
|
|
5652
|
+
);
|
|
5653
|
+
if (!ctx.localizer) {
|
|
5654
|
+
throw new Error(
|
|
5655
|
+
"Could not create localization provider. Please check your i18n.json configuration."
|
|
5656
|
+
);
|
|
5657
|
+
}
|
|
5658
|
+
task.title = ctx.localizer.id === "Lingo.dev" ? `Using ${chalk9.hex(colors.green)(ctx.localizer.id)} provider` : `Using raw ${chalk9.hex(colors.yellow)(ctx.localizer.id)} API`;
|
|
5659
|
+
}
|
|
5660
|
+
},
|
|
5661
|
+
{
|
|
5662
|
+
title: "Checking authentication",
|
|
5663
|
+
task: async (ctx, task) => {
|
|
5664
|
+
const authStatus = await ctx.localizer.checkAuth();
|
|
5665
|
+
if (!authStatus.authenticated) {
|
|
5666
|
+
throw new Error(
|
|
5667
|
+
`Failed to authenticate with ${chalk9.hex(colors.yellow)(ctx.localizer.id)} provider. Please check your API key and try again.`
|
|
5668
|
+
);
|
|
5669
|
+
}
|
|
5670
|
+
task.title = `Authenticated as ${chalk9.hex(colors.yellow)(authStatus.username)}`;
|
|
5671
|
+
}
|
|
5672
|
+
},
|
|
5673
|
+
{
|
|
5674
|
+
title: "Initializing localization provider",
|
|
5675
|
+
async task(ctx, task) {
|
|
5676
|
+
const isLingoDotDev = ctx.localizer.id === "Lingo.dev";
|
|
5677
|
+
const subTasks = isLingoDotDev ? [
|
|
5678
|
+
"Brand voice enabled",
|
|
5679
|
+
"Translation memory connected",
|
|
5680
|
+
"Glossary enabled",
|
|
5681
|
+
"Quality assurance enabled"
|
|
5682
|
+
].map((title) => ({ title, task: () => {
|
|
5683
|
+
} })) : [
|
|
5684
|
+
"Skipping brand voice",
|
|
5685
|
+
"Skipping glossary",
|
|
5686
|
+
"Skipping translation memory",
|
|
5687
|
+
"Skipping quality assurance"
|
|
5688
|
+
].map((title) => ({ title, task: () => {
|
|
5689
|
+
}, skip: true }));
|
|
5690
|
+
return task.newListr(subTasks, {
|
|
5691
|
+
concurrent: true,
|
|
5692
|
+
rendererOptions: { collapseSubtasks: false }
|
|
5693
|
+
});
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
],
|
|
5697
|
+
{
|
|
5698
|
+
rendererOptions: commonTaskRendererOptions
|
|
5597
5699
|
}
|
|
5700
|
+
).run(input2);
|
|
5701
|
+
}
|
|
5702
|
+
|
|
5703
|
+
// src/cli/cmd/run/plan.ts
|
|
5704
|
+
import chalk10 from "chalk";
|
|
5705
|
+
import { Listr as Listr2 } from "listr2";
|
|
5706
|
+
import { resolveOverriddenLocale as resolveOverriddenLocale6 } from "@lingo.dev/_spec";
|
|
5707
|
+
async function plan(input2) {
|
|
5708
|
+
console.log(chalk10.hex(colors.orange)("[Planning]"));
|
|
5709
|
+
let buckets = getBuckets(input2.config);
|
|
5710
|
+
if (input2.flags.bucket) {
|
|
5711
|
+
buckets = buckets.filter((b) => input2.flags.bucket.includes(b.type));
|
|
5598
5712
|
}
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
this.ora.start(
|
|
5604
|
-
`Fetching latest changes from ${this.platformKit.platformConfig.baseBranchName}`
|
|
5605
|
-
);
|
|
5606
|
-
execSync2(
|
|
5607
|
-
`git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
|
|
5608
|
-
{ stdio: "inherit" }
|
|
5713
|
+
const _sourceLocale = input2.flags.sourceLocale || input2.config.locale.source;
|
|
5714
|
+
if (!_sourceLocale) {
|
|
5715
|
+
throw new Error(
|
|
5716
|
+
`No source locale provided. Use --source-locale to specify the source locale or add it to i18n.json (locale.source)`
|
|
5609
5717
|
);
|
|
5610
|
-
|
|
5611
|
-
|
|
5718
|
+
}
|
|
5719
|
+
const _targetLocales = input2.flags.targetLocale || input2.config.locale.targets;
|
|
5720
|
+
if (!_targetLocales.length) {
|
|
5721
|
+
throw new Error(
|
|
5722
|
+
`No target locales provided. Use --target-locale to specify the target locales or add them to i18n.json (locale.targets)`
|
|
5612
5723
|
);
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
this.ora.start("Aborting failed rebase");
|
|
5623
|
-
execSync2("git rebase --abort", { stdio: "inherit" });
|
|
5624
|
-
this.ora.succeed("Aborted failed rebase");
|
|
5625
|
-
this.ora.start(
|
|
5626
|
-
`Resetting to ${this.platformKit.platformConfig.baseBranchName}`
|
|
5627
|
-
);
|
|
5628
|
-
execSync2(
|
|
5629
|
-
`git reset --hard origin/${this.platformKit.platformConfig.baseBranchName}`,
|
|
5630
|
-
{ stdio: "inherit" }
|
|
5631
|
-
);
|
|
5632
|
-
this.ora.succeed(
|
|
5633
|
-
`Reset to ${this.platformKit.platformConfig.baseBranchName}`
|
|
5634
|
-
);
|
|
5635
|
-
this.ora.start("Restoring target files");
|
|
5636
|
-
const targetFiles = ["i18n.lock"];
|
|
5637
|
-
const targetFileNames = execSync2(
|
|
5638
|
-
`npx lingo.dev@latest show files --target ${this.platformKit.platformConfig.baseBranchName}`,
|
|
5639
|
-
{ encoding: "utf8" }
|
|
5640
|
-
).split("\n").filter(Boolean);
|
|
5641
|
-
targetFiles.push(...targetFileNames);
|
|
5642
|
-
execSync2(`git fetch origin ${this.i18nBranchName}`, { stdio: "inherit" });
|
|
5643
|
-
for (const file of targetFiles) {
|
|
5644
|
-
try {
|
|
5645
|
-
execSync2(`git checkout FETCH_HEAD -- ${file}`, { stdio: "inherit" });
|
|
5646
|
-
} catch (error2) {
|
|
5647
|
-
this.ora.warn(`Skipping non-existent file: ${file}`);
|
|
5648
|
-
continue;
|
|
5724
|
+
}
|
|
5725
|
+
return new Listr2(
|
|
5726
|
+
[
|
|
5727
|
+
{
|
|
5728
|
+
title: "Locating content buckets",
|
|
5729
|
+
task: async (ctx, task) => {
|
|
5730
|
+
const bucketCount = buckets.length;
|
|
5731
|
+
const bucketFilter = input2.flags.bucket ? ` ${chalk10.dim(`(filtered by: ${chalk10.hex(colors.yellow)(input2.flags.bucket.join(", "))})`)}` : "";
|
|
5732
|
+
task.title = `Found ${chalk10.hex(colors.yellow)(bucketCount.toString())} bucket(s)${bucketFilter}`;
|
|
5649
5733
|
}
|
|
5650
|
-
}
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
if (hasChanges) {
|
|
5656
|
-
execSync2("git add .", { stdio: "inherit" });
|
|
5657
|
-
execSync2(
|
|
5658
|
-
`git commit -m "chore: sync with ${this.platformKit.platformConfig.baseBranchName}" --no-verify`,
|
|
5659
|
-
{
|
|
5660
|
-
stdio: "inherit"
|
|
5734
|
+
},
|
|
5735
|
+
{
|
|
5736
|
+
title: "Detecting locales",
|
|
5737
|
+
task: async (ctx, task) => {
|
|
5738
|
+
task.title = `Found ${chalk10.hex(colors.yellow)(_targetLocales.length.toString())} target locale(s)`;
|
|
5661
5739
|
}
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5740
|
+
},
|
|
5741
|
+
{
|
|
5742
|
+
title: "Locating localizable files",
|
|
5743
|
+
task: async (ctx, task) => {
|
|
5744
|
+
const patterns = [];
|
|
5745
|
+
for (const bucket of buckets) {
|
|
5746
|
+
for (const bucketPath of bucket.paths) {
|
|
5747
|
+
if (input2.flags.file) {
|
|
5748
|
+
if (!input2.flags.file.some(
|
|
5749
|
+
(f) => bucketPath.pathPattern.includes(f)
|
|
5750
|
+
)) {
|
|
5751
|
+
continue;
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
patterns.push(bucketPath.pathPattern);
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
const fileFilter = input2.flags.file ? ` ${chalk10.dim(`(filtered by: ${chalk10.hex(colors.yellow)(input2.flags.file.join(", "))})`)}` : "";
|
|
5758
|
+
task.title = `Found ${chalk10.hex(colors.yellow)(patterns.length.toString())} path pattern(s)${fileFilter}`;
|
|
5759
|
+
}
|
|
5760
|
+
},
|
|
5761
|
+
{
|
|
5762
|
+
title: "Computing translation tasks",
|
|
5763
|
+
task: async (ctx, task) => {
|
|
5764
|
+
for (const bucket of buckets) {
|
|
5765
|
+
for (const bucketPath of bucket.paths) {
|
|
5766
|
+
if (input2.flags.file) {
|
|
5767
|
+
if (!input2.flags.file.some(
|
|
5768
|
+
(f) => bucketPath.pathPattern.includes(f)
|
|
5769
|
+
)) {
|
|
5770
|
+
continue;
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
const sourceLocale = resolveOverriddenLocale6(
|
|
5774
|
+
_sourceLocale,
|
|
5775
|
+
bucketPath.delimiter
|
|
5776
|
+
);
|
|
5777
|
+
for (const _targetLocale of _targetLocales) {
|
|
5778
|
+
const targetLocale = resolveOverriddenLocale6(
|
|
5779
|
+
_targetLocale,
|
|
5780
|
+
bucketPath.delimiter
|
|
5781
|
+
);
|
|
5782
|
+
if (sourceLocale === targetLocale) continue;
|
|
5783
|
+
ctx.tasks.push({
|
|
5784
|
+
sourceLocale,
|
|
5785
|
+
targetLocale,
|
|
5786
|
+
bucketType: bucket.type,
|
|
5787
|
+
bucketPathPattern: bucketPath.pathPattern,
|
|
5788
|
+
injectLocale: bucket.injectLocale || [],
|
|
5789
|
+
lockedKeys: bucket.lockedKeys || [],
|
|
5790
|
+
lockedPatterns: bucket.lockedPatterns || []
|
|
5791
|
+
});
|
|
5792
|
+
}
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
task.title = `Prepared ${chalk10.hex(colors.green)(ctx.tasks.length.toString())} translation task(s)`;
|
|
5796
|
+
}
|
|
5797
|
+
}
|
|
5798
|
+
],
|
|
5799
|
+
{
|
|
5800
|
+
rendererOptions: commonTaskRendererOptions
|
|
5703
5801
|
}
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
const env = Z7.object({
|
|
5707
|
-
LINGODOTDEV_API_KEY: Z7.string(),
|
|
5708
|
-
LINGODOTDEV_PULL_REQUEST: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()),
|
|
5709
|
-
LINGODOTDEV_COMMIT_MESSAGE: Z7.string().optional(),
|
|
5710
|
-
LINGODOTDEV_PULL_REQUEST_TITLE: Z7.string().optional(),
|
|
5711
|
-
LINGODOTDEV_WORKING_DIRECTORY: Z7.string().optional(),
|
|
5712
|
-
LINGODOTDEV_PROCESS_OWN_COMMITS: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()).optional()
|
|
5713
|
-
}).parse(process.env);
|
|
5714
|
-
return {
|
|
5715
|
-
replexicaApiKey: env.LINGODOTDEV_API_KEY,
|
|
5716
|
-
isPullRequestMode: env.LINGODOTDEV_PULL_REQUEST,
|
|
5717
|
-
commitMessage: env.LINGODOTDEV_COMMIT_MESSAGE || defaultMessage,
|
|
5718
|
-
pullRequestTitle: env.LINGODOTDEV_PULL_REQUEST_TITLE || defaultMessage,
|
|
5719
|
-
workingDir: env.LINGODOTDEV_WORKING_DIRECTORY || ".",
|
|
5720
|
-
processOwnCommits: env.LINGODOTDEV_PROCESS_OWN_COMMITS || false
|
|
5721
|
-
};
|
|
5722
|
-
}
|
|
5723
|
-
};
|
|
5802
|
+
).run(input2);
|
|
5803
|
+
}
|
|
5724
5804
|
|
|
5725
|
-
// src/cli/cmd/
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
async getOpenPullRequestNumber({ branch }) {
|
|
5745
|
-
return await this.bb.repositories.listPullRequests({
|
|
5746
|
-
workspace: this.platformConfig.repositoryOwner,
|
|
5747
|
-
repo_slug: this.platformConfig.repositoryName,
|
|
5748
|
-
state: "OPEN"
|
|
5749
|
-
}).then(({ data: { values } }) => {
|
|
5750
|
-
return values?.find(
|
|
5751
|
-
({ source, destination }) => source?.branch?.name === branch && destination?.branch?.name === this.platformConfig.baseBranchName
|
|
5752
|
-
);
|
|
5753
|
-
}).then((pr) => pr?.id);
|
|
5754
|
-
}
|
|
5755
|
-
async closePullRequest({ pullRequestNumber }) {
|
|
5756
|
-
await this.bb.repositories.declinePullRequest({
|
|
5757
|
-
workspace: this.platformConfig.repositoryOwner,
|
|
5758
|
-
repo_slug: this.platformConfig.repositoryName,
|
|
5759
|
-
pull_request_id: pullRequestNumber
|
|
5760
|
-
});
|
|
5761
|
-
}
|
|
5762
|
-
async createPullRequest({
|
|
5763
|
-
title,
|
|
5764
|
-
body,
|
|
5765
|
-
head
|
|
5766
|
-
}) {
|
|
5767
|
-
return await this.bb.repositories.createPullRequest({
|
|
5768
|
-
workspace: this.platformConfig.repositoryOwner,
|
|
5769
|
-
repo_slug: this.platformConfig.repositoryName,
|
|
5770
|
-
_body: {
|
|
5771
|
-
title,
|
|
5772
|
-
description: body,
|
|
5773
|
-
source: { branch: { name: head } },
|
|
5774
|
-
destination: { branch: { name: this.platformConfig.baseBranchName } }
|
|
5775
|
-
}
|
|
5776
|
-
}).then(({ data }) => data.id ?? 0);
|
|
5777
|
-
}
|
|
5778
|
-
async commentOnPullRequest({
|
|
5779
|
-
pullRequestNumber,
|
|
5780
|
-
body
|
|
5781
|
-
}) {
|
|
5782
|
-
await this.bb.repositories.createPullRequestComment({
|
|
5783
|
-
workspace: this.platformConfig.repositoryOwner,
|
|
5784
|
-
repo_slug: this.platformConfig.repositoryName,
|
|
5785
|
-
pull_request_id: pullRequestNumber,
|
|
5786
|
-
_body: {
|
|
5787
|
-
content: {
|
|
5788
|
-
raw: body
|
|
5805
|
+
// src/cli/cmd/run/execute.ts
|
|
5806
|
+
import chalk11 from "chalk";
|
|
5807
|
+
import { Listr as Listr3 } from "listr2";
|
|
5808
|
+
import pLimit from "p-limit";
|
|
5809
|
+
import _32 from "lodash";
|
|
5810
|
+
var MAX_WORKER_COUNT = 10;
|
|
5811
|
+
async function execute(input2) {
|
|
5812
|
+
const effectiveConcurrency = Math.min(
|
|
5813
|
+
input2.flags.concurrency,
|
|
5814
|
+
input2.tasks.length,
|
|
5815
|
+
MAX_WORKER_COUNT
|
|
5816
|
+
);
|
|
5817
|
+
console.log(chalk11.hex(colors.orange)(`[Localization]`));
|
|
5818
|
+
return new Listr3(
|
|
5819
|
+
[
|
|
5820
|
+
{
|
|
5821
|
+
title: "Initializing localization engine",
|
|
5822
|
+
task: async (ctx, task) => {
|
|
5823
|
+
task.title = `Localization engine ${chalk11.hex(colors.green)("ready")} (${ctx.localizer.id})`;
|
|
5789
5824
|
}
|
|
5790
|
-
}
|
|
5791
|
-
});
|
|
5792
|
-
}
|
|
5793
|
-
async gitConfig() {
|
|
5794
|
-
execSync4("git config --unset http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy", {
|
|
5795
|
-
stdio: "inherit"
|
|
5796
|
-
});
|
|
5797
|
-
execSync4(
|
|
5798
|
-
"git config http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy http://host.docker.internal:29418/",
|
|
5825
|
+
},
|
|
5799
5826
|
{
|
|
5800
|
-
|
|
5827
|
+
title: `Processing localization tasks ${chalk11.dim(`(tasks: ${input2.tasks.length}, concurrency: ${effectiveConcurrency})`)}`,
|
|
5828
|
+
task: (ctx, task) => {
|
|
5829
|
+
if (input2.tasks.length < 1) {
|
|
5830
|
+
task.title = `Skipping, nothing to localize.`;
|
|
5831
|
+
task.skip();
|
|
5832
|
+
return;
|
|
5833
|
+
}
|
|
5834
|
+
const i18nLimiter = pLimit(effectiveConcurrency);
|
|
5835
|
+
const ioLimiter = pLimit(1);
|
|
5836
|
+
const workersCount = effectiveConcurrency;
|
|
5837
|
+
const workerTasks = [];
|
|
5838
|
+
for (let i = 0; i < workersCount; i++) {
|
|
5839
|
+
const assignedTasks = ctx.tasks.filter(
|
|
5840
|
+
(_33, idx) => idx % workersCount === i
|
|
5841
|
+
);
|
|
5842
|
+
workerTasks.push(
|
|
5843
|
+
createWorkerTask({
|
|
5844
|
+
ctx,
|
|
5845
|
+
assignedTasks,
|
|
5846
|
+
ioLimiter,
|
|
5847
|
+
i18nLimiter,
|
|
5848
|
+
onDone() {
|
|
5849
|
+
task.title = createExecutionProgressMessage(ctx);
|
|
5850
|
+
}
|
|
5851
|
+
})
|
|
5852
|
+
);
|
|
5853
|
+
}
|
|
5854
|
+
return task.newListr(workerTasks, {
|
|
5855
|
+
concurrent: true,
|
|
5856
|
+
exitOnError: false,
|
|
5857
|
+
rendererOptions: {
|
|
5858
|
+
...commonTaskRendererOptions,
|
|
5859
|
+
collapseSubtasks: true
|
|
5860
|
+
}
|
|
5861
|
+
});
|
|
5862
|
+
}
|
|
5801
5863
|
}
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5864
|
+
],
|
|
5865
|
+
{
|
|
5866
|
+
exitOnError: false,
|
|
5867
|
+
rendererOptions: commonTaskRendererOptions
|
|
5868
|
+
}
|
|
5869
|
+
).run(input2);
|
|
5870
|
+
}
|
|
5871
|
+
function createWorkerStatusMessage(args) {
|
|
5872
|
+
const displayPath = args.assignedTask.bucketPathPattern.replace(
|
|
5873
|
+
"[locale]",
|
|
5874
|
+
args.assignedTask.targetLocale
|
|
5875
|
+
);
|
|
5876
|
+
return `[${chalk11.hex(colors.yellow)(`${args.percentage}%`)}] Processing: ${chalk11.dim(
|
|
5877
|
+
displayPath
|
|
5878
|
+
)} (${chalk11.hex(colors.yellow)(args.assignedTask.sourceLocale)} -> ${chalk11.hex(
|
|
5879
|
+
colors.yellow
|
|
5880
|
+
)(args.assignedTask.targetLocale)})`;
|
|
5881
|
+
}
|
|
5882
|
+
function createExecutionProgressMessage(ctx) {
|
|
5883
|
+
const succeededTasksCount = countTasks(
|
|
5884
|
+
ctx,
|
|
5885
|
+
(_t, result) => result.status === "success"
|
|
5886
|
+
);
|
|
5887
|
+
const failedTasksCount = countTasks(
|
|
5888
|
+
ctx,
|
|
5889
|
+
(_t, result) => result.status === "error"
|
|
5890
|
+
);
|
|
5891
|
+
const skippedTasksCount = countTasks(
|
|
5892
|
+
ctx,
|
|
5893
|
+
(_t, result) => result.status === "skipped"
|
|
5894
|
+
);
|
|
5895
|
+
return `Processed ${chalk11.green(succeededTasksCount)}/${ctx.tasks.length}, Failed ${chalk11.red(failedTasksCount)}, Skipped ${chalk11.dim(skippedTasksCount)}`;
|
|
5896
|
+
}
|
|
5897
|
+
function createLoaderForTask(assignedTask) {
|
|
5898
|
+
const bucketLoader = createBucketLoader(
|
|
5899
|
+
assignedTask.bucketType,
|
|
5900
|
+
assignedTask.bucketPathPattern,
|
|
5901
|
+
{
|
|
5902
|
+
defaultLocale: assignedTask.sourceLocale,
|
|
5903
|
+
isCacheRestore: false,
|
|
5904
|
+
injectLocale: assignedTask.injectLocale
|
|
5905
|
+
},
|
|
5906
|
+
assignedTask.lockedKeys,
|
|
5907
|
+
assignedTask.lockedPatterns
|
|
5908
|
+
);
|
|
5909
|
+
bucketLoader.setDefaultLocale(assignedTask.sourceLocale);
|
|
5910
|
+
return bucketLoader;
|
|
5911
|
+
}
|
|
5912
|
+
function createWorkerTask(args) {
|
|
5913
|
+
return {
|
|
5914
|
+
title: "Initializing...",
|
|
5915
|
+
task: async (_subCtx, subTask) => {
|
|
5916
|
+
for (const assignedTask of args.assignedTasks) {
|
|
5917
|
+
subTask.title = createWorkerStatusMessage({
|
|
5918
|
+
assignedTask,
|
|
5919
|
+
percentage: 0
|
|
5920
|
+
});
|
|
5921
|
+
const bucketLoader = createLoaderForTask(assignedTask);
|
|
5922
|
+
const deltaProcessor = createDeltaProcessor(
|
|
5923
|
+
assignedTask.bucketPathPattern
|
|
5924
|
+
);
|
|
5925
|
+
const taskResult = await args.i18nLimiter(async () => {
|
|
5926
|
+
try {
|
|
5927
|
+
const sourceData = await bucketLoader.pull(
|
|
5928
|
+
assignedTask.sourceLocale
|
|
5929
|
+
);
|
|
5930
|
+
const targetData = await bucketLoader.pull(
|
|
5931
|
+
assignedTask.targetLocale
|
|
5932
|
+
);
|
|
5933
|
+
const checksums = await deltaProcessor.loadChecksums();
|
|
5934
|
+
const delta = await deltaProcessor.calculateDelta({
|
|
5935
|
+
sourceData,
|
|
5936
|
+
targetData,
|
|
5937
|
+
checksums
|
|
5938
|
+
});
|
|
5939
|
+
const processableData = _32.chain(sourceData).entries().filter(
|
|
5940
|
+
([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!args.ctx.flags.force
|
|
5941
|
+
).fromPairs().value();
|
|
5942
|
+
if (!Object.keys(processableData).length) {
|
|
5943
|
+
return { status: "skipped" };
|
|
5944
|
+
}
|
|
5945
|
+
const processedTargetData = await args.ctx.localizer.localize(
|
|
5946
|
+
{
|
|
5947
|
+
sourceLocale: assignedTask.sourceLocale,
|
|
5948
|
+
targetLocale: assignedTask.targetLocale,
|
|
5949
|
+
sourceData,
|
|
5950
|
+
targetData,
|
|
5951
|
+
processableData
|
|
5952
|
+
},
|
|
5953
|
+
(progress) => {
|
|
5954
|
+
subTask.title = createWorkerStatusMessage({
|
|
5955
|
+
assignedTask,
|
|
5956
|
+
percentage: progress
|
|
5957
|
+
});
|
|
5958
|
+
}
|
|
5959
|
+
);
|
|
5960
|
+
let finalTargetData = _32.merge(
|
|
5961
|
+
{},
|
|
5962
|
+
sourceData,
|
|
5963
|
+
targetData,
|
|
5964
|
+
processedTargetData
|
|
5965
|
+
);
|
|
5966
|
+
finalTargetData = _32.chain(finalTargetData).entries().map(([key, value]) => {
|
|
5967
|
+
const renaming = delta.renamed.find(
|
|
5968
|
+
([oldKey]) => oldKey === key
|
|
5969
|
+
);
|
|
5970
|
+
if (!renaming) {
|
|
5971
|
+
return [key, value];
|
|
5972
|
+
}
|
|
5973
|
+
return [renaming[1], value];
|
|
5974
|
+
}).fromPairs().value();
|
|
5975
|
+
await args.ioLimiter(async () => {
|
|
5976
|
+
await bucketLoader.pull(assignedTask.sourceLocale);
|
|
5977
|
+
await bucketLoader.push(
|
|
5978
|
+
assignedTask.targetLocale,
|
|
5979
|
+
finalTargetData
|
|
5980
|
+
);
|
|
5981
|
+
const checksums2 = await deltaProcessor.createChecksums(sourceData);
|
|
5982
|
+
await deltaProcessor.saveChecksums(checksums2);
|
|
5983
|
+
});
|
|
5984
|
+
return { status: "success" };
|
|
5985
|
+
} catch (error) {
|
|
5986
|
+
return {
|
|
5987
|
+
status: "error",
|
|
5988
|
+
error
|
|
5989
|
+
};
|
|
5990
|
+
}
|
|
5991
|
+
});
|
|
5992
|
+
args.ctx.results.set(assignedTask, taskResult);
|
|
5993
|
+
}
|
|
5994
|
+
subTask.title = "Done";
|
|
5995
|
+
}
|
|
5996
|
+
};
|
|
5997
|
+
}
|
|
5998
|
+
function countTasks(ctx, predicate) {
|
|
5999
|
+
return Array.from(ctx.results.entries()).filter(
|
|
6000
|
+
([task, result]) => predicate(task, result)
|
|
6001
|
+
).length;
|
|
6002
|
+
}
|
|
6003
|
+
|
|
6004
|
+
// src/cli/cmd/run/_types.ts
|
|
6005
|
+
import {
|
|
6006
|
+
bucketTypeSchema as bucketTypeSchema3
|
|
6007
|
+
} from "@lingo.dev/_spec";
|
|
6008
|
+
import { z as z2 } from "zod";
|
|
6009
|
+
var flagsSchema2 = z2.object({
|
|
6010
|
+
bucket: z2.array(bucketTypeSchema3).optional(),
|
|
6011
|
+
key: z2.array(z2.string()).optional(),
|
|
6012
|
+
file: z2.array(z2.string()).optional(),
|
|
6013
|
+
apiKey: z2.string().optional(),
|
|
6014
|
+
force: z2.boolean().optional(),
|
|
6015
|
+
frozen: z2.boolean().optional(),
|
|
6016
|
+
verbose: z2.boolean().optional(),
|
|
6017
|
+
strict: z2.boolean().optional(),
|
|
6018
|
+
interactive: z2.boolean().default(false),
|
|
6019
|
+
concurrency: z2.number().positive().default(10),
|
|
6020
|
+
debug: z2.boolean().default(false),
|
|
6021
|
+
sourceLocale: z2.string().optional(),
|
|
6022
|
+
targetLocale: z2.array(z2.string()).optional()
|
|
6023
|
+
});
|
|
6024
|
+
|
|
6025
|
+
// src/cli/cmd/run/_render.ts
|
|
6026
|
+
import chalk12 from "chalk";
|
|
6027
|
+
import figlet from "figlet";
|
|
6028
|
+
import { vice } from "gradient-string";
|
|
6029
|
+
import readline2 from "readline";
|
|
6030
|
+
async function renderClear() {
|
|
6031
|
+
console.log("\x1Bc");
|
|
6032
|
+
}
|
|
6033
|
+
async function renderSpacer() {
|
|
6034
|
+
console.log(" ");
|
|
6035
|
+
}
|
|
6036
|
+
async function renderBanner() {
|
|
6037
|
+
console.log(
|
|
6038
|
+
vice(
|
|
6039
|
+
figlet.textSync("LINGO.DEV", {
|
|
6040
|
+
font: "ANSI Shadow",
|
|
6041
|
+
horizontalLayout: "default",
|
|
6042
|
+
verticalLayout: "default"
|
|
6043
|
+
})
|
|
6044
|
+
)
|
|
6045
|
+
);
|
|
6046
|
+
}
|
|
6047
|
+
async function renderHero() {
|
|
6048
|
+
console.log(
|
|
6049
|
+
`\u26A1\uFE0F ${chalk12.hex(colors.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
|
|
6050
|
+
);
|
|
6051
|
+
console.log("");
|
|
6052
|
+
const label1 = "\u2B50 GitHub Repo:";
|
|
6053
|
+
const label2 = "\u{1F4DA} Docs:";
|
|
6054
|
+
const label3 = "\u{1F4AC} 24/7 Support:";
|
|
6055
|
+
const maxLabelWidth = 17;
|
|
6056
|
+
console.log(
|
|
6057
|
+
`${chalk12.hex(colors.blue)(label1.padEnd(maxLabelWidth))} ${chalk12.hex(colors.blue)("https://lingo.dev/go/gh")}`
|
|
6058
|
+
);
|
|
6059
|
+
console.log(
|
|
6060
|
+
`${chalk12.hex(colors.blue)(label2.padEnd(maxLabelWidth + 1))} ${chalk12.hex(colors.blue)("https://lingo.dev/go/docs")}`
|
|
6061
|
+
);
|
|
6062
|
+
console.log(
|
|
6063
|
+
`${chalk12.hex(colors.blue)(label3.padEnd(maxLabelWidth + 1))} ${chalk12.hex(colors.blue)("hi@lingo.dev")}`
|
|
6064
|
+
);
|
|
6065
|
+
}
|
|
6066
|
+
async function pauseIfDebug(debug) {
|
|
6067
|
+
if (debug) {
|
|
6068
|
+
await waitForUserPrompt("Press Enter to continue...");
|
|
5821
6069
|
}
|
|
5822
|
-
}
|
|
6070
|
+
}
|
|
6071
|
+
async function waitForUserPrompt(message) {
|
|
6072
|
+
const rl = readline2.createInterface({
|
|
6073
|
+
input: process.stdin,
|
|
6074
|
+
output: process.stdout
|
|
6075
|
+
});
|
|
6076
|
+
return new Promise((resolve) => {
|
|
6077
|
+
rl.question(chalk12.dim(`[${message}]
|
|
6078
|
+
`), () => {
|
|
6079
|
+
rl.close();
|
|
6080
|
+
resolve();
|
|
6081
|
+
});
|
|
6082
|
+
});
|
|
6083
|
+
}
|
|
6084
|
+
async function renderSummary(ctx) {
|
|
6085
|
+
console.log(chalk12.hex(colors.green)("[Done]"));
|
|
6086
|
+
const skippedTasksCount = Array.from(ctx.results.values()).filter(
|
|
6087
|
+
(r) => r.status === "skipped"
|
|
6088
|
+
).length;
|
|
6089
|
+
console.log(`\u2022 ${chalk12.hex(colors.yellow)(skippedTasksCount)} from cache`);
|
|
6090
|
+
const succeededTasksCount = Array.from(ctx.results.values()).filter(
|
|
6091
|
+
(r) => r.status === "success"
|
|
6092
|
+
).length;
|
|
6093
|
+
console.log(`\u2022 ${chalk12.hex(colors.yellow)(succeededTasksCount)} processed`);
|
|
6094
|
+
const failedTasksCount = Array.from(ctx.results.values()).filter(
|
|
6095
|
+
(r) => r.status === "error"
|
|
6096
|
+
).length;
|
|
6097
|
+
console.log(`\u2022 ${chalk12.hex(colors.yellow)(failedTasksCount)} failed`);
|
|
6098
|
+
}
|
|
5823
6099
|
|
|
5824
|
-
// src/cli/cmd/
|
|
5825
|
-
import
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
if (!
|
|
5831
|
-
|
|
6100
|
+
// src/cli/cmd/run/index.ts
|
|
6101
|
+
import chalk13 from "chalk";
|
|
6102
|
+
var run_default = new Command14().command("run").description("Run Lingo.dev localization engine").helpOption("-h, --help", "Show help").option(
|
|
6103
|
+
"--source-locale <source-locale>",
|
|
6104
|
+
"Locale to use as source locale. Defaults to i18n.json locale.source",
|
|
6105
|
+
(val, prev) => {
|
|
6106
|
+
if (!val) return prev;
|
|
6107
|
+
if (!process.argv.includes("--target-locale")) {
|
|
6108
|
+
console.error(
|
|
6109
|
+
`
|
|
6110
|
+
\u274C ${chalk13.red("Error")}: --source-locale must be used together with --target-locale
|
|
6111
|
+
`
|
|
6112
|
+
);
|
|
6113
|
+
process.exit(1);
|
|
5832
6114
|
}
|
|
5833
|
-
return
|
|
5834
|
-
}
|
|
5835
|
-
async branchExists({ branch }) {
|
|
5836
|
-
return await this.octokit.rest.repos.getBranch({
|
|
5837
|
-
branch,
|
|
5838
|
-
owner: this.platformConfig.repositoryOwner,
|
|
5839
|
-
repo: this.platformConfig.repositoryName
|
|
5840
|
-
}).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
|
|
6115
|
+
return val;
|
|
5841
6116
|
}
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
await this.octokit.rest.pulls.update({
|
|
5853
|
-
pull_number: pullRequestNumber,
|
|
5854
|
-
owner: this.platformConfig.repositoryOwner,
|
|
5855
|
-
repo: this.platformConfig.repositoryName,
|
|
5856
|
-
state: "closed"
|
|
5857
|
-
});
|
|
5858
|
-
}
|
|
5859
|
-
async createPullRequest({
|
|
5860
|
-
head,
|
|
5861
|
-
title,
|
|
5862
|
-
body
|
|
5863
|
-
}) {
|
|
5864
|
-
return await this.octokit.rest.pulls.create({
|
|
5865
|
-
head,
|
|
5866
|
-
title,
|
|
5867
|
-
body,
|
|
5868
|
-
owner: this.platformConfig.repositoryOwner,
|
|
5869
|
-
repo: this.platformConfig.repositoryName,
|
|
5870
|
-
base: this.platformConfig.baseBranchName
|
|
5871
|
-
}).then(({ data }) => data.number);
|
|
5872
|
-
}
|
|
5873
|
-
async commentOnPullRequest({
|
|
5874
|
-
pullRequestNumber,
|
|
5875
|
-
body
|
|
5876
|
-
}) {
|
|
5877
|
-
await this.octokit.rest.issues.createComment({
|
|
5878
|
-
issue_number: pullRequestNumber,
|
|
5879
|
-
body,
|
|
5880
|
-
owner: this.platformConfig.repositoryOwner,
|
|
5881
|
-
repo: this.platformConfig.repositoryName
|
|
5882
|
-
});
|
|
5883
|
-
}
|
|
5884
|
-
async gitConfig() {
|
|
5885
|
-
const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;
|
|
5886
|
-
const { processOwnCommits } = this.config;
|
|
5887
|
-
if (ghToken && processOwnCommits) {
|
|
5888
|
-
console.log(
|
|
5889
|
-
"Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again."
|
|
6117
|
+
).option(
|
|
6118
|
+
"--target-locale <target-locale>",
|
|
6119
|
+
"Locale to use as target locale. Defaults to i18n.json locale.targets",
|
|
6120
|
+
(val, prev) => {
|
|
6121
|
+
if (!val) return prev;
|
|
6122
|
+
if (!process.argv.includes("--source-locale")) {
|
|
6123
|
+
console.error(
|
|
6124
|
+
`
|
|
6125
|
+
\u274C ${chalk13.red("Error")}: --target-locale must be used together with --source-locale
|
|
6126
|
+
`
|
|
5890
6127
|
);
|
|
5891
|
-
|
|
5892
|
-
super.gitConfig(ghToken, url);
|
|
6128
|
+
process.exit(1);
|
|
5893
6129
|
}
|
|
6130
|
+
return prev ? [...prev, val] : [val];
|
|
5894
6131
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
6132
|
+
).option(
|
|
6133
|
+
"--bucket <bucket>",
|
|
6134
|
+
"Bucket to process",
|
|
6135
|
+
(val, prev) => prev ? [...prev, val] : [val]
|
|
6136
|
+
).option(
|
|
6137
|
+
"--file <file>",
|
|
6138
|
+
"File to process. Process only files that include this string in their path. Useful if you have a lot of files and want to focus on a specific one. Specify more files separated by commas or spaces.",
|
|
6139
|
+
(val, prev) => prev ? [...prev, val] : [val]
|
|
6140
|
+
).option(
|
|
6141
|
+
"--key <key>",
|
|
6142
|
+
"Key to process. Process only a specific translation key, useful for updating a single entry",
|
|
6143
|
+
(val, prev) => prev ? [...prev, val] : [val]
|
|
6144
|
+
).option(
|
|
6145
|
+
"--force",
|
|
6146
|
+
"Ignore lockfile and process all keys, useful for full re-translation"
|
|
6147
|
+
).option(
|
|
6148
|
+
"--api-key <api-key>",
|
|
6149
|
+
"Explicitly set the API key to use, override the default API key from settings"
|
|
6150
|
+
).option(
|
|
6151
|
+
"--debug",
|
|
6152
|
+
"Pause execution at start for debugging purposes, waits for user confirmation before proceeding"
|
|
6153
|
+
).option(
|
|
6154
|
+
"--concurrency <concurrency>",
|
|
6155
|
+
"Number of concurrent tasks to run",
|
|
6156
|
+
(val) => parseInt(val)
|
|
6157
|
+
).action(async (args) => {
|
|
6158
|
+
try {
|
|
6159
|
+
const ctx = {
|
|
6160
|
+
flags: flagsSchema2.parse(args),
|
|
6161
|
+
config: null,
|
|
6162
|
+
results: /* @__PURE__ */ new Map(),
|
|
6163
|
+
tasks: [],
|
|
6164
|
+
localizer: null
|
|
5909
6165
|
};
|
|
6166
|
+
await pauseIfDebug(ctx.flags.debug);
|
|
6167
|
+
await renderClear();
|
|
6168
|
+
await renderSpacer();
|
|
6169
|
+
await renderBanner();
|
|
6170
|
+
await renderHero();
|
|
6171
|
+
await renderSpacer();
|
|
6172
|
+
await setup(ctx);
|
|
6173
|
+
await renderSpacer();
|
|
6174
|
+
await plan(ctx);
|
|
6175
|
+
await renderSpacer();
|
|
6176
|
+
await execute(ctx);
|
|
6177
|
+
await renderSpacer();
|
|
6178
|
+
await renderSummary(ctx);
|
|
6179
|
+
await renderSpacer();
|
|
6180
|
+
} catch (error) {
|
|
6181
|
+
process.exit(1);
|
|
5910
6182
|
}
|
|
5911
|
-
|
|
5912
|
-
const { repositoryOwner, repositoryName } = this.platformConfig;
|
|
5913
|
-
return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;
|
|
5914
|
-
}
|
|
5915
|
-
};
|
|
6183
|
+
});
|
|
5916
6184
|
|
|
5917
|
-
// src/cli/cmd/ci/
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
super();
|
|
5925
|
-
process.chdir(this.platformConfig.projectDir);
|
|
6185
|
+
// src/cli/cmd/ci/flows/in-branch.ts
|
|
6186
|
+
var InBranchFlow = class extends IntegrationFlow {
|
|
6187
|
+
async preRun() {
|
|
6188
|
+
this.ora.start("Configuring git");
|
|
6189
|
+
const canContinue = this.configureGit();
|
|
6190
|
+
this.ora.succeed("Git configured");
|
|
6191
|
+
return canContinue;
|
|
5926
6192
|
}
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
6193
|
+
async run(options) {
|
|
6194
|
+
this.ora.start("Running Lingo.dev");
|
|
6195
|
+
await this.runLingoDotDev(options.parallel);
|
|
6196
|
+
this.ora.succeed("Done running Lingo.dev");
|
|
6197
|
+
execSync(`rm -f i18n.cache`, { stdio: "inherit" });
|
|
6198
|
+
this.ora.start("Checking for changes");
|
|
6199
|
+
const hasChanges = this.checkCommitableChanges();
|
|
6200
|
+
this.ora.succeed(hasChanges ? "Changes detected" : "No changes detected");
|
|
6201
|
+
if (hasChanges) {
|
|
6202
|
+
this.ora.start("Committing changes");
|
|
6203
|
+
execSync(`git add .`, { stdio: "inherit" });
|
|
6204
|
+
execSync(`git status --porcelain`, { stdio: "inherit" });
|
|
6205
|
+
execSync(
|
|
6206
|
+
`git commit -m ${escapeShellArg(this.platformKit.config.commitMessage)} --no-verify`,
|
|
6207
|
+
{
|
|
6208
|
+
stdio: "inherit"
|
|
6209
|
+
}
|
|
6210
|
+
);
|
|
6211
|
+
this.ora.succeed("Changes committed");
|
|
6212
|
+
this.ora.start("Pushing changes to remote");
|
|
6213
|
+
const currentBranch = this.i18nBranchName ?? this.platformKit.platformConfig.baseBranchName;
|
|
6214
|
+
execSync(
|
|
6215
|
+
`git push origin ${currentBranch} ${options.force ? "--force" : ""}`,
|
|
6216
|
+
{
|
|
6217
|
+
stdio: "inherit"
|
|
6218
|
+
}
|
|
6219
|
+
);
|
|
6220
|
+
this.ora.succeed("Changes pushed to remote");
|
|
5932
6221
|
}
|
|
5933
|
-
return
|
|
6222
|
+
return hasChanges;
|
|
5934
6223
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: Z10.string().optional(),
|
|
5940
|
-
CI_PROJECT_NAMESPACE: Z10.string(),
|
|
5941
|
-
CI_PROJECT_NAME: Z10.string(),
|
|
5942
|
-
CI_PROJECT_ID: Z10.string(),
|
|
5943
|
-
CI_PROJECT_DIR: Z10.string(),
|
|
5944
|
-
CI_REPOSITORY_URL: Z10.string()
|
|
5945
|
-
}).parse(process.env);
|
|
5946
|
-
const config = {
|
|
5947
|
-
glToken: env.GL_TOKEN,
|
|
5948
|
-
baseBranchName: env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ?? env.CI_COMMIT_BRANCH,
|
|
5949
|
-
repositoryOwner: env.CI_PROJECT_NAMESPACE,
|
|
5950
|
-
repositoryName: env.CI_PROJECT_NAME,
|
|
5951
|
-
gitlabProjectId: env.CI_PROJECT_ID,
|
|
5952
|
-
projectDir: env.CI_PROJECT_DIR,
|
|
5953
|
-
reporitoryUrl: env.CI_REPOSITORY_URL
|
|
5954
|
-
};
|
|
5955
|
-
return config;
|
|
6224
|
+
checkCommitableChanges() {
|
|
6225
|
+
return execSync('git status --porcelain || echo "has_changes"', {
|
|
6226
|
+
encoding: "utf8"
|
|
6227
|
+
}).length > 0;
|
|
5956
6228
|
}
|
|
5957
|
-
async
|
|
6229
|
+
async runLingoDotDev(isParallel) {
|
|
5958
6230
|
try {
|
|
5959
|
-
|
|
5960
|
-
this.
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
6231
|
+
if (!isParallel) {
|
|
6232
|
+
await i18n_default.exitOverride().parseAsync(["--api-key", this.platformKit.config.replexicaApiKey], {
|
|
6233
|
+
from: "user"
|
|
6234
|
+
});
|
|
6235
|
+
} else {
|
|
6236
|
+
await run_default.exitOverride().parseAsync(["--api-key", this.platformKit.config.replexicaApiKey], {
|
|
6237
|
+
from: "user"
|
|
6238
|
+
});
|
|
6239
|
+
}
|
|
6240
|
+
} catch (err) {
|
|
6241
|
+
if (err.code === "commander.helpDisplayed") return;
|
|
6242
|
+
throw err;
|
|
5966
6243
|
}
|
|
5967
6244
|
}
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
});
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
6245
|
+
configureGit() {
|
|
6246
|
+
const { processOwnCommits } = this.platformKit.config;
|
|
6247
|
+
const { baseBranchName } = this.platformKit.platformConfig;
|
|
6248
|
+
this.ora.info(`Current working directory:`);
|
|
6249
|
+
execSync(`pwd`, { stdio: "inherit" });
|
|
6250
|
+
execSync(`ls -la`, { stdio: "inherit" });
|
|
6251
|
+
execSync(`git config --global safe.directory ${process.cwd()}`);
|
|
6252
|
+
execSync(`git config user.name "${gitConfig.userName}"`);
|
|
6253
|
+
execSync(`git config user.email "${gitConfig.userEmail}"`);
|
|
6254
|
+
this.platformKit?.gitConfig();
|
|
6255
|
+
execSync(`git fetch origin ${baseBranchName}`, { stdio: "inherit" });
|
|
6256
|
+
execSync(`git checkout ${baseBranchName} --`, { stdio: "inherit" });
|
|
6257
|
+
if (!processOwnCommits) {
|
|
6258
|
+
const currentAuthor = `${gitConfig.userName} <${gitConfig.userEmail}>`;
|
|
6259
|
+
const authorOfLastCommit = execSync(
|
|
6260
|
+
`git log -1 --pretty=format:'%an <%ae>'`
|
|
6261
|
+
).toString();
|
|
6262
|
+
if (authorOfLastCommit === currentAuthor) {
|
|
6263
|
+
this.ora.warn(
|
|
6264
|
+
`The last commit was already made by ${currentAuthor}, so this run will be skipped, as running again would have no effect. See docs: https://docs.lingo.dev/ci-action/overview`
|
|
6265
|
+
);
|
|
6266
|
+
return false;
|
|
5986
6267
|
}
|
|
6268
|
+
}
|
|
6269
|
+
const workingDir = path15.resolve(
|
|
6270
|
+
process.cwd(),
|
|
6271
|
+
this.platformKit.config.workingDir
|
|
5987
6272
|
);
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
this.platformConfig.gitlabProjectId,
|
|
5996
|
-
head,
|
|
5997
|
-
this.platformConfig.baseBranchName,
|
|
5998
|
-
title,
|
|
5999
|
-
{
|
|
6000
|
-
description: body
|
|
6001
|
-
}
|
|
6002
|
-
);
|
|
6003
|
-
return mr.iid;
|
|
6004
|
-
}
|
|
6005
|
-
async commentOnPullRequest({
|
|
6006
|
-
pullRequestNumber,
|
|
6007
|
-
body
|
|
6008
|
-
}) {
|
|
6009
|
-
await this.gitlab.MergeRequestNotes.create(
|
|
6010
|
-
this.platformConfig.gitlabProjectId,
|
|
6011
|
-
pullRequestNumber,
|
|
6012
|
-
body
|
|
6013
|
-
);
|
|
6014
|
-
}
|
|
6015
|
-
gitConfig() {
|
|
6016
|
-
const glToken = this.platformConfig.glToken;
|
|
6017
|
-
const url = `https://oauth2:${glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;
|
|
6018
|
-
super.gitConfig(glToken, url);
|
|
6019
|
-
}
|
|
6020
|
-
buildPullRequestUrl(pullRequestNumber) {
|
|
6021
|
-
return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;
|
|
6273
|
+
if (workingDir !== process.cwd()) {
|
|
6274
|
+
this.ora.info(
|
|
6275
|
+
`Changing to working directory: ${this.platformKit.config.workingDir}`
|
|
6276
|
+
);
|
|
6277
|
+
process.chdir(workingDir);
|
|
6278
|
+
}
|
|
6279
|
+
return true;
|
|
6022
6280
|
}
|
|
6023
6281
|
};
|
|
6024
6282
|
|
|
6025
|
-
// src/cli/cmd/ci/
|
|
6026
|
-
var
|
|
6027
|
-
|
|
6028
|
-
|
|
6283
|
+
// src/cli/cmd/ci/flows/pull-request.ts
|
|
6284
|
+
var PullRequestFlow = class extends InBranchFlow {
|
|
6285
|
+
async preRun() {
|
|
6286
|
+
const canContinue = await super.preRun?.();
|
|
6287
|
+
if (!canContinue) {
|
|
6288
|
+
return false;
|
|
6289
|
+
}
|
|
6290
|
+
this.ora.start("Calculating automated branch name");
|
|
6291
|
+
this.i18nBranchName = this.calculatePrBranchName();
|
|
6292
|
+
this.ora.succeed(
|
|
6293
|
+
`Automated branch name calculated: ${this.i18nBranchName}`
|
|
6294
|
+
);
|
|
6295
|
+
this.ora.start("Checking if branch exists");
|
|
6296
|
+
const branchExists = await this.checkBranchExistance(this.i18nBranchName);
|
|
6297
|
+
this.ora.succeed(branchExists ? "Branch exists" : "Branch does not exist");
|
|
6298
|
+
if (branchExists) {
|
|
6299
|
+
this.ora.start(`Checking out branch ${this.i18nBranchName}`);
|
|
6300
|
+
this.checkoutI18nBranch(this.i18nBranchName);
|
|
6301
|
+
this.ora.succeed(`Checked out branch ${this.i18nBranchName}`);
|
|
6302
|
+
this.ora.start(
|
|
6303
|
+
`Syncing with ${this.platformKit.platformConfig.baseBranchName}`
|
|
6304
|
+
);
|
|
6305
|
+
this.syncI18nBranch();
|
|
6306
|
+
this.ora.succeed(`Checked out and synced branch ${this.i18nBranchName}`);
|
|
6307
|
+
} else {
|
|
6308
|
+
this.ora.start(`Creating branch ${this.i18nBranchName}`);
|
|
6309
|
+
this.createI18nBranch(this.i18nBranchName);
|
|
6310
|
+
this.ora.succeed(`Created branch ${this.i18nBranchName}`);
|
|
6311
|
+
}
|
|
6312
|
+
return true;
|
|
6029
6313
|
}
|
|
6030
|
-
|
|
6031
|
-
return
|
|
6314
|
+
async run(options) {
|
|
6315
|
+
return super.run({
|
|
6316
|
+
force: true,
|
|
6317
|
+
...options
|
|
6318
|
+
});
|
|
6032
6319
|
}
|
|
6033
|
-
|
|
6034
|
-
|
|
6320
|
+
async postRun() {
|
|
6321
|
+
if (!this.i18nBranchName) {
|
|
6322
|
+
throw new Error(
|
|
6323
|
+
"i18nBranchName is not set. Did you forget to call preRun?"
|
|
6324
|
+
);
|
|
6325
|
+
}
|
|
6326
|
+
this.ora.start("Checking if PR already exists");
|
|
6327
|
+
const pullRequestNumber = await this.ensureFreshPr(this.i18nBranchName);
|
|
6328
|
+
this.ora.succeed(
|
|
6329
|
+
`Pull request ready: ${this.platformKit.buildPullRequestUrl(pullRequestNumber)}`
|
|
6330
|
+
);
|
|
6035
6331
|
}
|
|
6036
|
-
|
|
6037
|
-
}
|
|
6038
|
-
|
|
6039
|
-
// src/cli/cmd/ci/index.ts
|
|
6040
|
-
var ci_default = new Command14().command("ci").description("Run Lingo.dev CI/CD action").helpOption("-h, --help", "Show help").option("--api-key <key>", "API key").option("--pull-request [boolean]", "Create a pull request with the changes").option("--commit-message <message>", "Commit message").option("--pull-request-title <title>", "Pull request title").option("--working-directory <dir>", "Working directory").option(
|
|
6041
|
-
"--process-own-commits [boolean]",
|
|
6042
|
-
"Process commits made by this action"
|
|
6043
|
-
).action(async (options) => {
|
|
6044
|
-
const settings = getSettings(options.apiKey);
|
|
6045
|
-
if (!settings.auth.apiKey) {
|
|
6046
|
-
console.error("No API key provided");
|
|
6047
|
-
return;
|
|
6332
|
+
calculatePrBranchName() {
|
|
6333
|
+
return `lingo.dev/${this.platformKit.platformConfig.baseBranchName}`;
|
|
6048
6334
|
}
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
const auth = await authenticator.whoami();
|
|
6054
|
-
if (!auth) {
|
|
6055
|
-
console.error("Not authenticated");
|
|
6056
|
-
return;
|
|
6335
|
+
async checkBranchExistance(prBranchName) {
|
|
6336
|
+
return this.platformKit.branchExists({
|
|
6337
|
+
branch: prBranchName
|
|
6338
|
+
});
|
|
6057
6339
|
}
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6340
|
+
async ensureFreshPr(i18nBranchName) {
|
|
6341
|
+
this.ora.start(
|
|
6342
|
+
`Checking for existing PR with head ${i18nBranchName} and base ${this.platformKit.platformConfig.baseBranchName}`
|
|
6343
|
+
);
|
|
6344
|
+
let prNumber = await this.platformKit.getOpenPullRequestNumber({
|
|
6345
|
+
branch: i18nBranchName
|
|
6346
|
+
});
|
|
6347
|
+
if (prNumber) {
|
|
6348
|
+
this.ora.succeed(`Existing PR found: #${prNumber}`);
|
|
6349
|
+
} else {
|
|
6350
|
+
this.ora.start(`Creating new PR`);
|
|
6351
|
+
prNumber = await this.platformKit.createPullRequest({
|
|
6352
|
+
head: i18nBranchName,
|
|
6353
|
+
title: this.platformKit.config.pullRequestTitle,
|
|
6354
|
+
body: this.getPrBodyContent()
|
|
6355
|
+
});
|
|
6356
|
+
this.ora.succeed(`Created new PR: #${prNumber}`);
|
|
6072
6357
|
}
|
|
6073
|
-
|
|
6074
|
-
process.env = { ...process.env, ...env };
|
|
6075
|
-
const ora = createOra();
|
|
6076
|
-
const platformKit = getPlatformKit();
|
|
6077
|
-
const { isPullRequestMode } = platformKit.config;
|
|
6078
|
-
ora.info(`Pull request mode: ${isPullRequestMode ? "on" : "off"}`);
|
|
6079
|
-
const flow = isPullRequestMode ? new PullRequestFlow(ora, platformKit) : new InBranchFlow(ora, platformKit);
|
|
6080
|
-
const canRun = await flow.preRun?.();
|
|
6081
|
-
if (canRun === false) {
|
|
6082
|
-
return;
|
|
6358
|
+
return prNumber;
|
|
6083
6359
|
}
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6360
|
+
checkoutI18nBranch(i18nBranchName) {
|
|
6361
|
+
execSync2(`git fetch origin ${i18nBranchName}`, { stdio: "inherit" });
|
|
6362
|
+
execSync2(`git checkout -b ${i18nBranchName}`, {
|
|
6363
|
+
stdio: "inherit"
|
|
6364
|
+
});
|
|
6087
6365
|
}
|
|
6088
|
-
|
|
6089
|
-
});
|
|
6090
|
-
|
|
6091
|
-
// src/cli/cmd/status.ts
|
|
6092
|
-
import { bucketTypeSchema as bucketTypeSchema3, localeCodeSchema as localeCodeSchema2, resolveOverriddenLocale as resolveOverriddenLocale6 } from "@lingo.dev/_spec";
|
|
6093
|
-
import { Command as Command15 } from "interactive-commander";
|
|
6094
|
-
import Z11 from "zod";
|
|
6095
|
-
import Ora8 from "ora";
|
|
6096
|
-
import chalk6 from "chalk";
|
|
6097
|
-
import Table from "cli-table3";
|
|
6098
|
-
var status_default = new Command15().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
|
|
6099
|
-
"--file [files...]",
|
|
6100
|
-
"File to process. Process only a specific path, may contain asterisk * to match multiple files."
|
|
6101
|
-
).option("--force", "Ignore lockfile and process all keys, useful for estimating full re-translation").option("--verbose", "Show detailed output including key-level word counts").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").action(async function(options) {
|
|
6102
|
-
const ora = Ora8();
|
|
6103
|
-
const flags = parseFlags2(options);
|
|
6104
|
-
let authId = null;
|
|
6105
|
-
try {
|
|
6106
|
-
ora.start("Loading configuration...");
|
|
6107
|
-
const i18nConfig = getConfig();
|
|
6108
|
-
const settings = getSettings(flags.apiKey);
|
|
6109
|
-
ora.succeed("Configuration loaded");
|
|
6366
|
+
createI18nBranch(i18nBranchName) {
|
|
6110
6367
|
try {
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6368
|
+
execSync2(
|
|
6369
|
+
`git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
|
|
6370
|
+
{ stdio: "inherit" }
|
|
6371
|
+
);
|
|
6372
|
+
execSync2(
|
|
6373
|
+
`git checkout -b ${i18nBranchName} origin/${this.platformKit.platformConfig.baseBranchName}`,
|
|
6374
|
+
{
|
|
6375
|
+
stdio: "inherit"
|
|
6376
|
+
}
|
|
6377
|
+
);
|
|
6121
6378
|
} catch (error) {
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
let buckets = getBuckets(i18nConfig);
|
|
6132
|
-
if (flags.bucket?.length) {
|
|
6133
|
-
buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
|
|
6134
|
-
}
|
|
6135
|
-
ora.succeed("Buckets retrieved");
|
|
6136
|
-
if (flags.file?.length) {
|
|
6137
|
-
buckets = buckets.map((bucket) => {
|
|
6138
|
-
const paths = bucket.paths.filter((path16) => flags.file.find((file) => path16.pathPattern?.match(file)));
|
|
6139
|
-
return { ...bucket, paths };
|
|
6140
|
-
}).filter((bucket) => bucket.paths.length > 0);
|
|
6141
|
-
if (buckets.length === 0) {
|
|
6142
|
-
ora.fail("No buckets found. All buckets were filtered out by --file option.");
|
|
6143
|
-
process.exit(1);
|
|
6144
|
-
} else {
|
|
6145
|
-
ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
|
|
6146
|
-
buckets.map((bucket) => {
|
|
6147
|
-
ora.info(` ${bucket.type}:`);
|
|
6148
|
-
bucket.paths.forEach((path16) => {
|
|
6149
|
-
ora.info(` - ${path16.pathPattern}`);
|
|
6150
|
-
});
|
|
6151
|
-
});
|
|
6152
|
-
}
|
|
6379
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
6380
|
+
this.ora.fail(`Failed to create branch: ${errorMessage}`);
|
|
6381
|
+
this.ora.info(`
|
|
6382
|
+
Troubleshooting tips:
|
|
6383
|
+
1. Make sure you have permission to create branches
|
|
6384
|
+
2. Check if the branch already exists locally (try 'git branch -a')
|
|
6385
|
+
3. Verify connectivity to remote repository
|
|
6386
|
+
`);
|
|
6387
|
+
throw new Error(`Branch creation failed: ${errorMessage}`);
|
|
6153
6388
|
}
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
const totalWordCount = /* @__PURE__ */ new Map();
|
|
6159
|
-
const languageStats = {};
|
|
6160
|
-
for (const locale of targetLocales) {
|
|
6161
|
-
languageStats[locale] = {
|
|
6162
|
-
complete: 0,
|
|
6163
|
-
missing: 0,
|
|
6164
|
-
updated: 0,
|
|
6165
|
-
words: 0
|
|
6166
|
-
};
|
|
6167
|
-
totalWordCount.set(locale, 0);
|
|
6389
|
+
}
|
|
6390
|
+
syncI18nBranch() {
|
|
6391
|
+
if (!this.i18nBranchName) {
|
|
6392
|
+
throw new Error("i18nBranchName is not set");
|
|
6168
6393
|
}
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
try {
|
|
6172
|
-
console.log();
|
|
6173
|
-
ora.info(`Analyzing bucket: ${bucket.type}`);
|
|
6174
|
-
for (const bucketPath of bucket.paths) {
|
|
6175
|
-
const bucketOra = Ora8({ indent: 2 }).info(`Analyzing path: ${bucketPath.pathPattern}`);
|
|
6176
|
-
const sourceLocale = resolveOverriddenLocale6(i18nConfig.locale.source, bucketPath.delimiter);
|
|
6177
|
-
const bucketLoader = createBucketLoader(
|
|
6178
|
-
bucket.type,
|
|
6179
|
-
bucketPath.pathPattern,
|
|
6180
|
-
{
|
|
6181
|
-
isCacheRestore: false,
|
|
6182
|
-
defaultLocale: sourceLocale,
|
|
6183
|
-
injectLocale: bucket.injectLocale
|
|
6184
|
-
},
|
|
6185
|
-
bucket.lockedKeys
|
|
6186
|
-
);
|
|
6187
|
-
bucketLoader.setDefaultLocale(sourceLocale);
|
|
6188
|
-
await bucketLoader.init();
|
|
6189
|
-
const filePath = bucketPath.pathPattern;
|
|
6190
|
-
if (!fileStats[filePath]) {
|
|
6191
|
-
fileStats[filePath] = {
|
|
6192
|
-
path: filePath,
|
|
6193
|
-
sourceKeys: 0,
|
|
6194
|
-
wordCount: 0,
|
|
6195
|
-
languageStats: {}
|
|
6196
|
-
};
|
|
6197
|
-
for (const locale of targetLocales) {
|
|
6198
|
-
fileStats[filePath].languageStats[locale] = {
|
|
6199
|
-
complete: 0,
|
|
6200
|
-
missing: 0,
|
|
6201
|
-
updated: 0,
|
|
6202
|
-
words: 0
|
|
6203
|
-
};
|
|
6204
|
-
}
|
|
6205
|
-
}
|
|
6206
|
-
const sourceData = await bucketLoader.pull(sourceLocale);
|
|
6207
|
-
const sourceKeys = Object.keys(sourceData);
|
|
6208
|
-
fileStats[filePath].sourceKeys = sourceKeys.length;
|
|
6209
|
-
totalSourceKeyCount += sourceKeys.length;
|
|
6210
|
-
let sourceWordCount = 0;
|
|
6211
|
-
for (const key of sourceKeys) {
|
|
6212
|
-
const value = sourceData[key];
|
|
6213
|
-
if (typeof value === "string") {
|
|
6214
|
-
const words = value.trim().split(/\s+/).length;
|
|
6215
|
-
sourceWordCount += words;
|
|
6216
|
-
}
|
|
6217
|
-
}
|
|
6218
|
-
fileStats[filePath].wordCount = sourceWordCount;
|
|
6219
|
-
for (const _targetLocale of targetLocales) {
|
|
6220
|
-
const targetLocale = resolveOverriddenLocale6(_targetLocale, bucketPath.delimiter);
|
|
6221
|
-
bucketOra.start(`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`);
|
|
6222
|
-
let targetData = {};
|
|
6223
|
-
let fileExists = true;
|
|
6224
|
-
try {
|
|
6225
|
-
targetData = await bucketLoader.pull(targetLocale);
|
|
6226
|
-
} catch (error) {
|
|
6227
|
-
fileExists = false;
|
|
6228
|
-
bucketOra.info(
|
|
6229
|
-
`[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`
|
|
6230
|
-
);
|
|
6231
|
-
}
|
|
6232
|
-
if (!fileExists) {
|
|
6233
|
-
fileStats[filePath].languageStats[targetLocale].missing = sourceKeys.length;
|
|
6234
|
-
fileStats[filePath].languageStats[targetLocale].words = sourceWordCount;
|
|
6235
|
-
languageStats[targetLocale].missing += sourceKeys.length;
|
|
6236
|
-
languageStats[targetLocale].words += sourceWordCount;
|
|
6237
|
-
totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + sourceWordCount);
|
|
6238
|
-
bucketOra.succeed(
|
|
6239
|
-
`[${sourceLocale} -> ${targetLocale}] ${chalk6.red(`0% complete`)} (0/${sourceKeys.length} keys) - file not found`
|
|
6240
|
-
);
|
|
6241
|
-
continue;
|
|
6242
|
-
}
|
|
6243
|
-
const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
|
|
6244
|
-
const checksums = await deltaProcessor.loadChecksums();
|
|
6245
|
-
const delta = await deltaProcessor.calculateDelta({
|
|
6246
|
-
sourceData,
|
|
6247
|
-
targetData,
|
|
6248
|
-
checksums
|
|
6249
|
-
});
|
|
6250
|
-
const missingKeys = delta.added;
|
|
6251
|
-
const updatedKeys = delta.updated;
|
|
6252
|
-
const completeKeys = sourceKeys.filter((key) => !missingKeys.includes(key) && !updatedKeys.includes(key));
|
|
6253
|
-
let wordsToTranslate = 0;
|
|
6254
|
-
const keysToProcess = flags.force ? sourceKeys : [...missingKeys, ...updatedKeys];
|
|
6255
|
-
for (const key of keysToProcess) {
|
|
6256
|
-
const value = sourceData[String(key)];
|
|
6257
|
-
if (typeof value === "string") {
|
|
6258
|
-
const words = value.trim().split(/\s+/).length;
|
|
6259
|
-
wordsToTranslate += words;
|
|
6260
|
-
}
|
|
6261
|
-
}
|
|
6262
|
-
fileStats[filePath].languageStats[targetLocale].missing = missingKeys.length;
|
|
6263
|
-
fileStats[filePath].languageStats[targetLocale].updated = updatedKeys.length;
|
|
6264
|
-
fileStats[filePath].languageStats[targetLocale].complete = completeKeys.length;
|
|
6265
|
-
fileStats[filePath].languageStats[targetLocale].words = wordsToTranslate;
|
|
6266
|
-
languageStats[targetLocale].missing += missingKeys.length;
|
|
6267
|
-
languageStats[targetLocale].updated += updatedKeys.length;
|
|
6268
|
-
languageStats[targetLocale].complete += completeKeys.length;
|
|
6269
|
-
languageStats[targetLocale].words += wordsToTranslate;
|
|
6270
|
-
totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + wordsToTranslate);
|
|
6271
|
-
const totalKeysInFile = sourceKeys.length;
|
|
6272
|
-
const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
|
|
6273
|
-
if (missingKeys.length === 0 && updatedKeys.length === 0) {
|
|
6274
|
-
bucketOra.succeed(
|
|
6275
|
-
`[${sourceLocale} -> ${targetLocale}] ${chalk6.green(`100% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`
|
|
6276
|
-
);
|
|
6277
|
-
} else {
|
|
6278
|
-
const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? chalk6.yellow(`${completionPercent}% complete`) : chalk6.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
|
|
6279
|
-
bucketOra.succeed(message);
|
|
6280
|
-
if (flags.verbose) {
|
|
6281
|
-
if (missingKeys.length > 0) {
|
|
6282
|
-
console.log(` ${chalk6.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`);
|
|
6283
|
-
console.log(
|
|
6284
|
-
` ${chalk6.dim(`Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`)}`
|
|
6285
|
-
);
|
|
6286
|
-
}
|
|
6287
|
-
if (updatedKeys.length > 0) {
|
|
6288
|
-
console.log(` ${chalk6.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`);
|
|
6289
|
-
}
|
|
6290
|
-
}
|
|
6291
|
-
}
|
|
6292
|
-
}
|
|
6293
|
-
}
|
|
6294
|
-
} catch (error) {
|
|
6295
|
-
ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
|
|
6296
|
-
}
|
|
6297
|
-
}
|
|
6298
|
-
const totalKeysNeedingTranslation = Object.values(languageStats).reduce((sum, stats) => {
|
|
6299
|
-
return sum + stats.missing + stats.updated;
|
|
6300
|
-
}, 0);
|
|
6301
|
-
const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
|
|
6302
|
-
console.log();
|
|
6303
|
-
ora.succeed(chalk6.green(`Localization status completed.`));
|
|
6304
|
-
console.log(chalk6.bold.cyan(`
|
|
6305
|
-
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
|
|
6306
|
-
console.log(chalk6.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
|
|
6307
|
-
console.log(chalk6.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
|
|
6308
|
-
console.log(chalk6.bold(`
|
|
6309
|
-
\u{1F4DD} SOURCE CONTENT:`));
|
|
6310
|
-
console.log(`\u2022 Source language: ${chalk6.green(i18nConfig.locale.source)}`);
|
|
6311
|
-
console.log(`\u2022 Source keys: ${chalk6.yellow(totalSourceKeyCount.toString())} keys across all files`);
|
|
6312
|
-
console.log(chalk6.bold(`
|
|
6313
|
-
\u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
|
|
6314
|
-
const table = new Table({
|
|
6315
|
-
head: ["Language", "Status", "Complete", "Missing", "Updated", "Total Keys", "Words to Translate"],
|
|
6316
|
-
style: {
|
|
6317
|
-
head: ["white"],
|
|
6318
|
-
// White color for headers
|
|
6319
|
-
border: []
|
|
6320
|
-
// No color for borders
|
|
6321
|
-
},
|
|
6322
|
-
colWidths: [12, 20, 18, 12, 12, 12, 15]
|
|
6323
|
-
// Explicit column widths, making Status column wider
|
|
6324
|
-
});
|
|
6325
|
-
let totalWordsToTranslate = 0;
|
|
6326
|
-
for (const locale of targetLocales) {
|
|
6327
|
-
const stats = languageStats[locale];
|
|
6328
|
-
const percentComplete = (stats.complete / totalSourceKeyCount * 100).toFixed(1);
|
|
6329
|
-
const totalNeeded = stats.missing + stats.updated;
|
|
6330
|
-
let statusText;
|
|
6331
|
-
let statusColor;
|
|
6332
|
-
if (stats.missing === totalSourceKeyCount) {
|
|
6333
|
-
statusText = "\u{1F534} Not started";
|
|
6334
|
-
statusColor = chalk6.red;
|
|
6335
|
-
} else if (stats.missing === 0 && stats.updated === 0) {
|
|
6336
|
-
statusText = "\u2705 Complete";
|
|
6337
|
-
statusColor = chalk6.green;
|
|
6338
|
-
} else if (parseFloat(percentComplete) > 80) {
|
|
6339
|
-
statusText = "\u{1F7E1} Almost done";
|
|
6340
|
-
statusColor = chalk6.yellow;
|
|
6341
|
-
} else if (parseFloat(percentComplete) > 0) {
|
|
6342
|
-
statusText = "\u{1F7E0} In progress";
|
|
6343
|
-
statusColor = chalk6.yellow;
|
|
6344
|
-
} else {
|
|
6345
|
-
statusText = "\u{1F534} Not started";
|
|
6346
|
-
statusColor = chalk6.red;
|
|
6347
|
-
}
|
|
6348
|
-
const words = totalWordCount.get(locale) || 0;
|
|
6349
|
-
totalWordsToTranslate += words;
|
|
6350
|
-
table.push([
|
|
6351
|
-
locale,
|
|
6352
|
-
statusColor(statusText),
|
|
6353
|
-
`${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
|
|
6354
|
-
stats.missing > 0 ? chalk6.red(stats.missing.toString()) : "0",
|
|
6355
|
-
stats.updated > 0 ? chalk6.yellow(stats.updated.toString()) : "0",
|
|
6356
|
-
totalNeeded > 0 ? chalk6.magenta(totalNeeded.toString()) : "0",
|
|
6357
|
-
words > 0 ? `~${words.toLocaleString()}` : "0"
|
|
6358
|
-
]);
|
|
6359
|
-
}
|
|
6360
|
-
console.log(table.toString());
|
|
6361
|
-
console.log(chalk6.bold(`
|
|
6362
|
-
\u{1F4CA} USAGE ESTIMATE:`));
|
|
6363
|
-
console.log(
|
|
6364
|
-
`\u2022 WORDS TO BE CONSUMED: ~${chalk6.yellow.bold(totalWordsToTranslate.toLocaleString())} words across all languages`
|
|
6394
|
+
this.ora.start(
|
|
6395
|
+
`Fetching latest changes from ${this.platformKit.platformConfig.baseBranchName}`
|
|
6365
6396
|
);
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
for (const locale of targetLocales) {
|
|
6370
|
-
const words = totalWordCount.get(locale) || 0;
|
|
6371
|
-
const percent = (words / totalWordsToTranslate * 100).toFixed(1);
|
|
6372
|
-
console.log(` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`);
|
|
6373
|
-
}
|
|
6374
|
-
}
|
|
6375
|
-
if (flags.confirm && Object.keys(fileStats).length > 0) {
|
|
6376
|
-
console.log(chalk6.bold(`
|
|
6377
|
-
\u{1F4D1} BREAKDOWN BY FILE:`));
|
|
6378
|
-
Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path16, stats]) => {
|
|
6379
|
-
if (stats.sourceKeys === 0) return;
|
|
6380
|
-
console.log(chalk6.bold(`
|
|
6381
|
-
\u2022 ${path16}:`));
|
|
6382
|
-
console.log(` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`);
|
|
6383
|
-
const fileTable = new Table({
|
|
6384
|
-
head: ["Language", "Status", "Details"],
|
|
6385
|
-
style: {
|
|
6386
|
-
head: ["white"],
|
|
6387
|
-
border: []
|
|
6388
|
-
},
|
|
6389
|
-
colWidths: [12, 20, 50]
|
|
6390
|
-
// Explicit column widths for file detail table
|
|
6391
|
-
});
|
|
6392
|
-
for (const locale of targetLocales) {
|
|
6393
|
-
const langStats = stats.languageStats[locale];
|
|
6394
|
-
const complete = langStats.complete;
|
|
6395
|
-
const total = stats.sourceKeys;
|
|
6396
|
-
const completion = (complete / total * 100).toFixed(1);
|
|
6397
|
-
let status = "\u2705 Complete";
|
|
6398
|
-
let statusColor = chalk6.green;
|
|
6399
|
-
if (langStats.missing === total) {
|
|
6400
|
-
status = "\u274C Not started";
|
|
6401
|
-
statusColor = chalk6.red;
|
|
6402
|
-
} else if (langStats.missing > 0 || langStats.updated > 0) {
|
|
6403
|
-
status = `\u26A0\uFE0F ${completion}% complete`;
|
|
6404
|
-
statusColor = chalk6.yellow;
|
|
6405
|
-
}
|
|
6406
|
-
let details = "";
|
|
6407
|
-
if (langStats.missing > 0 || langStats.updated > 0) {
|
|
6408
|
-
const parts = [];
|
|
6409
|
-
if (langStats.missing > 0) parts.push(`${langStats.missing} missing`);
|
|
6410
|
-
if (langStats.updated > 0) parts.push(`${langStats.updated} changed`);
|
|
6411
|
-
details = `${parts.join(", ")}, ~${langStats.words} words`;
|
|
6412
|
-
} else {
|
|
6413
|
-
details = "All keys translated";
|
|
6414
|
-
}
|
|
6415
|
-
fileTable.push([locale, statusColor(status), details]);
|
|
6416
|
-
}
|
|
6417
|
-
console.log(fileTable.toString());
|
|
6418
|
-
});
|
|
6419
|
-
}
|
|
6420
|
-
const completeLanguages = targetLocales.filter(
|
|
6421
|
-
(locale) => languageStats[locale].missing === 0 && languageStats[locale].updated === 0
|
|
6397
|
+
execSync2(
|
|
6398
|
+
`git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
|
|
6399
|
+
{ stdio: "inherit" }
|
|
6422
6400
|
);
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6401
|
+
this.ora.succeed(
|
|
6402
|
+
`Fetched latest changes from ${this.platformKit.platformConfig.baseBranchName}`
|
|
6403
|
+
);
|
|
6404
|
+
try {
|
|
6405
|
+
this.ora.start("Attempting to rebase branch");
|
|
6406
|
+
execSync2(
|
|
6407
|
+
`git rebase origin/${this.platformKit.platformConfig.baseBranchName}`,
|
|
6408
|
+
{ stdio: "inherit" }
|
|
6429
6409
|
);
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6410
|
+
this.ora.succeed("Successfully rebased branch");
|
|
6411
|
+
} catch (error) {
|
|
6412
|
+
this.ora.warn("Rebase failed, falling back to alternative sync method");
|
|
6413
|
+
this.ora.start("Aborting failed rebase");
|
|
6414
|
+
execSync2("git rebase --abort", { stdio: "inherit" });
|
|
6415
|
+
this.ora.succeed("Aborted failed rebase");
|
|
6416
|
+
this.ora.start(
|
|
6417
|
+
`Resetting to ${this.platformKit.platformConfig.baseBranchName}`
|
|
6434
6418
|
);
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6419
|
+
execSync2(
|
|
6420
|
+
`git reset --hard origin/${this.platformKit.platformConfig.baseBranchName}`,
|
|
6421
|
+
{ stdio: "inherit" }
|
|
6422
|
+
);
|
|
6423
|
+
this.ora.succeed(
|
|
6424
|
+
`Reset to ${this.platformKit.platformConfig.baseBranchName}`
|
|
6425
|
+
);
|
|
6426
|
+
this.ora.start("Restoring target files");
|
|
6427
|
+
const targetFiles = ["i18n.lock"];
|
|
6428
|
+
const targetFileNames = execSync2(
|
|
6429
|
+
`npx lingo.dev@latest show files --target ${this.platformKit.platformConfig.baseBranchName}`,
|
|
6430
|
+
{ encoding: "utf8" }
|
|
6431
|
+
).split("\n").filter(Boolean);
|
|
6432
|
+
targetFiles.push(...targetFileNames);
|
|
6433
|
+
execSync2(`git fetch origin ${this.i18nBranchName}`, { stdio: "inherit" });
|
|
6434
|
+
for (const file of targetFiles) {
|
|
6435
|
+
try {
|
|
6436
|
+
execSync2(`git checkout FETCH_HEAD -- ${file}`, { stdio: "inherit" });
|
|
6437
|
+
} catch (error2) {
|
|
6438
|
+
this.ora.warn(`Skipping non-existent file: ${file}`);
|
|
6439
|
+
continue;
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
this.ora.succeed("Restored target files");
|
|
6443
|
+
}
|
|
6444
|
+
this.ora.start("Checking for changes to commit");
|
|
6445
|
+
const hasChanges = this.checkCommitableChanges();
|
|
6446
|
+
if (hasChanges) {
|
|
6447
|
+
execSync2("git add .", { stdio: "inherit" });
|
|
6448
|
+
execSync2(
|
|
6449
|
+
`git commit -m "chore: sync with ${this.platformKit.platformConfig.baseBranchName}" --no-verify`,
|
|
6450
|
+
{
|
|
6451
|
+
stdio: "inherit"
|
|
6452
|
+
}
|
|
6453
|
+
);
|
|
6454
|
+
this.ora.succeed("Committed additional changes");
|
|
6455
|
+
} else {
|
|
6456
|
+
this.ora.succeed("No changes to commit");
|
|
6457
|
+
}
|
|
6456
6458
|
}
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6459
|
+
getPrBodyContent() {
|
|
6460
|
+
return `
|
|
6461
|
+
Hey team,
|
|
6462
|
+
|
|
6463
|
+
[**Lingo.dev**](https://lingo.dev) here with fresh translations!
|
|
6464
|
+
|
|
6465
|
+
### In this update
|
|
6466
|
+
|
|
6467
|
+
- Added missing translations
|
|
6468
|
+
- Performed brand voice, context and glossary checks
|
|
6469
|
+
- Enhanced translations using Lingo.dev Localization Engine
|
|
6470
|
+
|
|
6471
|
+
### Next Steps
|
|
6472
|
+
|
|
6473
|
+
- [ ] Review the changes
|
|
6474
|
+
- [ ] Merge when ready
|
|
6475
|
+
`.trim();
|
|
6472
6476
|
}
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6477
|
+
};
|
|
6478
|
+
|
|
6479
|
+
// src/cli/cmd/ci/platforms/bitbucket.ts
|
|
6480
|
+
import { execSync as execSync4 } from "child_process";
|
|
6481
|
+
import bbLib from "bitbucket";
|
|
6482
|
+
import Z8 from "zod";
|
|
6483
|
+
|
|
6484
|
+
// src/cli/cmd/ci/platforms/_base.ts
|
|
6485
|
+
import { execSync as execSync3 } from "child_process";
|
|
6486
|
+
import Z7 from "zod";
|
|
6487
|
+
var defaultMessage = "feat: update translations via @lingodotdev";
|
|
6488
|
+
var PlatformKit = class {
|
|
6489
|
+
gitConfig(token, repoUrl) {
|
|
6490
|
+
if (token && repoUrl) {
|
|
6491
|
+
execSync3(`git remote set-url origin ${repoUrl}`, {
|
|
6492
|
+
stdio: "inherit"
|
|
6493
|
+
});
|
|
6494
|
+
}
|
|
6482
6495
|
}
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
}
|
|
6500
|
-
} else if (flags.bucket?.some((bucket) => !i18nConfig.buckets[bucket])) {
|
|
6501
|
-
throw new CLIError({
|
|
6502
|
-
message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
|
|
6503
|
-
docUrl: "bucketNotFound"
|
|
6504
|
-
});
|
|
6496
|
+
get config() {
|
|
6497
|
+
const env = Z7.object({
|
|
6498
|
+
LINGODOTDEV_API_KEY: Z7.string(),
|
|
6499
|
+
LINGODOTDEV_PULL_REQUEST: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()),
|
|
6500
|
+
LINGODOTDEV_COMMIT_MESSAGE: Z7.string().optional(),
|
|
6501
|
+
LINGODOTDEV_PULL_REQUEST_TITLE: Z7.string().optional(),
|
|
6502
|
+
LINGODOTDEV_WORKING_DIRECTORY: Z7.string().optional(),
|
|
6503
|
+
LINGODOTDEV_PROCESS_OWN_COMMITS: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()).optional()
|
|
6504
|
+
}).parse(process.env);
|
|
6505
|
+
return {
|
|
6506
|
+
replexicaApiKey: env.LINGODOTDEV_API_KEY,
|
|
6507
|
+
isPullRequestMode: env.LINGODOTDEV_PULL_REQUEST,
|
|
6508
|
+
commitMessage: env.LINGODOTDEV_COMMIT_MESSAGE || defaultMessage,
|
|
6509
|
+
pullRequestTitle: env.LINGODOTDEV_PULL_REQUEST_TITLE || defaultMessage,
|
|
6510
|
+
workingDir: env.LINGODOTDEV_WORKING_DIRECTORY || ".",
|
|
6511
|
+
processOwnCommits: env.LINGODOTDEV_PROCESS_OWN_COMMITS || false
|
|
6512
|
+
};
|
|
6505
6513
|
}
|
|
6506
|
-
}
|
|
6507
|
-
|
|
6508
|
-
// src/cli/cmd/may-the-fourth.ts
|
|
6509
|
-
import { Command as Command16 } from "interactive-commander";
|
|
6510
|
-
import * as cp from "node:child_process";
|
|
6511
|
-
import figlet from "figlet";
|
|
6512
|
-
import chalk7 from "chalk";
|
|
6513
|
-
import { vice } from "gradient-string";
|
|
6514
|
-
var colors2 = {
|
|
6515
|
-
orange: "#ff6600",
|
|
6516
|
-
green: "#6ae300",
|
|
6517
|
-
blue: "#0090ff",
|
|
6518
|
-
yellow: "#ffcc00",
|
|
6519
|
-
grey: "#808080",
|
|
6520
|
-
red: "#ff0000"
|
|
6521
6514
|
};
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6515
|
+
|
|
6516
|
+
// src/cli/cmd/ci/platforms/bitbucket.ts
|
|
6517
|
+
var { Bitbucket } = bbLib;
|
|
6518
|
+
var BitbucketPlatformKit = class extends PlatformKit {
|
|
6519
|
+
_bb;
|
|
6520
|
+
get bb() {
|
|
6521
|
+
if (!this._bb) {
|
|
6522
|
+
this._bb = new Bitbucket({
|
|
6523
|
+
auth: { token: this.platformConfig.bbToken || "" }
|
|
6524
|
+
});
|
|
6525
|
+
}
|
|
6526
|
+
return this._bb;
|
|
6527
|
+
}
|
|
6528
|
+
async branchExists({ branch }) {
|
|
6529
|
+
return await this.bb.repositories.getBranch({
|
|
6530
|
+
workspace: this.platformConfig.repositoryOwner,
|
|
6531
|
+
repo_slug: this.platformConfig.repositoryName,
|
|
6532
|
+
name: branch
|
|
6533
|
+
}).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
|
|
6534
|
+
}
|
|
6535
|
+
async getOpenPullRequestNumber({ branch }) {
|
|
6536
|
+
return await this.bb.repositories.listPullRequests({
|
|
6537
|
+
workspace: this.platformConfig.repositoryOwner,
|
|
6538
|
+
repo_slug: this.platformConfig.repositoryName,
|
|
6539
|
+
state: "OPEN"
|
|
6540
|
+
}).then(({ data: { values } }) => {
|
|
6541
|
+
return values?.find(
|
|
6542
|
+
({ source, destination }) => source?.branch?.name === branch && destination?.branch?.name === this.platformConfig.baseBranchName
|
|
6543
|
+
);
|
|
6544
|
+
}).then((pr) => pr?.id);
|
|
6545
|
+
}
|
|
6546
|
+
async closePullRequest({ pullRequestNumber }) {
|
|
6547
|
+
await this.bb.repositories.declinePullRequest({
|
|
6548
|
+
workspace: this.platformConfig.repositoryOwner,
|
|
6549
|
+
repo_slug: this.platformConfig.repositoryName,
|
|
6550
|
+
pull_request_id: pullRequestNumber
|
|
6531
6551
|
});
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6552
|
+
}
|
|
6553
|
+
async createPullRequest({
|
|
6554
|
+
title,
|
|
6555
|
+
body,
|
|
6556
|
+
head
|
|
6557
|
+
}) {
|
|
6558
|
+
return await this.bb.repositories.createPullRequest({
|
|
6559
|
+
workspace: this.platformConfig.repositoryOwner,
|
|
6560
|
+
repo_slug: this.platformConfig.repositoryName,
|
|
6561
|
+
_body: {
|
|
6562
|
+
title,
|
|
6563
|
+
description: body,
|
|
6564
|
+
source: { branch: { name: head } },
|
|
6565
|
+
destination: { branch: { name: this.platformConfig.baseBranchName } }
|
|
6566
|
+
}
|
|
6567
|
+
}).then(({ data }) => data.id ?? 0);
|
|
6568
|
+
}
|
|
6569
|
+
async commentOnPullRequest({
|
|
6570
|
+
pullRequestNumber,
|
|
6571
|
+
body
|
|
6572
|
+
}) {
|
|
6573
|
+
await this.bb.repositories.createPullRequestComment({
|
|
6574
|
+
workspace: this.platformConfig.repositoryOwner,
|
|
6575
|
+
repo_slug: this.platformConfig.repositoryName,
|
|
6576
|
+
pull_request_id: pullRequestNumber,
|
|
6577
|
+
_body: {
|
|
6578
|
+
content: {
|
|
6579
|
+
raw: body
|
|
6580
|
+
}
|
|
6535
6581
|
}
|
|
6536
|
-
resolve();
|
|
6537
6582
|
});
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6583
|
+
}
|
|
6584
|
+
async gitConfig() {
|
|
6585
|
+
execSync4("git config --unset http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy", {
|
|
6586
|
+
stdio: "inherit"
|
|
6541
6587
|
});
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
}
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
}
|
|
6569
|
-
async function renderHero() {
|
|
6570
|
-
console.log(
|
|
6571
|
-
`\u26A1\uFE0F ${chalk7.hex(colors2.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
|
|
6572
|
-
);
|
|
6573
|
-
console.log(" ");
|
|
6574
|
-
console.log(
|
|
6575
|
-
chalk7.hex(colors2.blue)("\u2B50 GitHub Repo: https://lingo.dev/go/gh")
|
|
6576
|
-
);
|
|
6577
|
-
console.log(chalk7.hex(colors2.blue)("\u{1F4AC} 24/7 Support: hi@lingo.dev"));
|
|
6578
|
-
}
|
|
6588
|
+
execSync4(
|
|
6589
|
+
"git config http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy http://host.docker.internal:29418/",
|
|
6590
|
+
{
|
|
6591
|
+
stdio: "inherit"
|
|
6592
|
+
}
|
|
6593
|
+
);
|
|
6594
|
+
}
|
|
6595
|
+
get platformConfig() {
|
|
6596
|
+
const env = Z8.object({
|
|
6597
|
+
BITBUCKET_BRANCH: Z8.string(),
|
|
6598
|
+
BITBUCKET_REPO_FULL_NAME: Z8.string(),
|
|
6599
|
+
BB_TOKEN: Z8.string().optional()
|
|
6600
|
+
}).parse(process.env);
|
|
6601
|
+
const [repositoryOwner, repositoryName] = env.BITBUCKET_REPO_FULL_NAME.split("/");
|
|
6602
|
+
return {
|
|
6603
|
+
baseBranchName: env.BITBUCKET_BRANCH,
|
|
6604
|
+
repositoryOwner,
|
|
6605
|
+
repositoryName,
|
|
6606
|
+
bbToken: env.BB_TOKEN
|
|
6607
|
+
};
|
|
6608
|
+
}
|
|
6609
|
+
buildPullRequestUrl(pullRequestNumber) {
|
|
6610
|
+
const { repositoryOwner, repositoryName } = this.platformConfig;
|
|
6611
|
+
return `https://bitbucket.org/${repositoryOwner}/${repositoryName}/pull-requests/${pullRequestNumber}`;
|
|
6612
|
+
}
|
|
6613
|
+
};
|
|
6579
6614
|
|
|
6580
|
-
//
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
},
|
|
6589
|
-
type: "module",
|
|
6590
|
-
sideEffects: false,
|
|
6591
|
-
exports: {
|
|
6592
|
-
"./cli": {
|
|
6593
|
-
types: "./build/cli.d.ts",
|
|
6594
|
-
import: "./build/cli.mjs",
|
|
6595
|
-
require: "./build/cli.cjs"
|
|
6596
|
-
},
|
|
6597
|
-
"./sdk": {
|
|
6598
|
-
types: "./build/sdk.d.ts",
|
|
6599
|
-
import: "./build/sdk.mjs",
|
|
6600
|
-
require: "./build/sdk.cjs"
|
|
6601
|
-
},
|
|
6602
|
-
"./spec": {
|
|
6603
|
-
types: "./build/spec.d.ts",
|
|
6604
|
-
import: "./build/spec.mjs",
|
|
6605
|
-
require: "./build/spec.cjs"
|
|
6606
|
-
},
|
|
6607
|
-
"./compiler": {
|
|
6608
|
-
types: "./build/compiler.d.ts",
|
|
6609
|
-
import: "./build/compiler.mjs",
|
|
6610
|
-
require: "./build/compiler.cjs"
|
|
6611
|
-
},
|
|
6612
|
-
"./react": {
|
|
6613
|
-
types: "./build/react.d.ts",
|
|
6614
|
-
import: "./build/react.mjs",
|
|
6615
|
-
require: "./build/react.cjs"
|
|
6616
|
-
},
|
|
6617
|
-
"./react-client": {
|
|
6618
|
-
types: "./build/react/client.d.ts",
|
|
6619
|
-
import: "./build/react/client.mjs",
|
|
6620
|
-
require: "./build/react/client.cjs"
|
|
6621
|
-
},
|
|
6622
|
-
"./react/client": {
|
|
6623
|
-
types: "./build/react/client.d.ts",
|
|
6624
|
-
import: "./build/react/client.mjs",
|
|
6625
|
-
require: "./build/react/client.cjs"
|
|
6626
|
-
},
|
|
6627
|
-
"./react-rsc": {
|
|
6628
|
-
types: "./build/react/rsc.d.ts",
|
|
6629
|
-
import: "./build/react/rsc.mjs",
|
|
6630
|
-
require: "./build/react/rsc.cjs"
|
|
6631
|
-
},
|
|
6632
|
-
"./react/rsc": {
|
|
6633
|
-
types: "./build/react/rsc.d.ts",
|
|
6634
|
-
import: "./build/react/rsc.mjs",
|
|
6635
|
-
require: "./build/react/rsc.cjs"
|
|
6636
|
-
},
|
|
6637
|
-
"./react-router": {
|
|
6638
|
-
types: "./build/react/react-router.d.ts",
|
|
6639
|
-
import: "./build/react/react-router.mjs",
|
|
6640
|
-
require: "./build/react/react-router.cjs"
|
|
6641
|
-
},
|
|
6642
|
-
"./react/react-router": {
|
|
6643
|
-
types: "./build/react/react-router.d.ts",
|
|
6644
|
-
import: "./build/react/react-router.mjs",
|
|
6645
|
-
require: "./build/react/react-router.cjs"
|
|
6615
|
+
// src/cli/cmd/ci/platforms/github.ts
|
|
6616
|
+
import { Octokit } from "octokit";
|
|
6617
|
+
import Z9 from "zod";
|
|
6618
|
+
var GitHubPlatformKit = class extends PlatformKit {
|
|
6619
|
+
_octokit;
|
|
6620
|
+
get octokit() {
|
|
6621
|
+
if (!this._octokit) {
|
|
6622
|
+
this._octokit = new Octokit({ auth: this.platformConfig.ghToken });
|
|
6646
6623
|
}
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6624
|
+
return this._octokit;
|
|
6625
|
+
}
|
|
6626
|
+
async branchExists({ branch }) {
|
|
6627
|
+
return await this.octokit.rest.repos.getBranch({
|
|
6628
|
+
branch,
|
|
6629
|
+
owner: this.platformConfig.repositoryOwner,
|
|
6630
|
+
repo: this.platformConfig.repositoryName
|
|
6631
|
+
}).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
|
|
6632
|
+
}
|
|
6633
|
+
async getOpenPullRequestNumber({ branch }) {
|
|
6634
|
+
return await this.octokit.rest.pulls.list({
|
|
6635
|
+
head: `${this.platformConfig.repositoryOwner}:${branch}`,
|
|
6636
|
+
owner: this.platformConfig.repositoryOwner,
|
|
6637
|
+
repo: this.platformConfig.repositoryName,
|
|
6638
|
+
base: this.platformConfig.baseBranchName,
|
|
6639
|
+
state: "open"
|
|
6640
|
+
}).then(({ data }) => data[0]).then((pr) => pr?.number);
|
|
6641
|
+
}
|
|
6642
|
+
async closePullRequest({ pullRequestNumber }) {
|
|
6643
|
+
await this.octokit.rest.pulls.update({
|
|
6644
|
+
pull_number: pullRequestNumber,
|
|
6645
|
+
owner: this.platformConfig.repositoryOwner,
|
|
6646
|
+
repo: this.platformConfig.repositoryName,
|
|
6647
|
+
state: "closed"
|
|
6648
|
+
});
|
|
6649
|
+
}
|
|
6650
|
+
async createPullRequest({
|
|
6651
|
+
head,
|
|
6652
|
+
title,
|
|
6653
|
+
body
|
|
6654
|
+
}) {
|
|
6655
|
+
return await this.octokit.rest.pulls.create({
|
|
6656
|
+
head,
|
|
6657
|
+
title,
|
|
6658
|
+
body,
|
|
6659
|
+
owner: this.platformConfig.repositoryOwner,
|
|
6660
|
+
repo: this.platformConfig.repositoryName,
|
|
6661
|
+
base: this.platformConfig.baseBranchName
|
|
6662
|
+
}).then(({ data }) => data.number);
|
|
6663
|
+
}
|
|
6664
|
+
async commentOnPullRequest({
|
|
6665
|
+
pullRequestNumber,
|
|
6666
|
+
body
|
|
6667
|
+
}) {
|
|
6668
|
+
await this.octokit.rest.issues.createComment({
|
|
6669
|
+
issue_number: pullRequestNumber,
|
|
6670
|
+
body,
|
|
6671
|
+
owner: this.platformConfig.repositoryOwner,
|
|
6672
|
+
repo: this.platformConfig.repositoryName
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
async gitConfig() {
|
|
6676
|
+
const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;
|
|
6677
|
+
const { processOwnCommits } = this.config;
|
|
6678
|
+
if (ghToken && processOwnCommits) {
|
|
6679
|
+
console.log(
|
|
6680
|
+
"Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again."
|
|
6681
|
+
);
|
|
6682
|
+
const url = `https://${ghToken}@github.com/${repositoryOwner}/${repositoryName}.git`;
|
|
6683
|
+
super.gitConfig(ghToken, url);
|
|
6674
6684
|
}
|
|
6675
|
-
}
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
"@ai-sdk/openai": "^1.3.22",
|
|
6697
|
-
"@babel/generator": "^7.27.1",
|
|
6698
|
-
"@babel/parser": "^7.27.1",
|
|
6699
|
-
"@babel/traverse": "^7.27.4",
|
|
6700
|
-
"@babel/types": "^7.27.1",
|
|
6701
|
-
"@datocms/cma-client-node": "^4.0.1",
|
|
6702
|
-
"@gitbeaker/rest": "^39.34.3",
|
|
6703
|
-
"@inkjs/ui": "^2.0.0",
|
|
6704
|
-
"@inquirer/prompts": "^7.4.1",
|
|
6705
|
-
"@lingo.dev/_sdk": "workspace:*",
|
|
6706
|
-
"@lingo.dev/_spec": "workspace:*",
|
|
6707
|
-
"@lingo.dev/_react": "workspace:*",
|
|
6708
|
-
"@lingo.dev/_compiler": "workspace:*",
|
|
6709
|
-
"@modelcontextprotocol/sdk": "^1.5.0",
|
|
6710
|
-
"@paralleldrive/cuid2": "^2.2.2",
|
|
6711
|
-
ai: "^4.3.15",
|
|
6712
|
-
bitbucket: "^2.12.0",
|
|
6713
|
-
chalk: "^5.4.1",
|
|
6714
|
-
"cli-progress": "^3.12.0",
|
|
6715
|
-
"cli-table3": "^0.6.5",
|
|
6716
|
-
cors: "^2.8.5",
|
|
6717
|
-
"csv-parse": "^5.6.0",
|
|
6718
|
-
"csv-stringify": "^6.5.2",
|
|
6719
|
-
"date-fns": "^4.1.0",
|
|
6720
|
-
dedent: "^1.5.3",
|
|
6721
|
-
diff: "^7.0.0",
|
|
6722
|
-
dotenv: "^16.4.7",
|
|
6723
|
-
express: "^5.1.0",
|
|
6724
|
-
"external-editor": "^3.1.0",
|
|
6725
|
-
figlet: "^1.8.0",
|
|
6726
|
-
flat: "^6.0.1",
|
|
6727
|
-
"gettext-parser": "^8.0.0",
|
|
6728
|
-
glob: "<11.0.0",
|
|
6729
|
-
"gradient-string": "^3.0.0",
|
|
6730
|
-
"gray-matter": "^4.0.3",
|
|
6731
|
-
ini: "^5.0.0",
|
|
6732
|
-
ink: "^4.2.0",
|
|
6733
|
-
"ink-progress-bar": "^3.0.0",
|
|
6734
|
-
"ink-spinner": "^5.0.0",
|
|
6735
|
-
inquirer: "^12.6.0",
|
|
6736
|
-
"interactive-commander": "^0.5.194",
|
|
6737
|
-
"is-url": "^1.2.4",
|
|
6738
|
-
jsdom: "^25.0.1",
|
|
6739
|
-
json5: "^2.2.3",
|
|
6740
|
-
jsonrepair: "^3.11.2",
|
|
6741
|
-
listr2: "^8.3.2",
|
|
6742
|
-
lodash: "^4.17.21",
|
|
6743
|
-
marked: "^15.0.6",
|
|
6744
|
-
"mdast-util-from-markdown": "^2.0.2",
|
|
6745
|
-
"mdast-util-gfm": "^3.1.0",
|
|
6746
|
-
"micromark-extension-gfm": "^3.0.0",
|
|
6747
|
-
"node-machine-id": "^1.1.12",
|
|
6748
|
-
"node-webvtt": "^1.9.4",
|
|
6749
|
-
"object-hash": "^3.0.0",
|
|
6750
|
-
octokit: "^4.0.2",
|
|
6751
|
-
open: "^10.1.2",
|
|
6752
|
-
ora: "^8.1.1",
|
|
6753
|
-
"p-limit": "^6.2.0",
|
|
6754
|
-
"php-array-reader": "^2.1.2",
|
|
6755
|
-
plist: "^3.1.0",
|
|
6756
|
-
"posthog-node": "^4.17.0",
|
|
6757
|
-
prettier: "^3.4.2",
|
|
6758
|
-
react: "^18.3.1",
|
|
6759
|
-
"rehype-stringify": "^10.0.1",
|
|
6760
|
-
"remark-disable-tokenizers": "^1.1.1",
|
|
6761
|
-
"remark-frontmatter": "^5.0.0",
|
|
6762
|
-
"remark-gfm": "^4.0.1",
|
|
6763
|
-
"remark-mdx": "^3.1.0",
|
|
6764
|
-
"remark-mdx-frontmatter": "^5.1.0",
|
|
6765
|
-
"remark-parse": "^11.0.0",
|
|
6766
|
-
"remark-rehype": "^11.1.2",
|
|
6767
|
-
"remark-stringify": "^11.0.0",
|
|
6768
|
-
"srt-parser-2": "^1.2.3",
|
|
6769
|
-
unified: "^11.0.5",
|
|
6770
|
-
"unist-util-visit": "^5.0.0",
|
|
6771
|
-
vfile: "^6.0.3",
|
|
6772
|
-
xliff: "^6.2.1",
|
|
6773
|
-
xml2js: "^0.6.2",
|
|
6774
|
-
xpath: "^0.0.34",
|
|
6775
|
-
yaml: "^2.7.0",
|
|
6776
|
-
zod: "^3.24.1"
|
|
6777
|
-
},
|
|
6778
|
-
devDependencies: {
|
|
6779
|
-
"@types/babel__generator": "^7.27.0",
|
|
6780
|
-
"@types/cli-progress": "^3.11.6",
|
|
6781
|
-
"@types/cors": "^2.8.17",
|
|
6782
|
-
"@types/diff": "^7.0.0",
|
|
6783
|
-
"@types/express": "^5.0.1",
|
|
6784
|
-
"@types/figlet": "^1.7.0",
|
|
6785
|
-
"@types/gettext-parser": "^4.0.4",
|
|
6786
|
-
"@types/glob": "^8.1.0",
|
|
6787
|
-
"@types/ini": "^4.1.1",
|
|
6788
|
-
"@types/is-url": "^1.2.32",
|
|
6789
|
-
"@types/jsdom": "^21.1.7",
|
|
6790
|
-
"@types/lodash": "^4.17.16",
|
|
6791
|
-
"@types/mdast": "^4.0.4",
|
|
6792
|
-
"@types/node": "^22.10.2",
|
|
6793
|
-
"@types/node-gettext": "^3.0.6",
|
|
6794
|
-
"@types/object-hash": "^3.0.6",
|
|
6795
|
-
"@types/plist": "^3.0.5",
|
|
6796
|
-
"@types/react": "^18.3.20",
|
|
6797
|
-
"@types/xml2js": "^0.4.14",
|
|
6798
|
-
tsup: "^8.3.5",
|
|
6799
|
-
typescript: "^5.8.3",
|
|
6800
|
-
vitest: "^3.1.2"
|
|
6801
|
-
},
|
|
6802
|
-
engines: {
|
|
6803
|
-
node: ">=18"
|
|
6804
|
-
},
|
|
6805
|
-
packageManager: "pnpm@9.12.3"
|
|
6685
|
+
}
|
|
6686
|
+
get platformConfig() {
|
|
6687
|
+
const env = Z9.object({
|
|
6688
|
+
GITHUB_REPOSITORY: Z9.string(),
|
|
6689
|
+
GITHUB_REPOSITORY_OWNER: Z9.string(),
|
|
6690
|
+
GITHUB_REF_NAME: Z9.string(),
|
|
6691
|
+
GITHUB_HEAD_REF: Z9.string(),
|
|
6692
|
+
GH_TOKEN: Z9.string().optional()
|
|
6693
|
+
}).parse(process.env);
|
|
6694
|
+
const baseBranchName = !env.GITHUB_REF_NAME.endsWith("/merge") ? env.GITHUB_REF_NAME : env.GITHUB_HEAD_REF;
|
|
6695
|
+
return {
|
|
6696
|
+
ghToken: env.GH_TOKEN,
|
|
6697
|
+
baseBranchName,
|
|
6698
|
+
repositoryOwner: env.GITHUB_REPOSITORY_OWNER,
|
|
6699
|
+
repositoryName: env.GITHUB_REPOSITORY.split("/")[1]
|
|
6700
|
+
};
|
|
6701
|
+
}
|
|
6702
|
+
buildPullRequestUrl(pullRequestNumber) {
|
|
6703
|
+
const { repositoryOwner, repositoryName } = this.platformConfig;
|
|
6704
|
+
return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;
|
|
6705
|
+
}
|
|
6806
6706
|
};
|
|
6807
6707
|
|
|
6808
|
-
// src/cli/cmd/
|
|
6809
|
-
import {
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6708
|
+
// src/cli/cmd/ci/platforms/gitlab.ts
|
|
6709
|
+
import { Gitlab } from "@gitbeaker/rest";
|
|
6710
|
+
import Z10 from "zod";
|
|
6711
|
+
var gl = new Gitlab({ token: "" });
|
|
6712
|
+
var GitlabPlatformKit = class extends PlatformKit {
|
|
6713
|
+
_gitlab;
|
|
6714
|
+
constructor() {
|
|
6715
|
+
super();
|
|
6716
|
+
process.chdir(this.platformConfig.projectDir);
|
|
6717
|
+
}
|
|
6718
|
+
get gitlab() {
|
|
6719
|
+
if (!this._gitlab) {
|
|
6720
|
+
this._gitlab = new Gitlab({
|
|
6721
|
+
token: this.platformConfig.glToken || ""
|
|
6722
|
+
});
|
|
6723
|
+
}
|
|
6724
|
+
return this._gitlab;
|
|
6725
|
+
}
|
|
6726
|
+
get platformConfig() {
|
|
6727
|
+
const env = Z10.object({
|
|
6728
|
+
GL_TOKEN: Z10.string().optional(),
|
|
6729
|
+
CI_COMMIT_BRANCH: Z10.string(),
|
|
6730
|
+
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: Z10.string().optional(),
|
|
6731
|
+
CI_PROJECT_NAMESPACE: Z10.string(),
|
|
6732
|
+
CI_PROJECT_NAME: Z10.string(),
|
|
6733
|
+
CI_PROJECT_ID: Z10.string(),
|
|
6734
|
+
CI_PROJECT_DIR: Z10.string(),
|
|
6735
|
+
CI_REPOSITORY_URL: Z10.string()
|
|
6736
|
+
}).parse(process.env);
|
|
6737
|
+
const config = {
|
|
6738
|
+
glToken: env.GL_TOKEN,
|
|
6739
|
+
baseBranchName: env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ?? env.CI_COMMIT_BRANCH,
|
|
6740
|
+
repositoryOwner: env.CI_PROJECT_NAMESPACE,
|
|
6741
|
+
repositoryName: env.CI_PROJECT_NAME,
|
|
6742
|
+
gitlabProjectId: env.CI_PROJECT_ID,
|
|
6743
|
+
projectDir: env.CI_PROJECT_DIR,
|
|
6744
|
+
reporitoryUrl: env.CI_REPOSITORY_URL
|
|
6745
|
+
};
|
|
6746
|
+
return config;
|
|
6747
|
+
}
|
|
6748
|
+
async branchExists({ branch }) {
|
|
6749
|
+
try {
|
|
6750
|
+
await this.gitlab.Branches.show(
|
|
6751
|
+
this.platformConfig.gitlabProjectId,
|
|
6752
|
+
branch
|
|
6753
|
+
);
|
|
6754
|
+
return true;
|
|
6755
|
+
} catch {
|
|
6756
|
+
return false;
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
async getOpenPullRequestNumber({
|
|
6760
|
+
branch
|
|
6761
|
+
}) {
|
|
6762
|
+
const mergeRequests = await this.gitlab.MergeRequests.all({
|
|
6763
|
+
projectId: this.platformConfig.gitlabProjectId,
|
|
6764
|
+
sourceBranch: branch,
|
|
6765
|
+
state: "opened"
|
|
6766
|
+
});
|
|
6767
|
+
return mergeRequests[0]?.iid;
|
|
6768
|
+
}
|
|
6769
|
+
async closePullRequest({
|
|
6770
|
+
pullRequestNumber
|
|
6771
|
+
}) {
|
|
6772
|
+
await this.gitlab.MergeRequests.edit(
|
|
6773
|
+
this.platformConfig.gitlabProjectId,
|
|
6774
|
+
pullRequestNumber,
|
|
6775
|
+
{
|
|
6776
|
+
stateEvent: "close"
|
|
6777
|
+
}
|
|
6778
|
+
);
|
|
6779
|
+
}
|
|
6780
|
+
async createPullRequest({
|
|
6781
|
+
head,
|
|
6782
|
+
title,
|
|
6783
|
+
body
|
|
6784
|
+
}) {
|
|
6785
|
+
const mr = await this.gitlab.MergeRequests.create(
|
|
6786
|
+
this.platformConfig.gitlabProjectId,
|
|
6787
|
+
head,
|
|
6788
|
+
this.platformConfig.baseBranchName,
|
|
6789
|
+
title,
|
|
6790
|
+
{
|
|
6791
|
+
description: body
|
|
6792
|
+
}
|
|
6793
|
+
);
|
|
6794
|
+
return mr.iid;
|
|
6795
|
+
}
|
|
6796
|
+
async commentOnPullRequest({
|
|
6797
|
+
pullRequestNumber,
|
|
6798
|
+
body
|
|
6799
|
+
}) {
|
|
6800
|
+
await this.gitlab.MergeRequestNotes.create(
|
|
6801
|
+
this.platformConfig.gitlabProjectId,
|
|
6802
|
+
pullRequestNumber,
|
|
6803
|
+
body
|
|
6804
|
+
);
|
|
6805
|
+
}
|
|
6806
|
+
gitConfig() {
|
|
6807
|
+
const glToken = this.platformConfig.glToken;
|
|
6808
|
+
const url = `https://oauth2:${glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;
|
|
6809
|
+
super.gitConfig(glToken, url);
|
|
6810
|
+
}
|
|
6811
|
+
buildPullRequestUrl(pullRequestNumber) {
|
|
6812
|
+
return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;
|
|
6824
6813
|
}
|
|
6825
6814
|
};
|
|
6826
6815
|
|
|
6827
|
-
// src/cli/
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6816
|
+
// src/cli/cmd/ci/platforms/index.ts
|
|
6817
|
+
var getPlatformKit = () => {
|
|
6818
|
+
if (process.env.BITBUCKET_PIPELINE_UUID) {
|
|
6819
|
+
return new BitbucketPlatformKit();
|
|
6820
|
+
}
|
|
6821
|
+
if (process.env.GITHUB_ACTION) {
|
|
6822
|
+
return new GitHubPlatformKit();
|
|
6823
|
+
}
|
|
6824
|
+
if (process.env.GITLAB_CI) {
|
|
6825
|
+
return new GitlabPlatformKit();
|
|
6826
|
+
}
|
|
6827
|
+
throw new Error("This platform is not supported");
|
|
6828
|
+
};
|
|
6837
6829
|
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6830
|
+
// src/cli/cmd/ci/index.ts
|
|
6831
|
+
var ci_default = new Command15().command("ci").description("Run Lingo.dev CI/CD action").helpOption("-h, --help", "Show help").option("--parallel [boolean]", "Run in parallel mode", parseBooleanArg).option("--api-key <key>", "API key").option(
|
|
6832
|
+
"--pull-request [boolean]",
|
|
6833
|
+
"Create a pull request with the changes",
|
|
6834
|
+
parseBooleanArg
|
|
6835
|
+
).option("--commit-message <message>", "Commit message").option("--pull-request-title <title>", "Pull request title").option("--working-directory <dir>", "Working directory").option(
|
|
6836
|
+
"--process-own-commits [boolean]",
|
|
6837
|
+
"Process commits made by this action",
|
|
6838
|
+
parseBooleanArg
|
|
6839
|
+
).action(async (options) => {
|
|
6840
|
+
const settings = getSettings(options.apiKey);
|
|
6841
|
+
console.log(options);
|
|
6842
|
+
if (!settings.auth.apiKey) {
|
|
6843
|
+
console.error("No API key provided");
|
|
6844
|
+
return;
|
|
6844
6845
|
}
|
|
6845
|
-
const
|
|
6846
|
-
|
|
6847
|
-
|
|
6846
|
+
const authenticator = createAuthenticator({
|
|
6847
|
+
apiUrl: settings.auth.apiUrl,
|
|
6848
|
+
apiKey: settings.auth.apiKey
|
|
6848
6849
|
});
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
return { authenticated: false };
|
|
6860
|
-
}
|
|
6850
|
+
const auth = await authenticator.whoami();
|
|
6851
|
+
if (!auth) {
|
|
6852
|
+
console.error("Not authenticated");
|
|
6853
|
+
return;
|
|
6854
|
+
}
|
|
6855
|
+
const env = {
|
|
6856
|
+
LINGODOTDEV_API_KEY: settings.auth.apiKey,
|
|
6857
|
+
LINGODOTDEV_PULL_REQUEST: options.pullRequest?.toString() || "false",
|
|
6858
|
+
...options.commitMessage && {
|
|
6859
|
+
LINGODOTDEV_COMMIT_MESSAGE: options.commitMessage
|
|
6861
6860
|
},
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
targetLocale: input2.targetLocale,
|
|
6871
|
-
reference: {
|
|
6872
|
-
[input2.sourceLocale]: input2.sourceData,
|
|
6873
|
-
[input2.targetLocale]: input2.targetData
|
|
6874
|
-
}
|
|
6875
|
-
},
|
|
6876
|
-
onProgress
|
|
6877
|
-
);
|
|
6878
|
-
return processedData;
|
|
6861
|
+
...options.pullRequestTitle && {
|
|
6862
|
+
LINGODOTDEV_PULL_REQUEST_TITLE: options.pullRequestTitle
|
|
6863
|
+
},
|
|
6864
|
+
...options.workingDirectory && {
|
|
6865
|
+
LINGODOTDEV_WORKING_DIRECTORY: options.workingDirectory
|
|
6866
|
+
},
|
|
6867
|
+
...options.processOwnCommits && {
|
|
6868
|
+
LINGODOTDEV_PROCESS_OWN_COMMITS: options.processOwnCommits.toString()
|
|
6879
6869
|
}
|
|
6880
6870
|
};
|
|
6881
|
-
}
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
);
|
|
6904
|
-
case "openai":
|
|
6905
|
-
return createAiSdkLocalizer({
|
|
6906
|
-
factory: (params) => createOpenAI2(params).languageModel(provider.model),
|
|
6907
|
-
id: provider.id,
|
|
6908
|
-
prompt: provider.prompt,
|
|
6909
|
-
apiKeyName: "OPENAI_API_KEY",
|
|
6910
|
-
baseUrl: provider.baseUrl
|
|
6911
|
-
});
|
|
6912
|
-
case "anthropic":
|
|
6913
|
-
return createAiSdkLocalizer({
|
|
6914
|
-
factory: (params) => createAnthropic2(params).languageModel(provider.model),
|
|
6915
|
-
id: provider.id,
|
|
6916
|
-
prompt: provider.prompt,
|
|
6917
|
-
apiKeyName: "ANTHROPIC_API_KEY",
|
|
6918
|
-
baseUrl: provider.baseUrl
|
|
6919
|
-
});
|
|
6871
|
+
process.env = { ...process.env, ...env };
|
|
6872
|
+
const ora = createOra();
|
|
6873
|
+
const platformKit = getPlatformKit();
|
|
6874
|
+
const { isPullRequestMode } = platformKit.config;
|
|
6875
|
+
ora.info(`Pull request mode: ${isPullRequestMode ? "on" : "off"}`);
|
|
6876
|
+
const flow = isPullRequestMode ? new PullRequestFlow(ora, platformKit) : new InBranchFlow(ora, platformKit);
|
|
6877
|
+
const canRun = await flow.preRun?.();
|
|
6878
|
+
if (canRun === false) {
|
|
6879
|
+
return;
|
|
6880
|
+
}
|
|
6881
|
+
const hasChanges = await flow.run({
|
|
6882
|
+
parallel: options.parallel
|
|
6883
|
+
});
|
|
6884
|
+
if (!hasChanges) {
|
|
6885
|
+
return;
|
|
6886
|
+
}
|
|
6887
|
+
await flow.postRun?.();
|
|
6888
|
+
});
|
|
6889
|
+
function parseBooleanArg(val) {
|
|
6890
|
+
if (val === true) return true;
|
|
6891
|
+
if (typeof val === "string") {
|
|
6892
|
+
return val.toLowerCase() === "true";
|
|
6920
6893
|
}
|
|
6894
|
+
return false;
|
|
6921
6895
|
}
|
|
6922
|
-
function createAiSdkLocalizer(params) {
|
|
6923
|
-
const apiKey = process.env[params.apiKeyName];
|
|
6924
|
-
if (!apiKey) {
|
|
6925
|
-
throw new Error(
|
|
6926
|
-
dedent6`
|
|
6927
|
-
You're trying to use raw ${chalk10.dim(params.id)} API for translation, however, ${chalk10.dim(params.apiKeyName)} environment variable is not set.
|
|
6928
|
-
|
|
6929
|
-
To fix this issue:
|
|
6930
|
-
1. Set ${chalk10.dim(params.apiKeyName)} in your environment variables, or
|
|
6931
|
-
2. Remove the ${chalk10.italic("provider")} node from your i18n.json configuration to switch to ${chalk10.hex(colors.green)("Lingo.dev")}
|
|
6932
6896
|
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6897
|
+
// src/cli/cmd/status.ts
|
|
6898
|
+
import { bucketTypeSchema as bucketTypeSchema4, localeCodeSchema as localeCodeSchema3, resolveOverriddenLocale as resolveOverriddenLocale7 } from "@lingo.dev/_spec";
|
|
6899
|
+
import { Command as Command16 } from "interactive-commander";
|
|
6900
|
+
import Z11 from "zod";
|
|
6901
|
+
import Ora8 from "ora";
|
|
6902
|
+
import chalk14 from "chalk";
|
|
6903
|
+
import Table from "cli-table3";
|
|
6904
|
+
var status_default = new Command16().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
|
|
6905
|
+
"--file [files...]",
|
|
6906
|
+
"File to process. Process only a specific path, may contain asterisk * to match multiple files."
|
|
6907
|
+
).option("--force", "Ignore lockfile and process all keys, useful for estimating full re-translation").option("--verbose", "Show detailed output including key-level word counts").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").action(async function(options) {
|
|
6908
|
+
const ora = Ora8();
|
|
6909
|
+
const flags = parseFlags2(options);
|
|
6910
|
+
let authId = null;
|
|
6911
|
+
try {
|
|
6912
|
+
ora.start("Loading configuration...");
|
|
6913
|
+
const i18nConfig = getConfig();
|
|
6914
|
+
const settings = getSettings(flags.apiKey);
|
|
6915
|
+
ora.succeed("Configuration loaded");
|
|
6916
|
+
try {
|
|
6917
|
+
ora.start("Checking authentication status...");
|
|
6918
|
+
const auth = await tryAuthenticate(settings);
|
|
6919
|
+
if (auth) {
|
|
6920
|
+
authId = auth.id;
|
|
6921
|
+
ora.succeed(`Authenticated as ${auth.email}`);
|
|
6922
|
+
} else {
|
|
6923
|
+
ora.info(
|
|
6924
|
+
"Not authenticated. Continuing without authentication. (Run `lingo.dev auth --login` to authenticate)"
|
|
6925
|
+
);
|
|
6926
|
+
}
|
|
6927
|
+
} catch (error) {
|
|
6928
|
+
ora.info("Authentication failed. Continuing without authentication.");
|
|
6929
|
+
}
|
|
6930
|
+
ora.start("Validating localization configuration...");
|
|
6931
|
+
validateParams2(i18nConfig, flags);
|
|
6932
|
+
ora.succeed("Localization configuration is valid");
|
|
6933
|
+
trackEvent(authId || "status", "cmd.status.start", {
|
|
6934
|
+
i18nConfig,
|
|
6935
|
+
flags
|
|
6936
|
+
});
|
|
6937
|
+
let buckets = getBuckets(i18nConfig);
|
|
6938
|
+
if (flags.bucket?.length) {
|
|
6939
|
+
buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
|
|
6940
|
+
}
|
|
6941
|
+
ora.succeed("Buckets retrieved");
|
|
6942
|
+
if (flags.file?.length) {
|
|
6943
|
+
buckets = buckets.map((bucket) => {
|
|
6944
|
+
const paths = bucket.paths.filter((path16) => flags.file.find((file) => path16.pathPattern?.match(file)));
|
|
6945
|
+
return { ...bucket, paths };
|
|
6946
|
+
}).filter((bucket) => bucket.paths.length > 0);
|
|
6947
|
+
if (buckets.length === 0) {
|
|
6948
|
+
ora.fail("No buckets found. All buckets were filtered out by --file option.");
|
|
6949
|
+
process.exit(1);
|
|
6950
|
+
} else {
|
|
6951
|
+
ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
|
|
6952
|
+
buckets.map((bucket) => {
|
|
6953
|
+
ora.info(` ${bucket.type}:`);
|
|
6954
|
+
bucket.paths.forEach((path16) => {
|
|
6955
|
+
ora.info(` - ${path16.pathPattern}`);
|
|
6956
|
+
});
|
|
6953
6957
|
});
|
|
6954
|
-
return { authenticated: true, username: "anonymous" };
|
|
6955
|
-
} catch (error) {
|
|
6956
|
-
return { authenticated: false };
|
|
6957
6958
|
}
|
|
6958
|
-
}
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
sourceLocale: "en",
|
|
6972
|
-
targetLocale: "es",
|
|
6973
|
-
data: {
|
|
6974
|
-
message: "Hola, mundo!"
|
|
6975
|
-
}
|
|
6976
|
-
}
|
|
6977
|
-
]
|
|
6978
|
-
];
|
|
6979
|
-
const payload = {
|
|
6980
|
-
sourceLocale: input2.sourceLocale,
|
|
6981
|
-
targetLocale: input2.targetLocale,
|
|
6982
|
-
data: input2.processableData
|
|
6959
|
+
}
|
|
6960
|
+
const targetLocales = flags.locale?.length ? flags.locale : i18nConfig.locale.targets;
|
|
6961
|
+
let totalSourceKeyCount = 0;
|
|
6962
|
+
let uniqueKeysToTranslate = 0;
|
|
6963
|
+
let totalExistingTranslations = 0;
|
|
6964
|
+
const totalWordCount = /* @__PURE__ */ new Map();
|
|
6965
|
+
const languageStats = {};
|
|
6966
|
+
for (const locale of targetLocales) {
|
|
6967
|
+
languageStats[locale] = {
|
|
6968
|
+
complete: 0,
|
|
6969
|
+
missing: 0,
|
|
6970
|
+
updated: 0,
|
|
6971
|
+
words: 0
|
|
6983
6972
|
};
|
|
6984
|
-
|
|
6985
|
-
model,
|
|
6986
|
-
messages: [
|
|
6987
|
-
{ role: "system", content: systemPrompt },
|
|
6988
|
-
{ role: "user", content: "OK" },
|
|
6989
|
-
...shots.flatMap(
|
|
6990
|
-
([userShot, assistantShot]) => [
|
|
6991
|
-
{ role: "user", content: JSON.stringify(userShot) },
|
|
6992
|
-
{ role: "assistant", content: JSON.stringify(assistantShot) }
|
|
6993
|
-
]
|
|
6994
|
-
),
|
|
6995
|
-
{ role: "user", content: JSON.stringify(payload) }
|
|
6996
|
-
]
|
|
6997
|
-
});
|
|
6998
|
-
const result = JSON.parse(response.text);
|
|
6999
|
-
const index = result.data.indexOf("{");
|
|
7000
|
-
const lastIndex = result.data.lastIndexOf("}");
|
|
7001
|
-
const trimmed = result.data.slice(index, lastIndex + 1);
|
|
7002
|
-
const repaired = jsonrepair3(trimmed);
|
|
7003
|
-
const finalResult = JSON.parse(repaired);
|
|
7004
|
-
return finalResult.data;
|
|
6973
|
+
totalWordCount.set(locale, 0);
|
|
7005
6974
|
}
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
(locale) => !ctx.config?.locale.targets.includes(locale)
|
|
7043
|
-
)) {
|
|
7044
|
-
throw new Error(
|
|
7045
|
-
`One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list first and try again.`
|
|
7046
|
-
);
|
|
7047
|
-
} else if (ctx.flags.bucket?.some(
|
|
7048
|
-
(bucket) => !ctx.config?.buckets[bucket]
|
|
7049
|
-
)) {
|
|
7050
|
-
throw new Error(
|
|
7051
|
-
`One or more specified buckets do not exist in i18n.json. Please add them to the list first and try again.`
|
|
7052
|
-
);
|
|
7053
|
-
}
|
|
7054
|
-
task.title = `Loaded i18n configuration`;
|
|
7055
|
-
}
|
|
7056
|
-
},
|
|
7057
|
-
{
|
|
7058
|
-
title: "Selecting localization provider",
|
|
7059
|
-
task: async (ctx, task) => {
|
|
7060
|
-
ctx.localizer = createLocalizer(ctx.config?.provider);
|
|
7061
|
-
if (!ctx.localizer) {
|
|
7062
|
-
throw new Error(
|
|
7063
|
-
"Could not create localization provider. Please check your i18n.json configuration."
|
|
7064
|
-
);
|
|
7065
|
-
}
|
|
7066
|
-
task.title = ctx.localizer.id === "Lingo.dev" ? `Using ${chalk11.hex(colors.green)(ctx.localizer.id)} provider` : `Using raw ${chalk11.hex(colors.yellow)(ctx.localizer.id)} API`;
|
|
7067
|
-
}
|
|
7068
|
-
},
|
|
7069
|
-
{
|
|
7070
|
-
title: "Checking authentication",
|
|
7071
|
-
task: async (ctx, task) => {
|
|
7072
|
-
const authStatus = await ctx.localizer.checkAuth();
|
|
7073
|
-
if (!authStatus.authenticated) {
|
|
7074
|
-
throw new Error(
|
|
7075
|
-
`Failed to authenticate with ${chalk11.hex(colors.yellow)(ctx.localizer.id)} provider. Please check your API key and try again.`
|
|
7076
|
-
);
|
|
7077
|
-
}
|
|
7078
|
-
task.title = `Authenticated as ${chalk11.hex(colors.yellow)(authStatus.username)}`;
|
|
7079
|
-
}
|
|
7080
|
-
},
|
|
7081
|
-
{
|
|
7082
|
-
title: "Initializing localization provider",
|
|
7083
|
-
async task(ctx, task) {
|
|
7084
|
-
const isLingoDotDev = ctx.localizer.id === "Lingo.dev";
|
|
7085
|
-
const subTasks = isLingoDotDev ? [
|
|
7086
|
-
"Brand voice enabled",
|
|
7087
|
-
"Translation memory connected",
|
|
7088
|
-
"Glossary enabled",
|
|
7089
|
-
"Quality assurance enabled"
|
|
7090
|
-
].map((title) => ({ title, task: () => {
|
|
7091
|
-
} })) : [
|
|
7092
|
-
"Skipping brand voice",
|
|
7093
|
-
"Skipping glossary",
|
|
7094
|
-
"Skipping translation memory",
|
|
7095
|
-
"Skipping quality assurance"
|
|
7096
|
-
].map((title) => ({ title, task: () => {
|
|
7097
|
-
}, skip: true }));
|
|
7098
|
-
return task.newListr(subTasks, {
|
|
7099
|
-
concurrent: true,
|
|
7100
|
-
rendererOptions: { collapseSubtasks: false }
|
|
7101
|
-
});
|
|
7102
|
-
}
|
|
7103
|
-
}
|
|
7104
|
-
],
|
|
7105
|
-
{
|
|
7106
|
-
rendererOptions: commonTaskRendererOptions
|
|
7107
|
-
}
|
|
7108
|
-
).run(input2);
|
|
7109
|
-
}
|
|
7110
|
-
|
|
7111
|
-
// src/cli/cmd/run/plan.ts
|
|
7112
|
-
import chalk12 from "chalk";
|
|
7113
|
-
import { Listr as Listr2 } from "listr2";
|
|
7114
|
-
import { resolveOverriddenLocale as resolveOverriddenLocale7 } from "@lingo.dev/_spec";
|
|
7115
|
-
async function plan(input2) {
|
|
7116
|
-
console.log(chalk12.hex(colors.orange)("[Planning]"));
|
|
7117
|
-
let buckets = getBuckets(input2.config);
|
|
7118
|
-
if (input2.flags.bucket) {
|
|
7119
|
-
buckets = buckets.filter((b) => input2.flags.bucket.includes(b.type));
|
|
7120
|
-
}
|
|
7121
|
-
let locales = input2.config.locale.targets;
|
|
7122
|
-
if (input2.flags.locale) {
|
|
7123
|
-
locales = locales.filter((l) => input2.flags.locale.includes(l));
|
|
7124
|
-
}
|
|
7125
|
-
return new Listr2(
|
|
7126
|
-
[
|
|
7127
|
-
{
|
|
7128
|
-
title: "Locating content buckets",
|
|
7129
|
-
task: async (ctx, task) => {
|
|
7130
|
-
const bucketCount = buckets.length;
|
|
7131
|
-
const bucketFilter = input2.flags.bucket ? ` ${chalk12.dim(`(filtered by: ${chalk12.hex(colors.yellow)(input2.flags.bucket.join(", "))})`)}` : "";
|
|
7132
|
-
task.title = `Found ${chalk12.hex(colors.yellow)(bucketCount.toString())} bucket(s)${bucketFilter}`;
|
|
7133
|
-
}
|
|
7134
|
-
},
|
|
7135
|
-
{
|
|
7136
|
-
title: "Detecting locales",
|
|
7137
|
-
task: async (ctx, task) => {
|
|
7138
|
-
if (!locales.length) {
|
|
7139
|
-
throw new Error(
|
|
7140
|
-
`No target locales found in config. Please add locales to your i18n.json config file.`
|
|
7141
|
-
);
|
|
6975
|
+
const fileStats = {};
|
|
6976
|
+
for (const bucket of buckets) {
|
|
6977
|
+
try {
|
|
6978
|
+
console.log();
|
|
6979
|
+
ora.info(`Analyzing bucket: ${bucket.type}`);
|
|
6980
|
+
for (const bucketPath of bucket.paths) {
|
|
6981
|
+
const bucketOra = Ora8({ indent: 2 }).info(`Analyzing path: ${bucketPath.pathPattern}`);
|
|
6982
|
+
const sourceLocale = resolveOverriddenLocale7(i18nConfig.locale.source, bucketPath.delimiter);
|
|
6983
|
+
const bucketLoader = createBucketLoader(
|
|
6984
|
+
bucket.type,
|
|
6985
|
+
bucketPath.pathPattern,
|
|
6986
|
+
{
|
|
6987
|
+
isCacheRestore: false,
|
|
6988
|
+
defaultLocale: sourceLocale,
|
|
6989
|
+
injectLocale: bucket.injectLocale
|
|
6990
|
+
},
|
|
6991
|
+
bucket.lockedKeys
|
|
6992
|
+
);
|
|
6993
|
+
bucketLoader.setDefaultLocale(sourceLocale);
|
|
6994
|
+
await bucketLoader.init();
|
|
6995
|
+
const filePath = bucketPath.pathPattern;
|
|
6996
|
+
if (!fileStats[filePath]) {
|
|
6997
|
+
fileStats[filePath] = {
|
|
6998
|
+
path: filePath,
|
|
6999
|
+
sourceKeys: 0,
|
|
7000
|
+
wordCount: 0,
|
|
7001
|
+
languageStats: {}
|
|
7002
|
+
};
|
|
7003
|
+
for (const locale of targetLocales) {
|
|
7004
|
+
fileStats[filePath].languageStats[locale] = {
|
|
7005
|
+
complete: 0,
|
|
7006
|
+
missing: 0,
|
|
7007
|
+
updated: 0,
|
|
7008
|
+
words: 0
|
|
7009
|
+
};
|
|
7010
|
+
}
|
|
7142
7011
|
}
|
|
7143
|
-
const
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
if (input2.flags.file) {
|
|
7154
|
-
if (!input2.flags.file.some(
|
|
7155
|
-
(f) => bucketPath.pathPattern.includes(f)
|
|
7156
|
-
)) {
|
|
7157
|
-
continue;
|
|
7158
|
-
}
|
|
7159
|
-
}
|
|
7160
|
-
patterns.push(bucketPath.pathPattern);
|
|
7012
|
+
const sourceData = await bucketLoader.pull(sourceLocale);
|
|
7013
|
+
const sourceKeys = Object.keys(sourceData);
|
|
7014
|
+
fileStats[filePath].sourceKeys = sourceKeys.length;
|
|
7015
|
+
totalSourceKeyCount += sourceKeys.length;
|
|
7016
|
+
let sourceWordCount = 0;
|
|
7017
|
+
for (const key of sourceKeys) {
|
|
7018
|
+
const value = sourceData[key];
|
|
7019
|
+
if (typeof value === "string") {
|
|
7020
|
+
const words = value.trim().split(/\s+/).length;
|
|
7021
|
+
sourceWordCount += words;
|
|
7161
7022
|
}
|
|
7162
7023
|
}
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7024
|
+
fileStats[filePath].wordCount = sourceWordCount;
|
|
7025
|
+
for (const _targetLocale of targetLocales) {
|
|
7026
|
+
const targetLocale = resolveOverriddenLocale7(_targetLocale, bucketPath.delimiter);
|
|
7027
|
+
bucketOra.start(`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`);
|
|
7028
|
+
let targetData = {};
|
|
7029
|
+
let fileExists = true;
|
|
7030
|
+
try {
|
|
7031
|
+
targetData = await bucketLoader.pull(targetLocale);
|
|
7032
|
+
} catch (error) {
|
|
7033
|
+
fileExists = false;
|
|
7034
|
+
bucketOra.info(
|
|
7035
|
+
`[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`
|
|
7036
|
+
);
|
|
7037
|
+
}
|
|
7038
|
+
if (!fileExists) {
|
|
7039
|
+
fileStats[filePath].languageStats[targetLocale].missing = sourceKeys.length;
|
|
7040
|
+
fileStats[filePath].languageStats[targetLocale].words = sourceWordCount;
|
|
7041
|
+
languageStats[targetLocale].missing += sourceKeys.length;
|
|
7042
|
+
languageStats[targetLocale].words += sourceWordCount;
|
|
7043
|
+
totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + sourceWordCount);
|
|
7044
|
+
bucketOra.succeed(
|
|
7045
|
+
`[${sourceLocale} -> ${targetLocale}] ${chalk14.red(`0% complete`)} (0/${sourceKeys.length} keys) - file not found`
|
|
7046
|
+
);
|
|
7047
|
+
continue;
|
|
7048
|
+
}
|
|
7049
|
+
const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
|
|
7050
|
+
const checksums = await deltaProcessor.loadChecksums();
|
|
7051
|
+
const delta = await deltaProcessor.calculateDelta({
|
|
7052
|
+
sourceData,
|
|
7053
|
+
targetData,
|
|
7054
|
+
checksums
|
|
7055
|
+
});
|
|
7056
|
+
const missingKeys = delta.added;
|
|
7057
|
+
const updatedKeys = delta.updated;
|
|
7058
|
+
const completeKeys = sourceKeys.filter((key) => !missingKeys.includes(key) && !updatedKeys.includes(key));
|
|
7059
|
+
let wordsToTranslate = 0;
|
|
7060
|
+
const keysToProcess = flags.force ? sourceKeys : [...missingKeys, ...updatedKeys];
|
|
7061
|
+
for (const key of keysToProcess) {
|
|
7062
|
+
const value = sourceData[String(key)];
|
|
7063
|
+
if (typeof value === "string") {
|
|
7064
|
+
const words = value.trim().split(/\s+/).length;
|
|
7065
|
+
wordsToTranslate += words;
|
|
7178
7066
|
}
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7067
|
+
}
|
|
7068
|
+
fileStats[filePath].languageStats[targetLocale].missing = missingKeys.length;
|
|
7069
|
+
fileStats[filePath].languageStats[targetLocale].updated = updatedKeys.length;
|
|
7070
|
+
fileStats[filePath].languageStats[targetLocale].complete = completeKeys.length;
|
|
7071
|
+
fileStats[filePath].languageStats[targetLocale].words = wordsToTranslate;
|
|
7072
|
+
languageStats[targetLocale].missing += missingKeys.length;
|
|
7073
|
+
languageStats[targetLocale].updated += updatedKeys.length;
|
|
7074
|
+
languageStats[targetLocale].complete += completeKeys.length;
|
|
7075
|
+
languageStats[targetLocale].words += wordsToTranslate;
|
|
7076
|
+
totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + wordsToTranslate);
|
|
7077
|
+
const totalKeysInFile = sourceKeys.length;
|
|
7078
|
+
const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
|
|
7079
|
+
if (missingKeys.length === 0 && updatedKeys.length === 0) {
|
|
7080
|
+
bucketOra.succeed(
|
|
7081
|
+
`[${sourceLocale} -> ${targetLocale}] ${chalk14.green(`100% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`
|
|
7182
7082
|
);
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
)
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
lockedPatterns: bucket.lockedPatterns || []
|
|
7197
|
-
});
|
|
7083
|
+
} else {
|
|
7084
|
+
const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? chalk14.yellow(`${completionPercent}% complete`) : chalk14.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
|
|
7085
|
+
bucketOra.succeed(message);
|
|
7086
|
+
if (flags.verbose) {
|
|
7087
|
+
if (missingKeys.length > 0) {
|
|
7088
|
+
console.log(` ${chalk14.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`);
|
|
7089
|
+
console.log(
|
|
7090
|
+
` ${chalk14.dim(`Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`)}`
|
|
7091
|
+
);
|
|
7092
|
+
}
|
|
7093
|
+
if (updatedKeys.length > 0) {
|
|
7094
|
+
console.log(` ${chalk14.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`);
|
|
7095
|
+
}
|
|
7198
7096
|
}
|
|
7199
7097
|
}
|
|
7200
7098
|
}
|
|
7201
|
-
task.title = `Prepared ${chalk12.hex(colors.green)(ctx.tasks.length.toString())} translation task(s)`;
|
|
7202
7099
|
}
|
|
7100
|
+
} catch (error) {
|
|
7101
|
+
ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
|
|
7203
7102
|
}
|
|
7204
|
-
],
|
|
7205
|
-
{
|
|
7206
|
-
rendererOptions: commonTaskRendererOptions
|
|
7207
7103
|
}
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
{
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7104
|
+
const totalKeysNeedingTranslation = Object.values(languageStats).reduce((sum, stats) => {
|
|
7105
|
+
return sum + stats.missing + stats.updated;
|
|
7106
|
+
}, 0);
|
|
7107
|
+
const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
|
|
7108
|
+
console.log();
|
|
7109
|
+
ora.succeed(chalk14.green(`Localization status completed.`));
|
|
7110
|
+
console.log(chalk14.bold.cyan(`
|
|
7111
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
|
|
7112
|
+
console.log(chalk14.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
|
|
7113
|
+
console.log(chalk14.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
|
|
7114
|
+
console.log(chalk14.bold(`
|
|
7115
|
+
\u{1F4DD} SOURCE CONTENT:`));
|
|
7116
|
+
console.log(`\u2022 Source language: ${chalk14.green(i18nConfig.locale.source)}`);
|
|
7117
|
+
console.log(`\u2022 Source keys: ${chalk14.yellow(totalSourceKeyCount.toString())} keys across all files`);
|
|
7118
|
+
console.log(chalk14.bold(`
|
|
7119
|
+
\u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
|
|
7120
|
+
const table = new Table({
|
|
7121
|
+
head: ["Language", "Status", "Complete", "Missing", "Updated", "Total Keys", "Words to Translate"],
|
|
7122
|
+
style: {
|
|
7123
|
+
head: ["white"],
|
|
7124
|
+
// White color for headers
|
|
7125
|
+
border: []
|
|
7126
|
+
// No color for borders
|
|
7231
7127
|
},
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7128
|
+
colWidths: [12, 20, 18, 12, 12, 12, 15]
|
|
7129
|
+
// Explicit column widths, making Status column wider
|
|
7130
|
+
});
|
|
7131
|
+
let totalWordsToTranslate = 0;
|
|
7132
|
+
for (const locale of targetLocales) {
|
|
7133
|
+
const stats = languageStats[locale];
|
|
7134
|
+
const percentComplete = (stats.complete / totalSourceKeyCount * 100).toFixed(1);
|
|
7135
|
+
const totalNeeded = stats.missing + stats.updated;
|
|
7136
|
+
let statusText;
|
|
7137
|
+
let statusColor;
|
|
7138
|
+
if (stats.missing === totalSourceKeyCount) {
|
|
7139
|
+
statusText = "\u{1F534} Not started";
|
|
7140
|
+
statusColor = chalk14.red;
|
|
7141
|
+
} else if (stats.missing === 0 && stats.updated === 0) {
|
|
7142
|
+
statusText = "\u2705 Complete";
|
|
7143
|
+
statusColor = chalk14.green;
|
|
7144
|
+
} else if (parseFloat(percentComplete) > 80) {
|
|
7145
|
+
statusText = "\u{1F7E1} Almost done";
|
|
7146
|
+
statusColor = chalk14.yellow;
|
|
7147
|
+
} else if (parseFloat(percentComplete) > 0) {
|
|
7148
|
+
statusText = "\u{1F7E0} In progress";
|
|
7149
|
+
statusColor = chalk14.yellow;
|
|
7150
|
+
} else {
|
|
7151
|
+
statusText = "\u{1F534} Not started";
|
|
7152
|
+
statusColor = chalk14.red;
|
|
7153
|
+
}
|
|
7154
|
+
const words = totalWordCount.get(locale) || 0;
|
|
7155
|
+
totalWordsToTranslate += words;
|
|
7156
|
+
table.push([
|
|
7157
|
+
locale,
|
|
7158
|
+
statusColor(statusText),
|
|
7159
|
+
`${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
|
|
7160
|
+
stats.missing > 0 ? chalk14.red(stats.missing.toString()) : "0",
|
|
7161
|
+
stats.updated > 0 ? chalk14.yellow(stats.updated.toString()) : "0",
|
|
7162
|
+
totalNeeded > 0 ? chalk14.magenta(totalNeeded.toString()) : "0",
|
|
7163
|
+
words > 0 ? `~${words.toLocaleString()}` : "0"
|
|
7164
|
+
]);
|
|
7165
|
+
}
|
|
7166
|
+
console.log(table.toString());
|
|
7167
|
+
console.log(chalk14.bold(`
|
|
7168
|
+
\u{1F4CA} USAGE ESTIMATE:`));
|
|
7169
|
+
console.log(
|
|
7170
|
+
`\u2022 WORDS TO BE CONSUMED: ~${chalk14.yellow.bold(totalWordsToTranslate.toLocaleString())} words across all languages`
|
|
7171
|
+
);
|
|
7172
|
+
console.log(` (Words are counted from source language for keys that need translation in target languages)`);
|
|
7173
|
+
if (targetLocales.length > 1) {
|
|
7174
|
+
console.log(`\u2022 Per-language breakdown:`);
|
|
7175
|
+
for (const locale of targetLocales) {
|
|
7176
|
+
const words = totalWordCount.get(locale) || 0;
|
|
7177
|
+
const percent = (words / totalWordsToTranslate * 100).toFixed(1);
|
|
7178
|
+
console.log(` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`);
|
|
7179
|
+
}
|
|
7180
|
+
}
|
|
7181
|
+
if (flags.confirm && Object.keys(fileStats).length > 0) {
|
|
7182
|
+
console.log(chalk14.bold(`
|
|
7183
|
+
\u{1F4D1} BREAKDOWN BY FILE:`));
|
|
7184
|
+
Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path16, stats]) => {
|
|
7185
|
+
if (stats.sourceKeys === 0) return;
|
|
7186
|
+
console.log(chalk14.bold(`
|
|
7187
|
+
\u2022 ${path16}:`));
|
|
7188
|
+
console.log(` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`);
|
|
7189
|
+
const fileTable = new Table({
|
|
7190
|
+
head: ["Language", "Status", "Details"],
|
|
7191
|
+
style: {
|
|
7192
|
+
head: ["white"],
|
|
7193
|
+
border: []
|
|
7194
|
+
},
|
|
7195
|
+
colWidths: [12, 20, 50]
|
|
7196
|
+
// Explicit column widths for file detail table
|
|
7197
|
+
});
|
|
7198
|
+
for (const locale of targetLocales) {
|
|
7199
|
+
const langStats = stats.languageStats[locale];
|
|
7200
|
+
const complete = langStats.complete;
|
|
7201
|
+
const total = stats.sourceKeys;
|
|
7202
|
+
const completion = (complete / total * 100).toFixed(1);
|
|
7203
|
+
let status = "\u2705 Complete";
|
|
7204
|
+
let statusColor = chalk14.green;
|
|
7205
|
+
if (langStats.missing === total) {
|
|
7206
|
+
status = "\u274C Not started";
|
|
7207
|
+
statusColor = chalk14.red;
|
|
7208
|
+
} else if (langStats.missing > 0 || langStats.updated > 0) {
|
|
7209
|
+
status = `\u26A0\uFE0F ${completion}% complete`;
|
|
7210
|
+
statusColor = chalk14.yellow;
|
|
7239
7211
|
}
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
workerTasks.push(
|
|
7249
|
-
createWorkerTask({
|
|
7250
|
-
ctx,
|
|
7251
|
-
assignedTasks,
|
|
7252
|
-
ioLimiter,
|
|
7253
|
-
i18nLimiter,
|
|
7254
|
-
onDone() {
|
|
7255
|
-
task.title = createExecutionProgressMessage(ctx);
|
|
7256
|
-
}
|
|
7257
|
-
})
|
|
7258
|
-
);
|
|
7212
|
+
let details = "";
|
|
7213
|
+
if (langStats.missing > 0 || langStats.updated > 0) {
|
|
7214
|
+
const parts = [];
|
|
7215
|
+
if (langStats.missing > 0) parts.push(`${langStats.missing} missing`);
|
|
7216
|
+
if (langStats.updated > 0) parts.push(`${langStats.updated} changed`);
|
|
7217
|
+
details = `${parts.join(", ")}, ~${langStats.words} words`;
|
|
7218
|
+
} else {
|
|
7219
|
+
details = "All keys translated";
|
|
7259
7220
|
}
|
|
7260
|
-
|
|
7261
|
-
concurrent: true,
|
|
7262
|
-
exitOnError: false,
|
|
7263
|
-
rendererOptions: {
|
|
7264
|
-
...commonTaskRendererOptions,
|
|
7265
|
-
collapseSubtasks: true
|
|
7266
|
-
}
|
|
7267
|
-
});
|
|
7221
|
+
fileTable.push([locale, statusColor(status), details]);
|
|
7268
7222
|
}
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
{
|
|
7272
|
-
exitOnError: false,
|
|
7273
|
-
rendererOptions: commonTaskRendererOptions
|
|
7223
|
+
console.log(fileTable.toString());
|
|
7224
|
+
});
|
|
7274
7225
|
}
|
|
7275
|
-
|
|
7226
|
+
const completeLanguages = targetLocales.filter(
|
|
7227
|
+
(locale) => languageStats[locale].missing === 0 && languageStats[locale].updated === 0
|
|
7228
|
+
);
|
|
7229
|
+
const missingLanguages = targetLocales.filter((locale) => languageStats[locale].complete === 0);
|
|
7230
|
+
console.log(chalk14.bold.green(`
|
|
7231
|
+
\u{1F4A1} OPTIMIZATION TIPS:`));
|
|
7232
|
+
if (missingLanguages.length > 0) {
|
|
7233
|
+
console.log(
|
|
7234
|
+
`\u2022 ${chalk14.yellow(missingLanguages.join(", "))} ${missingLanguages.length === 1 ? "has" : "have"} no translations yet`
|
|
7235
|
+
);
|
|
7236
|
+
}
|
|
7237
|
+
if (completeLanguages.length > 0) {
|
|
7238
|
+
console.log(
|
|
7239
|
+
`\u2022 ${chalk14.green(completeLanguages.join(", "))} ${completeLanguages.length === 1 ? "is" : "are"} completely translated`
|
|
7240
|
+
);
|
|
7241
|
+
}
|
|
7242
|
+
if (targetLocales.length > 1) {
|
|
7243
|
+
console.log(`\u2022 Translating one language at a time reduces complexity`);
|
|
7244
|
+
console.log(`\u2022 Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`);
|
|
7245
|
+
}
|
|
7246
|
+
trackEvent(authId || "status", "cmd.status.success", {
|
|
7247
|
+
i18nConfig,
|
|
7248
|
+
flags,
|
|
7249
|
+
totalSourceKeyCount,
|
|
7250
|
+
languageStats,
|
|
7251
|
+
totalWordsToTranslate,
|
|
7252
|
+
authenticated: !!authId
|
|
7253
|
+
});
|
|
7254
|
+
} catch (error) {
|
|
7255
|
+
ora.fail(error.message);
|
|
7256
|
+
trackEvent(authId || "status", "cmd.status.error", {
|
|
7257
|
+
flags,
|
|
7258
|
+
error: error.message,
|
|
7259
|
+
authenticated: !!authId
|
|
7260
|
+
});
|
|
7261
|
+
process.exit(1);
|
|
7262
|
+
}
|
|
7263
|
+
});
|
|
7264
|
+
function parseFlags2(options) {
|
|
7265
|
+
return Z11.object({
|
|
7266
|
+
locale: Z11.array(localeCodeSchema3).optional(),
|
|
7267
|
+
bucket: Z11.array(bucketTypeSchema4).optional(),
|
|
7268
|
+
force: Z11.boolean().optional(),
|
|
7269
|
+
confirm: Z11.boolean().optional(),
|
|
7270
|
+
verbose: Z11.boolean().optional(),
|
|
7271
|
+
file: Z11.array(Z11.string()).optional(),
|
|
7272
|
+
apiKey: Z11.string().optional()
|
|
7273
|
+
}).parse(options);
|
|
7276
7274
|
}
|
|
7277
|
-
function
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7275
|
+
async function tryAuthenticate(settings) {
|
|
7276
|
+
if (!settings.auth.apiKey) {
|
|
7277
|
+
return null;
|
|
7278
|
+
}
|
|
7279
|
+
try {
|
|
7280
|
+
const authenticator = createAuthenticator({
|
|
7281
|
+
apiKey: settings.auth.apiKey,
|
|
7282
|
+
apiUrl: settings.auth.apiUrl
|
|
7283
|
+
});
|
|
7284
|
+
const user = await authenticator.whoami();
|
|
7285
|
+
return user;
|
|
7286
|
+
} catch (error) {
|
|
7287
|
+
return null;
|
|
7288
|
+
}
|
|
7287
7289
|
}
|
|
7288
|
-
function
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7290
|
+
function validateParams2(i18nConfig, flags) {
|
|
7291
|
+
if (!i18nConfig) {
|
|
7292
|
+
throw new CLIError({
|
|
7293
|
+
message: "i18n.json not found. Please run `lingo.dev init` to initialize the project.",
|
|
7294
|
+
docUrl: "i18nNotFound"
|
|
7295
|
+
});
|
|
7296
|
+
} else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {
|
|
7297
|
+
throw new CLIError({
|
|
7298
|
+
message: "No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
|
|
7299
|
+
docUrl: "bucketNotFound"
|
|
7300
|
+
});
|
|
7301
|
+
} else if (flags.locale?.some((locale) => !i18nConfig.locale.targets.includes(locale))) {
|
|
7302
|
+
throw new CLIError({
|
|
7303
|
+
message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
|
|
7304
|
+
docUrl: "localeTargetNotFound"
|
|
7305
|
+
});
|
|
7306
|
+
} else if (flags.bucket?.some((bucket) => !i18nConfig.buckets[bucket])) {
|
|
7307
|
+
throw new CLIError({
|
|
7308
|
+
message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
|
|
7309
|
+
docUrl: "bucketNotFound"
|
|
7310
|
+
});
|
|
7311
|
+
}
|
|
7302
7312
|
}
|
|
7303
|
-
function createLoaderForTask(assignedTask) {
|
|
7304
|
-
const bucketLoader = createBucketLoader(
|
|
7305
|
-
assignedTask.bucketType,
|
|
7306
|
-
assignedTask.bucketPathPattern,
|
|
7307
|
-
{
|
|
7308
|
-
defaultLocale: assignedTask.sourceLocale,
|
|
7309
|
-
isCacheRestore: false,
|
|
7310
|
-
injectLocale: assignedTask.injectLocale
|
|
7311
|
-
},
|
|
7312
|
-
assignedTask.lockedKeys,
|
|
7313
|
-
assignedTask.lockedPatterns
|
|
7314
|
-
);
|
|
7315
|
-
bucketLoader.setDefaultLocale(assignedTask.sourceLocale);
|
|
7316
|
-
return bucketLoader;
|
|
7317
|
-
}
|
|
7318
|
-
function createWorkerTask(args) {
|
|
7319
|
-
return {
|
|
7320
|
-
title: "Initializing...",
|
|
7321
|
-
task: async (_subCtx, subTask) => {
|
|
7322
|
-
for (const assignedTask of args.assignedTasks) {
|
|
7323
|
-
subTask.title = createWorkerStatusMessage({
|
|
7324
|
-
assignedTask,
|
|
7325
|
-
percentage: 0
|
|
7326
|
-
});
|
|
7327
|
-
const bucketLoader = createLoaderForTask(assignedTask);
|
|
7328
|
-
const deltaProcessor = createDeltaProcessor(
|
|
7329
|
-
assignedTask.bucketPathPattern
|
|
7330
|
-
);
|
|
7331
|
-
const taskResult = await args.i18nLimiter(async () => {
|
|
7332
|
-
try {
|
|
7333
|
-
const sourceData = await bucketLoader.pull(
|
|
7334
|
-
assignedTask.sourceLocale
|
|
7335
|
-
);
|
|
7336
|
-
const targetData = await bucketLoader.pull(
|
|
7337
|
-
assignedTask.targetLocale
|
|
7338
|
-
);
|
|
7339
|
-
const checksums = await deltaProcessor.loadChecksums();
|
|
7340
|
-
const delta = await deltaProcessor.calculateDelta({
|
|
7341
|
-
sourceData,
|
|
7342
|
-
targetData,
|
|
7343
|
-
checksums
|
|
7344
|
-
});
|
|
7345
|
-
const processableData = _32.chain(sourceData).entries().filter(
|
|
7346
|
-
([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!args.ctx.flags.force
|
|
7347
|
-
).fromPairs().value();
|
|
7348
|
-
if (!Object.keys(processableData).length) {
|
|
7349
|
-
return { status: "skipped" };
|
|
7350
|
-
}
|
|
7351
|
-
const processedTargetData = await args.ctx.localizer.localize(
|
|
7352
|
-
{
|
|
7353
|
-
sourceLocale: assignedTask.sourceLocale,
|
|
7354
|
-
targetLocale: assignedTask.targetLocale,
|
|
7355
|
-
sourceData,
|
|
7356
|
-
targetData,
|
|
7357
|
-
processableData
|
|
7358
|
-
},
|
|
7359
|
-
(progress) => {
|
|
7360
|
-
subTask.title = createWorkerStatusMessage({
|
|
7361
|
-
assignedTask,
|
|
7362
|
-
percentage: progress
|
|
7363
|
-
});
|
|
7364
|
-
}
|
|
7365
|
-
);
|
|
7366
|
-
let finalTargetData = _32.merge(
|
|
7367
|
-
{},
|
|
7368
|
-
sourceData,
|
|
7369
|
-
targetData,
|
|
7370
|
-
processedTargetData
|
|
7371
|
-
);
|
|
7372
|
-
finalTargetData = _32.chain(finalTargetData).entries().map(([key, value]) => {
|
|
7373
|
-
const renaming = delta.renamed.find(
|
|
7374
|
-
([oldKey]) => oldKey === key
|
|
7375
|
-
);
|
|
7376
|
-
if (!renaming) {
|
|
7377
|
-
return [key, value];
|
|
7378
|
-
}
|
|
7379
|
-
return [renaming[1], value];
|
|
7380
|
-
}).fromPairs().value();
|
|
7381
|
-
await args.ioLimiter(async () => {
|
|
7382
|
-
await bucketLoader.pull(assignedTask.sourceLocale);
|
|
7383
|
-
await bucketLoader.push(
|
|
7384
|
-
assignedTask.targetLocale,
|
|
7385
|
-
finalTargetData
|
|
7386
|
-
);
|
|
7387
|
-
const checksums2 = await deltaProcessor.createChecksums(sourceData);
|
|
7388
|
-
await deltaProcessor.saveChecksums(checksums2);
|
|
7389
|
-
});
|
|
7390
|
-
return { status: "success" };
|
|
7391
|
-
} catch (error) {
|
|
7392
|
-
return {
|
|
7393
|
-
status: "error",
|
|
7394
|
-
error
|
|
7395
|
-
};
|
|
7396
|
-
}
|
|
7397
|
-
});
|
|
7398
|
-
args.ctx.results.set(assignedTask, taskResult);
|
|
7399
|
-
}
|
|
7400
|
-
subTask.title = "Done";
|
|
7401
|
-
}
|
|
7402
|
-
};
|
|
7403
|
-
}
|
|
7404
|
-
function countTasks(ctx, predicate) {
|
|
7405
|
-
return Array.from(ctx.results.entries()).filter(
|
|
7406
|
-
([task, result]) => predicate(task, result)
|
|
7407
|
-
).length;
|
|
7408
|
-
}
|
|
7409
|
-
|
|
7410
|
-
// src/cli/cmd/run/_types.ts
|
|
7411
|
-
import {
|
|
7412
|
-
bucketTypeSchema as bucketTypeSchema4,
|
|
7413
|
-
localeCodeSchema as localeCodeSchema3
|
|
7414
|
-
} from "@lingo.dev/_spec";
|
|
7415
|
-
import { z as z2 } from "zod";
|
|
7416
|
-
var flagsSchema2 = z2.object({
|
|
7417
|
-
locale: z2.array(localeCodeSchema3).optional(),
|
|
7418
|
-
bucket: z2.array(bucketTypeSchema4).optional(),
|
|
7419
|
-
key: z2.array(z2.string()).optional(),
|
|
7420
|
-
file: z2.array(z2.string()).optional(),
|
|
7421
|
-
apiKey: z2.string().optional(),
|
|
7422
|
-
force: z2.boolean().optional(),
|
|
7423
|
-
frozen: z2.boolean().optional(),
|
|
7424
|
-
verbose: z2.boolean().optional(),
|
|
7425
|
-
strict: z2.boolean().optional(),
|
|
7426
|
-
interactive: z2.boolean().default(false),
|
|
7427
|
-
concurrency: z2.number().positive().default(10),
|
|
7428
|
-
debug: z2.boolean().default(false)
|
|
7429
|
-
});
|
|
7430
7313
|
|
|
7431
|
-
// src/cli/cmd/
|
|
7432
|
-
import
|
|
7314
|
+
// src/cli/cmd/may-the-fourth.ts
|
|
7315
|
+
import { Command as Command17 } from "interactive-commander";
|
|
7316
|
+
import * as cp from "node:child_process";
|
|
7433
7317
|
import figlet2 from "figlet";
|
|
7318
|
+
import chalk15 from "chalk";
|
|
7434
7319
|
import { vice as vice2 } from "gradient-string";
|
|
7435
|
-
|
|
7320
|
+
var colors2 = {
|
|
7321
|
+
orange: "#ff6600",
|
|
7322
|
+
green: "#6ae300",
|
|
7323
|
+
blue: "#0090ff",
|
|
7324
|
+
yellow: "#ffcc00",
|
|
7325
|
+
grey: "#808080",
|
|
7326
|
+
red: "#ff0000"
|
|
7327
|
+
};
|
|
7328
|
+
var may_the_fourth_default = new Command17().command("may-the-fourth").description("May the Fourth be with you").helpOption("-h, --help", "Show help").action(async () => {
|
|
7329
|
+
await renderClear2();
|
|
7330
|
+
await renderBanner2();
|
|
7331
|
+
await renderSpacer2();
|
|
7332
|
+
console.log(chalk15.hex(colors2.yellow)("Loading the Star Wars movie..."));
|
|
7333
|
+
await renderSpacer2();
|
|
7334
|
+
await new Promise((resolve, reject) => {
|
|
7335
|
+
const ssh = cp.spawn("ssh", ["starwarstel.net"], {
|
|
7336
|
+
stdio: "inherit"
|
|
7337
|
+
});
|
|
7338
|
+
ssh.on("close", (code) => {
|
|
7339
|
+
if (code !== 0) {
|
|
7340
|
+
console.error(`SSH process exited with code ${code}`);
|
|
7341
|
+
}
|
|
7342
|
+
resolve();
|
|
7343
|
+
});
|
|
7344
|
+
ssh.on("error", (err) => {
|
|
7345
|
+
console.error("Failed to start SSH process:", err);
|
|
7346
|
+
reject(err);
|
|
7347
|
+
});
|
|
7348
|
+
});
|
|
7349
|
+
await renderSpacer2();
|
|
7350
|
+
console.log(
|
|
7351
|
+
`${chalk15.hex(colors2.green)("We hope you enjoyed it! :)")} ${chalk15.hex(colors2.blue)("May the Fourth be with you! \u{1F680}")}`
|
|
7352
|
+
);
|
|
7353
|
+
await renderSpacer2();
|
|
7354
|
+
console.log(chalk15.dim(`---`));
|
|
7355
|
+
await renderSpacer2();
|
|
7356
|
+
await renderHero2();
|
|
7357
|
+
});
|
|
7436
7358
|
async function renderClear2() {
|
|
7437
7359
|
console.log("\x1Bc");
|
|
7438
7360
|
}
|
|
@@ -7452,114 +7374,242 @@ async function renderBanner2() {
|
|
|
7452
7374
|
}
|
|
7453
7375
|
async function renderHero2() {
|
|
7454
7376
|
console.log(
|
|
7455
|
-
`\u26A1\uFE0F ${
|
|
7456
|
-
);
|
|
7457
|
-
console.log("");
|
|
7458
|
-
const label1 = "\u2B50 GitHub Repo:";
|
|
7459
|
-
const label2 = "\u{1F4DA} Docs:";
|
|
7460
|
-
const label3 = "\u{1F4AC} 24/7 Support:";
|
|
7461
|
-
const maxLabelWidth = 17;
|
|
7462
|
-
console.log(
|
|
7463
|
-
`${chalk14.hex(colors.blue)(label1.padEnd(maxLabelWidth))} ${chalk14.hex(colors.blue)("https://lingo.dev/go/gh")}`
|
|
7464
|
-
);
|
|
7465
|
-
console.log(
|
|
7466
|
-
`${chalk14.hex(colors.blue)(label2.padEnd(maxLabelWidth + 1))} ${chalk14.hex(colors.blue)("https://lingo.dev/go/docs")}`
|
|
7377
|
+
`\u26A1\uFE0F ${chalk15.hex(colors2.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
|
|
7467
7378
|
);
|
|
7379
|
+
console.log(" ");
|
|
7468
7380
|
console.log(
|
|
7469
|
-
|
|
7381
|
+
chalk15.hex(colors2.blue)("\u2B50 GitHub Repo: https://lingo.dev/go/gh")
|
|
7470
7382
|
);
|
|
7471
|
-
}
|
|
7472
|
-
async function pauseIfDebug(debug) {
|
|
7473
|
-
if (debug) {
|
|
7474
|
-
await waitForUserPrompt("Press Enter to continue...");
|
|
7475
|
-
}
|
|
7476
|
-
}
|
|
7477
|
-
async function waitForUserPrompt(message) {
|
|
7478
|
-
const rl = readline2.createInterface({
|
|
7479
|
-
input: process.stdin,
|
|
7480
|
-
output: process.stdout
|
|
7481
|
-
});
|
|
7482
|
-
return new Promise((resolve) => {
|
|
7483
|
-
rl.question(chalk14.dim(`[${message}]
|
|
7484
|
-
`), () => {
|
|
7485
|
-
rl.close();
|
|
7486
|
-
resolve();
|
|
7487
|
-
});
|
|
7488
|
-
});
|
|
7489
|
-
}
|
|
7490
|
-
async function renderSummary(ctx) {
|
|
7491
|
-
console.log(chalk14.hex(colors.green)("[Done]"));
|
|
7492
|
-
const skippedTasksCount = Array.from(ctx.results.values()).filter(
|
|
7493
|
-
(r) => r.status === "skipped"
|
|
7494
|
-
).length;
|
|
7495
|
-
console.log(`\u2022 ${chalk14.hex(colors.yellow)(skippedTasksCount)} from cache`);
|
|
7496
|
-
const succeededTasksCount = Array.from(ctx.results.values()).filter(
|
|
7497
|
-
(r) => r.status === "success"
|
|
7498
|
-
).length;
|
|
7499
|
-
console.log(`\u2022 ${chalk14.hex(colors.yellow)(succeededTasksCount)} processed`);
|
|
7500
|
-
const failedTasksCount = Array.from(ctx.results.values()).filter(
|
|
7501
|
-
(r) => r.status === "error"
|
|
7502
|
-
).length;
|
|
7503
|
-
console.log(`\u2022 ${chalk14.hex(colors.yellow)(failedTasksCount)} failed`);
|
|
7383
|
+
console.log(chalk15.hex(colors2.blue)("\u{1F4AC} 24/7 Support: hi@lingo.dev"));
|
|
7504
7384
|
}
|
|
7505
7385
|
|
|
7506
|
-
//
|
|
7507
|
-
var
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
}
|
|
7386
|
+
// package.json
|
|
7387
|
+
var package_default = {
|
|
7388
|
+
name: "lingo.dev",
|
|
7389
|
+
version: "0.93.7",
|
|
7390
|
+
description: "Lingo.dev CLI",
|
|
7391
|
+
private: false,
|
|
7392
|
+
publishConfig: {
|
|
7393
|
+
access: "public"
|
|
7394
|
+
},
|
|
7395
|
+
type: "module",
|
|
7396
|
+
sideEffects: false,
|
|
7397
|
+
exports: {
|
|
7398
|
+
"./cli": {
|
|
7399
|
+
types: "./build/cli.d.ts",
|
|
7400
|
+
import: "./build/cli.mjs",
|
|
7401
|
+
require: "./build/cli.cjs"
|
|
7402
|
+
},
|
|
7403
|
+
"./sdk": {
|
|
7404
|
+
types: "./build/sdk.d.ts",
|
|
7405
|
+
import: "./build/sdk.mjs",
|
|
7406
|
+
require: "./build/sdk.cjs"
|
|
7407
|
+
},
|
|
7408
|
+
"./spec": {
|
|
7409
|
+
types: "./build/spec.d.ts",
|
|
7410
|
+
import: "./build/spec.mjs",
|
|
7411
|
+
require: "./build/spec.cjs"
|
|
7412
|
+
},
|
|
7413
|
+
"./compiler": {
|
|
7414
|
+
types: "./build/compiler.d.ts",
|
|
7415
|
+
import: "./build/compiler.mjs",
|
|
7416
|
+
require: "./build/compiler.cjs"
|
|
7417
|
+
},
|
|
7418
|
+
"./react": {
|
|
7419
|
+
types: "./build/react.d.ts",
|
|
7420
|
+
import: "./build/react.mjs",
|
|
7421
|
+
require: "./build/react.cjs"
|
|
7422
|
+
},
|
|
7423
|
+
"./react-client": {
|
|
7424
|
+
types: "./build/react/client.d.ts",
|
|
7425
|
+
import: "./build/react/client.mjs",
|
|
7426
|
+
require: "./build/react/client.cjs"
|
|
7427
|
+
},
|
|
7428
|
+
"./react/client": {
|
|
7429
|
+
types: "./build/react/client.d.ts",
|
|
7430
|
+
import: "./build/react/client.mjs",
|
|
7431
|
+
require: "./build/react/client.cjs"
|
|
7432
|
+
},
|
|
7433
|
+
"./react-rsc": {
|
|
7434
|
+
types: "./build/react/rsc.d.ts",
|
|
7435
|
+
import: "./build/react/rsc.mjs",
|
|
7436
|
+
require: "./build/react/rsc.cjs"
|
|
7437
|
+
},
|
|
7438
|
+
"./react/rsc": {
|
|
7439
|
+
types: "./build/react/rsc.d.ts",
|
|
7440
|
+
import: "./build/react/rsc.mjs",
|
|
7441
|
+
require: "./build/react/rsc.cjs"
|
|
7442
|
+
},
|
|
7443
|
+
"./react-router": {
|
|
7444
|
+
types: "./build/react/react-router.d.ts",
|
|
7445
|
+
import: "./build/react/react-router.mjs",
|
|
7446
|
+
require: "./build/react/react-router.cjs"
|
|
7447
|
+
},
|
|
7448
|
+
"./react/react-router": {
|
|
7449
|
+
types: "./build/react/react-router.d.ts",
|
|
7450
|
+
import: "./build/react/react-router.mjs",
|
|
7451
|
+
require: "./build/react/react-router.cjs"
|
|
7452
|
+
}
|
|
7453
|
+
},
|
|
7454
|
+
typesVersions: {
|
|
7455
|
+
"*": {
|
|
7456
|
+
sdk: [
|
|
7457
|
+
"./build/sdk.d.ts"
|
|
7458
|
+
],
|
|
7459
|
+
cli: [
|
|
7460
|
+
"./build/cli.d.ts"
|
|
7461
|
+
],
|
|
7462
|
+
spec: [
|
|
7463
|
+
"./build/spec.d.ts"
|
|
7464
|
+
],
|
|
7465
|
+
compiler: [
|
|
7466
|
+
"./build/compiler.d.ts"
|
|
7467
|
+
],
|
|
7468
|
+
react: [
|
|
7469
|
+
"./build/react.d.ts"
|
|
7470
|
+
],
|
|
7471
|
+
"react/client": [
|
|
7472
|
+
"./build/react/client.d.ts"
|
|
7473
|
+
],
|
|
7474
|
+
"react/rsc": [
|
|
7475
|
+
"./build/react/rsc.d.ts"
|
|
7476
|
+
],
|
|
7477
|
+
"react/react-router": [
|
|
7478
|
+
"./build/react/react-router.d.ts"
|
|
7479
|
+
]
|
|
7480
|
+
}
|
|
7481
|
+
},
|
|
7482
|
+
bin: {
|
|
7483
|
+
"lingo.dev": "./bin/cli.mjs"
|
|
7484
|
+
},
|
|
7485
|
+
files: [
|
|
7486
|
+
"bin",
|
|
7487
|
+
"build"
|
|
7488
|
+
],
|
|
7489
|
+
scripts: {
|
|
7490
|
+
"lingo.dev": "node --inspect=9229 ./bin/cli.mjs",
|
|
7491
|
+
dev: "tsup --watch",
|
|
7492
|
+
build: "tsc --noEmit && tsup",
|
|
7493
|
+
test: "vitest run",
|
|
7494
|
+
"test:watch": "vitest",
|
|
7495
|
+
clean: "rm -rf build"
|
|
7496
|
+
},
|
|
7497
|
+
keywords: [],
|
|
7498
|
+
author: "",
|
|
7499
|
+
license: "Apache-2.0",
|
|
7500
|
+
dependencies: {
|
|
7501
|
+
"@ai-sdk/anthropic": "^1.2.11",
|
|
7502
|
+
"@ai-sdk/openai": "^1.3.22",
|
|
7503
|
+
"@babel/generator": "^7.27.1",
|
|
7504
|
+
"@babel/parser": "^7.27.1",
|
|
7505
|
+
"@babel/traverse": "^7.27.4",
|
|
7506
|
+
"@babel/types": "^7.27.1",
|
|
7507
|
+
"@datocms/cma-client-node": "^4.0.1",
|
|
7508
|
+
"@gitbeaker/rest": "^39.34.3",
|
|
7509
|
+
"@inkjs/ui": "^2.0.0",
|
|
7510
|
+
"@inquirer/prompts": "^7.4.1",
|
|
7511
|
+
"@lingo.dev/_sdk": "workspace:*",
|
|
7512
|
+
"@lingo.dev/_spec": "workspace:*",
|
|
7513
|
+
"@lingo.dev/_react": "workspace:*",
|
|
7514
|
+
"@lingo.dev/_compiler": "workspace:*",
|
|
7515
|
+
"@modelcontextprotocol/sdk": "^1.5.0",
|
|
7516
|
+
"@paralleldrive/cuid2": "^2.2.2",
|
|
7517
|
+
ai: "^4.3.15",
|
|
7518
|
+
bitbucket: "^2.12.0",
|
|
7519
|
+
chalk: "^5.4.1",
|
|
7520
|
+
"cli-progress": "^3.12.0",
|
|
7521
|
+
"cli-table3": "^0.6.5",
|
|
7522
|
+
cors: "^2.8.5",
|
|
7523
|
+
"csv-parse": "^5.6.0",
|
|
7524
|
+
"csv-stringify": "^6.5.2",
|
|
7525
|
+
"date-fns": "^4.1.0",
|
|
7526
|
+
dedent: "^1.5.3",
|
|
7527
|
+
diff: "^7.0.0",
|
|
7528
|
+
dotenv: "^16.4.7",
|
|
7529
|
+
express: "^5.1.0",
|
|
7530
|
+
"external-editor": "^3.1.0",
|
|
7531
|
+
figlet: "^1.8.0",
|
|
7532
|
+
flat: "^6.0.1",
|
|
7533
|
+
"gettext-parser": "^8.0.0",
|
|
7534
|
+
glob: "<11.0.0",
|
|
7535
|
+
"gradient-string": "^3.0.0",
|
|
7536
|
+
"gray-matter": "^4.0.3",
|
|
7537
|
+
ini: "^5.0.0",
|
|
7538
|
+
ink: "^4.2.0",
|
|
7539
|
+
"ink-progress-bar": "^3.0.0",
|
|
7540
|
+
"ink-spinner": "^5.0.0",
|
|
7541
|
+
inquirer: "^12.6.0",
|
|
7542
|
+
"interactive-commander": "^0.5.194",
|
|
7543
|
+
"is-url": "^1.2.4",
|
|
7544
|
+
jsdom: "^25.0.1",
|
|
7545
|
+
json5: "^2.2.3",
|
|
7546
|
+
jsonrepair: "^3.11.2",
|
|
7547
|
+
listr2: "^8.3.2",
|
|
7548
|
+
lodash: "^4.17.21",
|
|
7549
|
+
marked: "^15.0.6",
|
|
7550
|
+
"mdast-util-from-markdown": "^2.0.2",
|
|
7551
|
+
"mdast-util-gfm": "^3.1.0",
|
|
7552
|
+
"micromark-extension-gfm": "^3.0.0",
|
|
7553
|
+
"node-machine-id": "^1.1.12",
|
|
7554
|
+
"node-webvtt": "^1.9.4",
|
|
7555
|
+
"object-hash": "^3.0.0",
|
|
7556
|
+
octokit: "^4.0.2",
|
|
7557
|
+
open: "^10.1.2",
|
|
7558
|
+
ora: "^8.1.1",
|
|
7559
|
+
"p-limit": "^6.2.0",
|
|
7560
|
+
"php-array-reader": "^2.1.2",
|
|
7561
|
+
plist: "^3.1.0",
|
|
7562
|
+
"posthog-node": "^4.17.0",
|
|
7563
|
+
prettier: "^3.4.2",
|
|
7564
|
+
react: "^18.3.1",
|
|
7565
|
+
"rehype-stringify": "^10.0.1",
|
|
7566
|
+
"remark-disable-tokenizers": "^1.1.1",
|
|
7567
|
+
"remark-frontmatter": "^5.0.0",
|
|
7568
|
+
"remark-gfm": "^4.0.1",
|
|
7569
|
+
"remark-mdx": "^3.1.0",
|
|
7570
|
+
"remark-mdx-frontmatter": "^5.1.0",
|
|
7571
|
+
"remark-parse": "^11.0.0",
|
|
7572
|
+
"remark-rehype": "^11.1.2",
|
|
7573
|
+
"remark-stringify": "^11.0.0",
|
|
7574
|
+
"srt-parser-2": "^1.2.3",
|
|
7575
|
+
unified: "^11.0.5",
|
|
7576
|
+
"unist-util-visit": "^5.0.0",
|
|
7577
|
+
vfile: "^6.0.3",
|
|
7578
|
+
xliff: "^6.2.1",
|
|
7579
|
+
xml2js: "^0.6.2",
|
|
7580
|
+
xpath: "^0.0.34",
|
|
7581
|
+
yaml: "^2.7.0",
|
|
7582
|
+
zod: "^3.24.1"
|
|
7583
|
+
},
|
|
7584
|
+
devDependencies: {
|
|
7585
|
+
"@types/babel__generator": "^7.27.0",
|
|
7586
|
+
"@types/cli-progress": "^3.11.6",
|
|
7587
|
+
"@types/cors": "^2.8.17",
|
|
7588
|
+
"@types/diff": "^7.0.0",
|
|
7589
|
+
"@types/express": "^5.0.1",
|
|
7590
|
+
"@types/figlet": "^1.7.0",
|
|
7591
|
+
"@types/gettext-parser": "^4.0.4",
|
|
7592
|
+
"@types/glob": "^8.1.0",
|
|
7593
|
+
"@types/ini": "^4.1.1",
|
|
7594
|
+
"@types/is-url": "^1.2.32",
|
|
7595
|
+
"@types/jsdom": "^21.1.7",
|
|
7596
|
+
"@types/lodash": "^4.17.16",
|
|
7597
|
+
"@types/mdast": "^4.0.4",
|
|
7598
|
+
"@types/node": "^22.10.2",
|
|
7599
|
+
"@types/node-gettext": "^3.0.6",
|
|
7600
|
+
"@types/object-hash": "^3.0.6",
|
|
7601
|
+
"@types/plist": "^3.0.5",
|
|
7602
|
+
"@types/react": "^18.3.20",
|
|
7603
|
+
"@types/xml2js": "^0.4.14",
|
|
7604
|
+
tsup: "^8.3.5",
|
|
7605
|
+
typescript: "^5.8.3",
|
|
7606
|
+
vitest: "^3.1.2"
|
|
7607
|
+
},
|
|
7608
|
+
engines: {
|
|
7609
|
+
node: ">=18"
|
|
7610
|
+
},
|
|
7611
|
+
packageManager: "pnpm@9.12.3"
|
|
7612
|
+
};
|
|
7563
7613
|
|
|
7564
7614
|
// src/cli/index.ts
|
|
7565
7615
|
dotenv.config();
|