Add language metadata and enhance language validation tests (#2748)

Resolves #2739

## Description:

Introduce language metadata handling and refactor existing language
checks to validate the existence of language JSON and corresponding SVG
files. Add tests to ensure the integrity of the new metadata structure
and its references.

The lang field is intentionally kept in each language file.
This is because the files are frequently regenerated by Crowdin, and the
field also serves as a hint for management and maintenance.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

aotumuri

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Aotumuri
2026-01-02 18:45:49 -08:00
committed by GitHub
co-authored by coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
parent d9ccb0ea16
commit ae6293f6da
5 changed files with 328 additions and 176 deletions
-27
View File
@@ -1,27 +0,0 @@
import fs from "fs";
import path from "path";
describe("LangCode Filename Check", () => {
const langDir = path.join(__dirname, "../resources/lang");
test("lang_code matches filename", () => {
const files = fs
.readdirSync(langDir)
.filter((file) => file.endsWith(".json"));
if (files.length === 0) {
console.log("No resources/lang/*.json files found. Skipping check.");
return;
}
for (const file of files) {
const filePath = path.join(langDir, file);
const jsonData = JSON.parse(fs.readFileSync(filePath, "utf-8"));
const fileNameWithoutExt = path.basename(file, ".json");
const langCode = jsonData.lang?.lang_code;
expect(fileNameWithoutExt).toBe(langCode);
}
});
});
+62
View File
@@ -0,0 +1,62 @@
import fs from "fs";
import path from "path";
describe("Lang Metadata Check", () => {
const langDir = path.join(__dirname, "../resources/lang");
const flagDir = path.join(__dirname, "../resources/flags");
const metadataFile = path.join(langDir, "metadata.json");
test("metadata languages point to existing lang json and flag files", () => {
if (!fs.existsSync(metadataFile)) {
console.log(
"No resources/lang/metadata.json file found. Skipping check.",
);
return;
}
const metadata = JSON.parse(fs.readFileSync(metadataFile, "utf-8"));
if (!Array.isArray(metadata) || metadata.length === 0) {
console.log(
"No language entries found in metadata.json. Skipping check.",
);
return;
}
const errors: string[] = [];
for (const entry of metadata) {
const code = entry?.code;
const svg = entry?.svg;
if (typeof code !== "string" || code.length === 0) {
errors.push(
`metadata entry missing valid code: ${JSON.stringify(entry)}`,
);
continue;
}
if (typeof svg !== "string" || svg.length === 0) {
errors.push(
`[${code}]: metadata svg is missing or not a non-empty string`,
);
continue;
}
const langFilePath = path.join(langDir, `${code}.json`);
if (!fs.existsSync(langFilePath)) {
errors.push(`[${code}]: lang json file does not exist: ${code}.json`);
}
const svgFile = svg.endsWith(".svg") ? svg : `${svg}.svg`;
const flagPath = path.join(flagDir, svgFile);
if (!fs.existsSync(flagPath)) {
errors.push(`[${code}]: SVG file does not exist: ${svgFile}`);
}
}
if (errors.length > 0) {
console.error(
"Metadata lang or SVG file check failed:\n" + errors.join("\n"),
);
expect(errors).toEqual([]);
}
});
});
-53
View File
@@ -1,53 +0,0 @@
import fs from "fs";
import path from "path";
describe("Lang SVG Field and File Existence Check", () => {
const langDir = path.join(__dirname, "../resources/lang");
const flagDir = path.join(__dirname, "../resources/flags");
test("each lang.json file has a valid lang.svg string and the SVG file exists", () => {
const files = fs
.readdirSync(langDir)
.filter((file) => file.endsWith(".json"));
if (files.length === 0) {
console.log("No resources/lang/*.json files found. Skipping check.");
return;
}
const errors: string[] = [];
for (const file of files) {
try {
const filePath = path.join(langDir, file);
const jsonData = JSON.parse(fs.readFileSync(filePath, "utf-8"));
const langSvg = jsonData.lang?.svg;
if (typeof langSvg !== "string" || langSvg.length === 0) {
errors.push(
`[${file}]: lang.svg is missing or not a non-empty string`,
);
continue;
}
// Check if the SVG file exists in the flags directory
const svgFile = langSvg.endsWith(".svg") ? langSvg : `${langSvg}.svg`;
const flagPath = path.join(flagDir, svgFile);
if (!fs.existsSync(flagPath)) {
errors.push(`[${file}]: SVG file does not exist: ${svgFile}`);
}
} catch (err) {
errors.push(
`[${file}]: Exception occurred - ${(err as Error).message}`,
);
}
}
if (errors.length > 0) {
console.error(
"Lang SVG field or file check failed:\n" + errors.join("\n"),
);
expect(errors).toEqual([]);
}
});
});