Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tame-cats-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"lingo.dev": minor
---

Implemented check command to that exits with non-zero code when translations need updating, replacing the lingo.dev i18n --frozen functionality we're deprecating in the future (in favor of run command).
156 changes: 156 additions & 0 deletions packages/cli/src/cli/cmd/check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { Command } from "interactive-commander";
import setup from "./run/setup";
import plan from "./run/plan";
import { CmdRunContext, flagsSchema } from "./run/_types";
import {
renderClear,
renderSpacer,
renderBanner,
renderHero,
pauseIfDebug,
} from "../utils/ui";
import chalk from "chalk";
import trackEvent from "../utils/observability";
import { determineAuthId } from "./run/_utils";
import { colors } from "../constants";

export default new Command()
.command("check")
.description("Check if translations need updating without making changes")
.helpOption("-h, --help", "Show help")
.option(
"--source-locale <source-locale>",
"Locale to use as source locale. Defaults to i18n.json locale.source",
)
.option(
"--target-locale <target-locale>",
"Locale to use as target locale. Defaults to i18n.json locale.targets",
(val: string, prev: string[]) => (prev ? [...prev, val] : [val]),
)
.option(
"--bucket <bucket>",
"Bucket to process",
(val: string, prev: string[]) => (prev ? [...prev, val] : [val]),
)
.option(
"--file <file>",
"File to process. Process only files that match this glob pattern in their path. Use quotes around patterns to prevent shell expansion (e.g., --file '**/*.json'). Useful if you have a lot of files and want to focus on a specific one. Specify more files separated by commas or spaces. Accepts glob patterns.",
(val: string, prev: string[]) => (prev ? [...prev, val] : [val]),
)
.option(
"--key <key>",
"Key to process. Process only a specific translation key, useful for updating a single entry. Accepts glob patterns.",
(val: string, prev: string[]) => (prev ? [...prev, val] : [val]),
)
.option(
"--force",
"Ignore lockfile and process all keys, useful for full re-translation",
)
.option(
"--api-key <api-key>",
"Explicitly set the API key to use, override the default API key from settings",
)
.option(
"--debug",
"Pause execution at start for debugging purposes, waits for user confirmation before proceeding",
)
.option(
"--concurrency <concurrency>",
"Number of concurrent tasks to run",
(val: string) => parseInt(val),
)
.action(async (args) => {
let authId: string | null = null;
try {
const ctx: CmdRunContext = {
flags: flagsSchema.parse(args),
config: null,
results: new Map(),
tasks: [],
localizer: null,
};

await pauseIfDebug(ctx.flags.debug);
await renderClear();
await renderSpacer();
await renderBanner();
await renderHero();
await renderSpacer();

await setup(ctx);

authId = await determineAuthId(ctx);

trackEvent(authId, "cmd.check.start", {
config: ctx.config,
flags: ctx.flags,
});

await renderSpacer();

await plan(ctx);
await renderSpacer();

// Display summary of what needs updating
if (ctx.tasks.length > 0) {
console.log(chalk.hex(colors.orange)("[Check Results]"));
console.log(
`${chalk.hex(colors.red)("✗")} Translations need updating: ${chalk.hex(
colors.yellow,
)(ctx.tasks.length.toString())} task(s) found`,
);

// Group tasks by target locale for better readability
const tasksByTargetLocale = ctx.tasks.reduce(
(acc, task) => {
if (!acc[task.targetLocale]) {
acc[task.targetLocale] = [];
}
acc[task.targetLocale].push(task);
return acc;
},
{} as Record<string, typeof ctx.tasks>,
);

Object.entries(tasksByTargetLocale).forEach(([locale, tasks]) => {
console.log(
` ${chalk.hex(colors.blue)("→")} ${chalk.hex(colors.blue)(
locale,
)}: ${tasks.length} task(s)`,
);
});

console.log(
`\nRun ${chalk.hex(colors.green)(
"lingo.dev run",
)} to update translations.`,
);
await renderSpacer();

trackEvent(authId, "cmd.check.needs_update", {
config: ctx.config,
flags: ctx.flags,
taskCount: ctx.tasks.length,
});

process.exit(1);
} else {
console.log(chalk.hex(colors.orange)("[Check Results]"));
console.log(
`${chalk.hex(colors.green)("✓")} All translations are up-to-date`,
);
await renderSpacer();

trackEvent(authId, "cmd.check.up_to_date", {
config: ctx.config,
flags: ctx.flags,
});

process.exit(0);
}
} catch (error: any) {
trackEvent(authId || "unknown", "cmd.check.error", {});
console.error(chalk.red("Error during check:"), error.message);
process.exit(1);
}
});
2 changes: 2 additions & 0 deletions packages/cli/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import statusCmd from "./cmd/status";
import mayTheFourthCmd from "./cmd/may-the-fourth";
import packageJson from "../../package.json";
import run from "./cmd/run";
import checkCmd from "./cmd/check";
import purgeCmd from "./cmd/purge";

export default new InteractiveCommand()
Expand Down Expand Up @@ -58,6 +59,7 @@ Star the the repo :) https://github.com/LingoDotDev/lingo.dev
.addCommand(statusCmd)
.addCommand(mayTheFourthCmd, { hidden: true })
.addCommand(run)
.addCommand(checkCmd)
.addCommand(purgeCmd)
.exitOverride((err) => {
// Exit with code 0 when help or version is displayed
Expand Down
Loading