Improve MineGeo interface and functionality
This commit is contained in:
82
tests/i18n.test.ts
Normal file
82
tests/i18n.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import ts from "typescript";
|
||||
import russian from "../shared/locales/ru.json";
|
||||
import { detectLocale, resolveLocale, translator } from "../shared/i18n";
|
||||
|
||||
test("browser language preferences support regional tags and weighted fallbacks", () => {
|
||||
assert.equal(detectLocale("ru-RU,ru;q=0.9,en;q=0.8"), "ru");
|
||||
assert.equal(detectLocale("en-US;q=0.3,ru;q=0.9"), "ru");
|
||||
assert.equal(detectLocale("de-DE,ru;q=0.7,en;q=0.5"), "ru");
|
||||
assert.equal(detectLocale("ru;q=0,en-GB;q=0.8"), "en");
|
||||
assert.equal(detectLocale("ru;q=invalid,en"), "en");
|
||||
assert.equal(detectLocale(null), "en");
|
||||
assert.equal(detectLocale("fr-FR"), "en");
|
||||
});
|
||||
|
||||
test("an explicit language choice overrides automatic detection", () => {
|
||||
assert.equal(resolveLocale("en", "ru-RU"), "en");
|
||||
assert.equal(resolveLocale("ru", "en-US"), "ru");
|
||||
assert.equal(resolveLocale("auto", "ru"), "ru");
|
||||
assert.equal(resolveLocale("unexpected", "ru"), "ru");
|
||||
});
|
||||
|
||||
test("translations interpolate values without changing player content", () => {
|
||||
const ru = translator("ru"),
|
||||
en = translator("en");
|
||||
assert.equal(ru("Log in"), "Войти");
|
||||
assert.equal(en("Log in"), "Log in");
|
||||
assert.equal(
|
||||
ru("Keep {location}", { location: "Steve's Castle" }),
|
||||
"Оставить: Steve's Castle",
|
||||
);
|
||||
assert.equal(ru("Map authored by Steve"), "Map authored by Steve");
|
||||
assert.equal(ru("Keep {location}"), "Оставить: {location}");
|
||||
});
|
||||
|
||||
test("all literal interface messages have Russian translations with matching placeholders", () => {
|
||||
const missing: string[] = [],
|
||||
mismatches: string[] = [];
|
||||
const dictionary = russian as Record<string, string>;
|
||||
const placeholders = (s: string) =>
|
||||
[...s.matchAll(/\{(\w+)\}/g)].map((match) => match[1]).sort();
|
||||
function walk(directory: string) {
|
||||
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||
const file = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) walk(file);
|
||||
else if (file.endsWith(".tsx")) {
|
||||
const source = ts.createSourceFile(
|
||||
file,
|
||||
fs.readFileSync(file, "utf8"),
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.TSX,
|
||||
);
|
||||
function visit(node: ts.Node) {
|
||||
if (
|
||||
ts.isCallExpression(node) &&
|
||||
node.expression.getText(source) === "t" &&
|
||||
node.arguments[0] &&
|
||||
ts.isStringLiteral(node.arguments[0])
|
||||
) {
|
||||
const message = node.arguments[0].text;
|
||||
if (!dictionary[message]) missing.push(`${file}: ${message}`);
|
||||
else if (
|
||||
JSON.stringify(placeholders(message)) !==
|
||||
JSON.stringify(placeholders(dictionary[message]))
|
||||
)
|
||||
mismatches.push(message);
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
visit(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
walk("app");
|
||||
walk("components");
|
||||
assert.deepEqual(missing, []);
|
||||
assert.deepEqual(mismatches, []);
|
||||
});
|
||||
Reference in New Issue
Block a user