@scoutello/i18n-magic
Version:
Intelligent CLI toolkit that automates internationalization workflows for JavaScript/TypeScript projects and auto-translates new string keys to other languages
136 lines ⢠5.62 kB
JavaScript
import prompts from "prompts";
import { getKeysWithNamespaces, getPureKey, loadLocalesFile, writeLocalesFile, } from "../lib/utils.js";
export const findUnusedKeys = async (config) => {
const { globPatterns, namespaces, defaultNamespace, locales, loadPath } = config;
const keysWithNamespaces = await getKeysWithNamespaces({
globPatterns,
defaultNamespace,
});
const keysByNamespace = {};
for (const { key, namespaces: keyNamespaces } of keysWithNamespaces) {
for (const namespace of keyNamespaces) {
if (!keysByNamespace[namespace]) {
keysByNamespace[namespace] = new Set();
}
const pureKey = getPureKey(key, namespace, namespace === defaultNamespace);
const finalKey = pureKey || (!key.includes(":") ? key : null);
if (finalKey) {
keysByNamespace[namespace].add(finalKey);
}
}
}
const unusedKeys = [];
let totalKeys = 0;
for (const namespace of namespaces) {
const usedKeysSet = keysByNamespace[namespace] || new Set();
const unusedInNamespace = new Map();
for (const locale of locales) {
const existingKeys = await loadLocalesFile(loadPath, locale, namespace);
for (const key of Object.keys(existingKeys)) {
totalKeys++;
if (!usedKeysSet.has(key)) {
if (!unusedInNamespace.has(key)) {
unusedInNamespace.set(key, []);
}
unusedInNamespace.get(key).push(locale);
}
}
}
for (const [key, keyLocales] of unusedInNamespace) {
unusedKeys.push({ key, namespace, locales: keyLocales });
}
}
const uniqueUnusedKeys = new Map();
for (const item of unusedKeys) {
const uniqueKey = `${item.namespace}:${item.key}`;
if (!uniqueUnusedKeys.has(uniqueKey)) {
uniqueUnusedKeys.set(uniqueKey, item);
}
}
return {
totalKeys,
unusedCount: uniqueUnusedKeys.size,
unusedKeys: Array.from(uniqueUnusedKeys.values()),
};
};
export const removeUnusedKeys = async (config, options) => {
const { globPatterns, namespaces, defaultNamespace, locales, loadPath, savePath, } = config;
console.log("š Scanning for unused translation keys...\n");
const report = await findUnusedKeys(config);
if (report.unusedCount === 0) {
console.log(`ā
No unused keys found in the project (${report.totalKeys} total key instances across all locales)`);
return;
}
console.log(`\nā ļø Found ${report.unusedCount} unused translation key(s):\n`);
const maxKeysToShow = 20;
const keysToShow = report.unusedKeys.slice(0, maxKeysToShow);
for (const { key, namespace } of keysToShow) {
console.log(` ⢠${namespace}:${key}`);
}
if (report.unusedKeys.length > maxKeysToShow) {
console.log(` ... and ${report.unusedKeys.length - maxKeysToShow} more`);
}
console.log("");
if (!options?.skipConfirmation) {
const { confirmed } = await prompts({
type: "confirm",
name: "confirmed",
message: `Do you want to remove these ${report.unusedCount} unused key(s)?`,
initial: false,
});
if (!confirmed) {
console.log("\nā Operation cancelled. No keys were removed.");
return;
}
}
console.log("\nšļø Removing unused keys...");
const keysWithNamespaces = await getKeysWithNamespaces({
globPatterns,
defaultNamespace,
});
const keysByNamespace = {};
for (const { key, namespaces: keyNamespaces } of keysWithNamespaces) {
for (const namespace of keyNamespaces) {
if (!keysByNamespace[namespace]) {
keysByNamespace[namespace] = new Set();
}
const pureKey = getPureKey(key, namespace, namespace === defaultNamespace);
const finalKey = pureKey || (!key.includes(":") ? key : null);
if (finalKey) {
keysByNamespace[namespace].add(finalKey);
}
}
}
const stats = {
total: 0,
removed: 0,
};
const results = await Promise.all(namespaces.flatMap((namespace) => {
const usedKeysSet = keysByNamespace[namespace] || new Set();
return locales.map(async (locale) => {
const existingKeys = await loadLocalesFile(loadPath, locale, namespace);
const existingKeysCount = Object.keys(existingKeys).length;
const cleanedKeys = {};
let removedCount = 0;
for (const [key, value] of Object.entries(existingKeys)) {
if (usedKeysSet.has(key)) {
cleanedKeys[key] = value;
}
else {
removedCount++;
}
}
if (removedCount > 0) {
await writeLocalesFile(savePath, locale, namespace, cleanedKeys);
console.log(` ā Removed ${removedCount} unused keys from ${locale}:${namespace} (${Object.keys(cleanedKeys).length} keys remaining)`);
}
return { total: existingKeysCount, removed: removedCount };
});
}));
for (const result of results) {
stats.total += result.total;
stats.removed += result.removed;
}
console.log(`\nā
Removed ${stats.removed} unused keys (out of ${stats.total} total keys)`);
};
//# sourceMappingURL=clean.js.map