Commit 57797e5a authored by Laureline Nevin's avatar Laureline Nevin ⌨️
Browse files

Added check-lang.js option to list unused keys

parent 34c26a9d
Loading
Loading
Loading
Loading
+56 −14
Changes for scripts/check-lang.js: 56 added lines, 14 removed lines.
Original line number Diff line number Diff line
@@ -2,18 +2,16 @@ import path from 'path';
import * as fs from 'node:fs/promises';
import * as yaml from 'yaml';

const projRoot = path.resolve(import.meta.dirname, "..");
const verbose = process.argv.includes("--verbose");
const projRoot = path.resolve(import.meta.dirname, '..');
const verbose = process.argv.includes('--verbose');
let exit = 0;

if (!verbose)
    console.debug = () => {
    };
if (!verbose) console.debug = () => {};

const langs = [];
const langDir = path.resolve(projRoot, 'src/lang/');
for (const langFile of await fs.readdir(langDir))
    if (langFile.endsWith(".yaml"))
    if (langFile.endsWith('.yaml'))
        try {
            console.debug(`Reading file ${langFile}`);
            const data = await fs.readFile(path.resolve(langDir, langFile), 'utf8');
@@ -27,7 +25,8 @@ for (const langFile of await fs.readdir(langDir))

const keys = await fs.readFile(path.resolve(projRoot, 'index.html'), 'utf8').then((data) => {
    /** @type string[] */ const keys = [];
    for (const [match, value] of data.toString().matchAll(/t\('([^']+)'\)/g)) {
    for (const [match, v1, v2] of data.toString().matchAll(/\bt\('([^']+)'\)|\bv-t(?:\.raw)?="([^"]+)"/g)) {
        const value = v1 ?? v2;
        if (value.match(/^[a-z]+(\.[a-z]+)*$/i)) {
            console.debug(`Found key ${value}`);
            keys.push(value);
@@ -37,7 +36,7 @@ const keys = await fs.readFile(path.resolve(projRoot, 'index.html'), 'utf8').the
        }
    }
    if (!keys.length) {
        console.error("Error found no translation key in HTML file");
        console.error('Error found no translation key in HTML file');
        exit = 1;
    }
    return keys;
@@ -46,25 +45,68 @@ keys.sort();

/**
 * @param {string[]} key
 * @param {* | undefined} lang
 * @param {Record<string, any>} lang
 */
function checkKeyExists(key, lang) {
    return lang && (key.length === 0 || checkKeyExists(key.slice(1), lang[key[0]]));
}

const used = new Set();
for (const [lang, data] of langs) {
    const missing = [];
    for (const key of keys) {
        if (!checkKeyExists(key.split('.'), data))
        if (!used.has(key)) {
            if (checkKeyExists(key.split('.'), data)) {
                used.add(key);
            } else {
                missing.push(key);
            }
    if (missing.length)
        console.error(`Missing keys from ${lang}: \n${missing.map((s) => '\t' + s).join('\n')}`);
        }
    }
    if (missing.length) console.error(`Missing keys from ${lang}: \n${missing.map((s) => '\t' + s).join('\n')}`);
}

if (verbose) {
    /**
     * @param {string | Record<string | Record<string, any>>} lang
     * @param {Array<string>} keys
     * @param {string} prefix
     */
    const unusedListKeys = (lang, keys = [], prefix = '') => {
        if (typeof lang === 'string') {
            if (!used.has(prefix)) keys.push(prefix);
        } else {
            for (const [key, value] of Object.entries(lang)) {
                unusedListKeys(value, keys, (prefix ? prefix + '.' : '') + key);
            }
        }
        return keys;
    };
    /** @type {Set<string>} */
    let unused = new Set();
    const langUnused = langs.map(([lang, data]) => {
            const keys = new Set(unusedListKeys(data));
            unused = unused.union(keys);
            return [lang, keys];
        }),
        unusedArr = Array.from(unused).sort(),
        maxLen = unusedArr.reduce((p, v) => Math.max(p, v.length), 0),
        lines = unusedArr
            .map(
                (key) =>
                    ` - ${key.padStart(maxLen)} : ${langUnused
                        .filter(([_, vs]) => vs.has(key))
                        .map(([n, _]) => n)
                        .join(', ')}`,
            )
            .join('\n');

    console.log('Unused keys found in YAMLs: \n' + lines);
}

if (exit !== 0) {
    console.log("Some errors were found");
    console.log('Some errors were found');
} else {
    console.log("All i18n data checked successfully");
    console.log('All i18n data checked successfully');
}
process.exit(exit);