Skip to content

Commit

Permalink
Add clean command
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Dec 23, 2024
1 parent 028fdae commit 40dd28d
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 55 deletions.
47 changes: 3 additions & 44 deletions apps/web/src/components/commands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export function Commands() {
step >= 2 ? "opacity-100" : "opacity-0",
)}
>
│ es,pt,fr
│ es, pt, fr
</span>
<span
className={cn(
Expand Down Expand Up @@ -163,22 +163,6 @@ export function Commands() {
>
│ ○ JSON (.json)
</span>
<span
className={cn(
"transition-opacity duration-100",
step >= 4 ? "opacity-100" : "opacity-0",
)}
>
│ ○ YAML (.yaml)
</span>
<span
className={cn(
"transition-opacity duration-100",
step >= 4 ? "opacity-100" : "opacity-0",
)}
>
│ ○ Markdown (.md)
</span>
<span
className={cn(
"transition-opacity duration-100",
Expand Down Expand Up @@ -236,31 +220,6 @@ export function Commands() {
>
│ ○ GPT-3.5 Turbo
</span>
<span
className={cn(
"transition-opacity duration-100",
step >= 5 ? "opacity-100" : "opacity-0",
)}
>
</span>

<span
className={cn(
"transition-opacity duration-100",
step >= 6 ? "opacity-100" : "opacity-0",
)}
>
◆ Which OpenAI model should be used for translations?
</span>
<span
className={cn(
"transition-opacity duration-100",
step >= 6 ? "opacity-100" : "opacity-0",
)}
>
│ GPT-4 (Default)
</span>
<span
className={cn(
"transition-opacity duration-100",
Expand All @@ -271,8 +230,8 @@ export function Commands() {
</span>
<span
className={cn(
"transition-opacity duration-500 -ml-[1.5px]",
step >= 7 ? "opacity-100" : "opacity-0",
"transition-opacity duration-100 -ml-[1.5px]",
step >= 6 ? "opacity-100" : "opacity-0",
)}
>
└ Configuration file and language files created successfully!
Expand Down
3 changes: 1 addition & 2 deletions examples/i18next/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
},
"interpolated": "Have a nice day, {{name}}!",
"pluralKey_one": "This is a nice example.",
"pluralKey_other": "This are nice examples.",
"missing_translation": "This should work"
"pluralKey_other": "This are nice examples."
}
3 changes: 1 addition & 2 deletions examples/i18next/locales/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
},
"interpolated": "Ha en trevlig dag, {{name}}!",
"pluralKey_one": "Det här är ett fint exempel.",
"pluralKey_other": "Det här är fina exempel.",
"missing_translation": "Detta bör fungera"
"pluralKey_other": "Det här är fina exempel."
}
1 change: 0 additions & 1 deletion examples/next-international/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ export default {
"scope.more.stars#other": "{count} stars on GitHub",
"missing.translation.in.fr": "This should work",
"cows#one": "A cow",
"cows#other": "{count} cows",
} as const;
4 changes: 1 addition & 3 deletions examples/next-international/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,5 @@ export default {
"scope.more.stars#one": "1 étoile sur GitHub",
"scope.more.stars#other": "{count} étoiles sur GitHub",
"missing.translation.in.fr": "Cela devrait fonctionner",
"cows#one": "Une vache",
"cows#other": "{count} vaches",
"hello.world2": "Bonjour le monde !"
"cows#one": "Une vache"
} as const;
99 changes: 99 additions & 0 deletions packages/cli/src/commands/clean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import fs from "node:fs/promises";
import path from "node:path";
import { intro, outro, spinner } from "@clack/prompts";
import chalk from "chalk";
import { getConfig } from "../utils.js";

export async function clean() {
intro("Cleaning unused translations...");

try {
const config = await getConfig();
const s = spinner();
s.start("Removing unused translation keys...");

const { source, targets } = config.locale;
let totalKeysRemoved = 0;

// Process each file format and pattern
for (const [format, { include }] of Object.entries(config.files)) {
for (const pattern of include) {
const sourcePath = pattern.replace("[locale]", source);

// Read source file to get reference keys
const sourceContent = await fs.readFile(
path.join(process.cwd(), sourcePath),
"utf-8",
);

const sourceKeys =
format === "ts"
? Object.keys(
Function(
`return ${sourceContent.replace(/export default |as const;/g, "")}`,
)(),
)
: Object.keys(JSON.parse(sourceContent));

// Clean each target locale file
for (const locale of targets) {
const targetPath = pattern.replace("[locale]", locale);

try {
const targetContent = await fs.readFile(
path.join(process.cwd(), targetPath),
"utf-8",
);

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

const targetKeys = Object.keys(targetObj);
const keysToRemove = targetKeys.filter(
(key) => !sourceKeys.includes(key),
);
totalKeysRemoved += keysToRemove.length;

// Remove keys not in source
const cleanedObj = Object.fromEntries(
Object.entries(targetObj).filter(([key]) =>
sourceKeys.includes(key),
),
);

// Format and write cleaned content
const finalContent =
format === "ts"
? `export default ${JSON.stringify(cleanedObj, null, 2)} as const;\n`
: JSON.stringify(cleanedObj, null, 2);

await fs.writeFile(
path.join(process.cwd(), targetPath),
finalContent,
"utf-8",
);
} catch (error) {
console.error(chalk.red(`Error cleaning ${targetPath}:`), error);
}
}
}
}

s.stop("Cleaning completed");
outro(
totalKeysRemoved > 0
? chalk.green(
`Successfully removed ${totalKeysRemoved} unused translation keys!`,
)
: chalk.green("No unused translations found!"),
);
} catch (error) {
outro(chalk.red("Failed to clean translations"));
console.error(error);
process.exit(1);
}
}
12 changes: 9 additions & 3 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dotenv.config();
import { select } from "@clack/prompts";
import chalk from "chalk";
import dedent from "dedent";
import { clean } from "./commands/clean.js";
import { diff } from "./commands/diff.js";
import { init } from "./commands/init.js";
import { instructions } from "./commands/instructions.js";
Expand Down Expand Up @@ -41,6 +42,8 @@ const command =
{ value: "translate", label: "Translate to target languages" },
{ value: "instructions", label: "Add custom translation instructions" },
{ value: "diff", label: "Check for changes in source locale file" },
{ value: "clean", label: "Clean unused translations" },
{ value: "available", label: "Available commands" },
],
}));

Expand All @@ -54,14 +57,17 @@ if (command === "init") {
instructions();
} else if (command === "diff") {
diff();
} else {
console.log(chalk.bold("\nAvailable commands:"));
} else if (command === "clean") {
clean();
} else if (command === "available") {
console.log(dedent`
${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("instructions")} Add custom translation instructions
${chalk.cyan("diff")} Check for changes in source locale file
${chalk.cyan("clean")} Clean unused translations
${chalk.cyan("available")} Show available commands
Run ${chalk.cyan("languine <command>")} to execute a command
`);
}
1 change: 1 addition & 0 deletions packages/cli/src/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Translation Requirements:
- Respect existing whitespace and newline patterns
- Keep all technical identifiers unchanged
- Translate only user-facing strings
- Never add space before a ! or ?
`;

0 comments on commit 40dd28d

Please sign in to comment.