feat: Replace python translation check script with JS test case
This commit is contained in:
parent
b3ef5a9e85
commit
b4760847b2
@ -1,87 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def find_missing_translations():
|
||||
# Load the English translation file
|
||||
with open("src/lang/en.json", "r", encoding="utf-8") as f:
|
||||
en_translations = json.load(f)
|
||||
|
||||
# Regex to find i18n keys in Vue files
|
||||
translation_regex = re.compile(
|
||||
# Matches $t('key'), $t("key"), $t('key', [ ... ])
|
||||
r"""\$t\((['"])(?P<key1>.*?)\1\s*[,)]"""
|
||||
+ "|"
|
||||
+
|
||||
# Matches i18n-t keypath="key"
|
||||
r"""i18n-t\s+keypath="(?P<key2>[^"]+)" """,
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
missing_keys = []
|
||||
|
||||
# Walk through the src directory
|
||||
for root, _, files in os.walk("src"):
|
||||
for file in files:
|
||||
if file.endswith((".vue", ".js")):
|
||||
file_path = os.path.join(root, file)
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
for line_num, line in enumerate(lines, 1):
|
||||
for match in translation_regex.finditer(line):
|
||||
key = (
|
||||
match.group("key1")
|
||||
or match.group("key2")
|
||||
or match.group("key3")
|
||||
)
|
||||
if key and key not in en_translations:
|
||||
# Find start and end of the key itself
|
||||
for group_name in ["key1", "key2", "key3"]:
|
||||
if match.group(group_name):
|
||||
start, end = match.span(group_name)
|
||||
break
|
||||
missing_keys.append(
|
||||
(
|
||||
file_path,
|
||||
line_num,
|
||||
key,
|
||||
line.rstrip(),
|
||||
start,
|
||||
end,
|
||||
)
|
||||
)
|
||||
|
||||
except UnicodeDecodeError:
|
||||
print(f"Skipping file due to UnicodeDecodeError: {file_path}")
|
||||
|
||||
# Print the report
|
||||
if not missing_keys:
|
||||
print("No missing translation keys found.")
|
||||
else:
|
||||
for file_path, line_num, key, line_content, start, end in missing_keys:
|
||||
print(f"\nerror: Missing translation key: '{key}'")
|
||||
print(f" --> {file_path}:{line_num}:{start}")
|
||||
print(" |")
|
||||
print(f"{line_num:<5}| {line_content}")
|
||||
arrow = " " * (start - 1) + "^" * (end - start + 2)
|
||||
print(f" | {arrow} unrecognized translation key")
|
||||
print(" |")
|
||||
print(
|
||||
f" = note: please register the translation key '{key}' in en.json so that our awesome team of translators can translate them"
|
||||
)
|
||||
print(
|
||||
" = tip: if you want to contribute translations, please visit our https://weblate.kuma.pet"
|
||||
)
|
||||
print("")
|
||||
|
||||
print("===============================")
|
||||
file_count = len(set([item[0] for item in missing_keys]))
|
||||
print(
|
||||
f"Found a total of {len(missing_keys)} missing keys in {file_count} files."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
find_missing_translations()
|
||||
76
test/backend-test/check-translations.test.js
Normal file
76
test/backend-test/check-translations.test.js
Normal file
@ -0,0 +1,76 @@
|
||||
const { describe, it } = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
function* walk(dir) {
|
||||
const files = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const file of files) {
|
||||
if (file.isDirectory()) {
|
||||
yield* walk(path.join(dir, file.name));
|
||||
} else {
|
||||
yield path.join(dir, file.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("Check Translations", () => {
|
||||
it("should not have missing translation keys", () => {
|
||||
const enTranslations = JSON.parse(fs.readFileSync("src/lang/en.json", "utf-8"));
|
||||
|
||||
const translationRegex = /\$t\(['"](?<key1>.*?)['"]\s*[,)]|i18n-t\s+keypath=\"(?<key2>[^\"]+)\"/g;
|
||||
|
||||
const missingKeys = [];
|
||||
|
||||
for (const filePath of walk("src")) {
|
||||
if (filePath.endsWith(".vue") || filePath.endsWith(".js")) {
|
||||
try {
|
||||
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
|
||||
lines.forEach((line, lineNum) => {
|
||||
let match;
|
||||
while ((match = translationRegex.exec(line)) !== null) {
|
||||
const key = match.groups.key1 || match.groups.key2;
|
||||
if (key && !enTranslations[key]) {
|
||||
const start = match.index;
|
||||
const end = start + key.length;
|
||||
missingKeys.push({
|
||||
filePath,
|
||||
lineNum: lineNum + 1,
|
||||
key,
|
||||
line: line.trim(),
|
||||
start,
|
||||
end,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError && e.message.includes("is not a function")) {
|
||||
// Ignore errors from binary files
|
||||
} else {
|
||||
console.error(`Error processing file: ${filePath}`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.length > 0) {
|
||||
let report = "Missing translation keys found:\n";
|
||||
missingKeys.forEach(({ filePath, lineNum, key, line, start, end }) => {
|
||||
report += `\nerror: Missing translation key: '${key}'`;
|
||||
report += `\n --> ${filePath}:${lineNum}:${start}`;
|
||||
report += "\n |";
|
||||
report += `\n${String(lineNum).padEnd(5)}| ${line}`;
|
||||
const arrow = " ".repeat(start) + "^".repeat(end - start);
|
||||
report += `\n | ${arrow} unrecognized translation key`;
|
||||
report += "\n |";
|
||||
report += `\n = note: please register the translation key '${key}' in en.json so that our awesome team of translators can translate them`;
|
||||
report += "\n = tip: if you want to contribute translations, please visit our https://weblate.kuma.pet\n";
|
||||
});
|
||||
report += `\n===============================`;
|
||||
const fileCount = new Set(missingKeys.map(item => item.filePath)).size;
|
||||
report += `\nFound a total of ${missingKeys.length} missing keys in ${fileCount} files.`;
|
||||
assert.fail(report);
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user