Skip to content

Commit

Permalink
Add force command
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Dec 23, 2024
1 parent 7f855dc commit 7b4e8a4
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 35 deletions.
1 change: 1 addition & 0 deletions apps/web/src/components/install.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function Install() {
<code>{`init Initialize a new Languine configuration
translate Translate to all target locales
translate <locale> Translate to a specific locale
translate --force Force translate all keys
instructions Add custom translation instructions
diff Check for changes in source locale file
clean Clean unused translations
Expand Down
2 changes: 1 addition & 1 deletion examples/next-international/locales/fr.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default {
"hello": "Bonjour",
"welcome": "Bonjour {name} !",
"about.you": "Bonjour {name} ! Vous avez {age} ans",
"about.you": "Bonjour {name} ! Tu as {age} ans",
"scope.test": "Un domaine",
"scope.more.test": "Un domaine",
"scope.more.param": "Un domaine avec {param}",
Expand Down
81 changes: 48 additions & 33 deletions packages/cli/src/commands/translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import dedent from "dedent";
import { prompt as defaultPrompt } from "../prompt.js";
import { extractChangedKeys, getApiKey, getConfig } from "../utils.js";

export async function translate(targetLocale?: string) {
export async function translate(targetLocale?: string, force?: boolean) {
intro("Starting translation process...");

const config = await getConfig();
Expand Down Expand Up @@ -43,19 +43,24 @@ export async function translate(targetLocale?: string) {
const targetPath = pattern.replace("[locale]", locale);

try {
// Get git diff for source file
const diff = execSync(`git diff HEAD -- ${sourcePath}`, {
encoding: "utf-8",
});
let addedKeys: string[] = [];

if (!diff) {
return { locale, sourcePath, success: true, noChanges: true };
}
if (!force) {
// Get git diff for source file if not force translating
const diff = execSync(`git diff HEAD -- ${sourcePath}`, {
encoding: "utf-8",
});

if (!diff) {
return { locale, sourcePath, success: true, noChanges: true };
}

const { addedKeys } = extractChangedKeys(diff);
const changes = extractChangedKeys(diff);
addedKeys = changes.addedKeys;

if (addedKeys.length === 0) {
return { locale, sourcePath, success: true, noChanges: true };
if (addedKeys.length === 0) {
return { locale, sourcePath, success: true, noChanges: true };
}
}

// Read source and target files
Expand All @@ -78,31 +83,33 @@ export async function translate(targetLocale?: string) {
await fs.mkdir(targetDir, { recursive: true });
}

// Prepare translation prompt with only new keys
// Parse source content
const sourceObj =
format === "ts"
? Function(
`return ${sourceContent.replace(/export default |as const;/g, "")}`,
)()
: JSON.parse(sourceContent);

const newKeysObj: Record<string, string> = {};
for (const key of addedKeys) {
newKeysObj[key] = sourceObj[key];
// If force is true, translate everything. Otherwise only new keys
const keysToTranslate = force ? Object.keys(sourceObj) : addedKeys;
const contentToTranslate: Record<string, string> = {};
for (const key of keysToTranslate) {
contentToTranslate[key] = sourceObj[key];
}

const prompt = dedent`
You are a professional translator working with ${format.toUpperCase()} files.
Task: Translate the content below from ${source} to ${locale}.
Only translate the new keys provided.
${force ? "" : "Only translate the new keys provided."}
${defaultPrompt}
${config.instructions ?? ""}
Source content (new keys only):
${JSON.stringify(newKeysObj, null, 2)}
Source content ${force ? "" : "(new keys only)"}:
${JSON.stringify(contentToTranslate, null, 2)}
Return only the translated content with identical structure.
`;
Expand All @@ -119,22 +126,25 @@ export async function translate(targetLocale?: string) {
? Function(`return ${text.replace(/as const;?/g, "")}`)()
: JSON.parse(text);

// Merge with existing translations
const existingObj = targetContent
? format === "ts"
? Function(
`return ${targetContent.replace(/export default |as const;/g, "")}`,
)()
: JSON.parse(targetContent)
: {};

const mergedObj = { ...existingObj, ...translatedObj };
// Merge with existing translations if not force translating
const finalObj = force
? translatedObj
: {
...(targetContent
? format === "ts"
? Function(
`return ${targetContent.replace(/export default |as const;/g, "")}`,
)()
: JSON.parse(targetContent)
: {}),
...translatedObj,
};

// Format the final content
let finalContent =
format === "ts"
? `export default ${JSON.stringify(mergedObj, null, 2)} as const;\n`
: JSON.stringify(mergedObj, null, 2);
? `export default ${JSON.stringify(finalObj, null, 2)} as const;\n`
: JSON.stringify(finalObj, null, 2);

// Run afterTranslate hook if defined
if (config.hooks?.afterTranslate) {
Expand All @@ -151,7 +161,12 @@ export async function translate(targetLocale?: string) {
"utf-8",
);

return { locale, sourcePath, success: true, addedKeys };
return {
locale,
sourcePath,
success: true,
addedKeys: keysToTranslate,
};
} catch (error) {
return { locale, sourcePath, success: false, error };
}
Expand All @@ -172,12 +187,12 @@ export async function translate(targetLocale?: string) {
for (const result of changes) {
console.log(
chalk.green(
`✓ Translated ${result.addedKeys?.length} new keys for ${result.locale}`,
`✓ Translated ${result.addedKeys?.length} ${force ? "total" : "new"} keys for ${result.locale}`,
),
);
}
} else {
console.log(chalk.yellow("No new keys to translate"));
console.log(chalk.yellow(`No ${force ? "" : "new "}keys to translate`));
}

if (failures.length > 0) {
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const command =
options: [
{ value: "init", label: "Initialize a new Languine configuration" },
{ value: "translate", label: "Translate to target languages" },
{ value: "add", label: "Add a new language" },
{ value: "instructions", label: "Add custom translation instructions" },
{ value: "diff", label: "Check for changes in source locale file" },
{ value: "clean", label: "Clean unused translations" },
Expand All @@ -48,11 +49,12 @@ const command =
}));

const targetLocale = process.argv[3];
const force = process.argv.includes("--force") || process.argv.includes("-f");

if (command === "init") {
init();
} else if (command === "translate") {
translate(targetLocale);
translate(targetLocale, force);
} else if (command === "instructions") {
instructions();
} else if (command === "diff") {
Expand All @@ -64,6 +66,7 @@ if (command === "init") {
${chalk.cyan("init")} Initialize a new Languine configuration
${chalk.cyan("translate")} Translate to all target locales
${chalk.cyan("translate")} ${chalk.gray("<locale>")} Translate to a specific locale
${chalk.cyan("translate")} ${chalk.gray("--force")} Force translate all keys
${chalk.cyan("instructions")} Add custom translation instructions
${chalk.cyan("diff")} Check for changes in source locale file
${chalk.cyan("clean")} Clean unused translations
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { execSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { confirm, outro, text } from "@clack/prompts";
import chalk from "chalk";
Expand Down Expand Up @@ -105,3 +106,7 @@ export function extractChangedKeys(diff: string) {
removedKeys: Array.from(removedKeys),
};
}

export function updateConfig(config: Config) {
fs.writeFileSync(configPath, `export default ${JSON.stringify(config)}`);
}

0 comments on commit 7b4e8a4

Please sign in to comment.