Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ planned for 2025-07-01
- [calendar] fix fullday event rrule until with timezone offset (#3781)
- [feat] Add rule `no-undef` in config file validation to fix #3785 (#3786)
- [fonts] Fix `roboto.css` to avoid error message `Unknown descriptor 'var(' in @font-face rule.` in firefox console (#3787)
- [tests] Fix and refactor e2e test `Same keys` in `translations_spec.js` (#3809)

### Updated

Expand Down
106 changes: 54 additions & 52 deletions tests/e2e/translations_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,77 +154,79 @@ describe("Translations", () => {

describe("Same keys", () => {
let base;
let missing = [];

beforeAll(() => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
// Some expressions are not easy to translate automatically. For the sake of a working test, we filter them out.
const COMMON_EXCEPTIONS = ["WEEK_SHORT"];

// Some languages don't have certain words, so we need to filter those language specific exceptions.
const LANGUAGE_EXCEPTIONS = {
ca: ["DAYBEFOREYESTERDAY"],
cv: ["DAYBEFOREYESTERDAY"],
cy: ["DAYBEFOREYESTERDAY"],
en: ["DAYAFTERTOMORROW", "DAYBEFOREYESTERDAY"],
fy: ["DAYBEFOREYESTERDAY"],
gl: ["DAYBEFOREYESTERDAY"],
hu: ["DAYBEFOREYESTERDAY"],
id: ["DAYBEFOREYESTERDAY"],
it: ["DAYBEFOREYESTERDAY"],
"pt-br": ["DAYAFTERTOMORROW"],
tr: ["DAYBEFOREYESTERDAY"]
};

// Function to initialize JSDOM and load translations
const initializeTranslationDOM = (language) => {
const dom = new JSDOM("", { runScripts: "dangerously", resources: "usable" });
dom.window.Log = { log: jest.fn() };
dom.window.translations = translations;
const translatorJs = fs.readFileSync(path.join(__dirname, "..", "..", "js", "translator.js"), "utf-8");
dom.window.eval(translatorJs);

return new Promise((resolve) => {
dom.window.onload = async () => {
const { Translator } = dom.window;

await Translator.load(mmm, translations.de, false);
base = Object.keys(Translator.translations[mmm.name]).sort();
done();
await Translator.load(mmm, translations[language], false);
resolve(Translator.translations[mmm.name]);
};
});
});
};

afterAll(() => {
console.log(missing);
beforeAll(async () => {
// Using German as the base rather than English, since
// some words do not have a direct translation in English.
const germanTranslations = await initializeTranslationDOM("de");
base = Object.keys(germanTranslations).sort();
});

// Using German as the base rather than English, since
// at least one translated word doesn't exist in English.
for (let language in translations) {
if (language === "de") {
continue;
}
for (const language in translations) {
if (language === "de") continue;

describe(`Translation keys of ${language}`, () => {
let keys;

beforeAll(() => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;

await Translator.load(mmm, translations[language], false);
keys = Object.keys(Translator.translations[mmm.name]).sort();
done();
};
});
beforeAll(async () => {
const languageTranslations = await initializeTranslationDOM(language);
keys = Object.keys(languageTranslations).sort();
});

it(`${language} keys should be in base`, () => {
it(`${language} should not contain keys that are not in base language`, () => {
keys.forEach((key) => {
expect(base.indexOf(key)).toBeGreaterThanOrEqual(0);
expect(base).toContain(key, `Translation key '${key}' in language '${language}' is not present in base language`);
});
});

it(`${language} should contain all base keys`, () => {
// TODO: when all translations are fixed, use
// expect(keys).toEqual(base);
// instead of the try-catch-block

try {
expect(keys).toEqual(base);
} catch (e) {
if (e.message.match(/expect.*toEqual/)) {
const diff = base.filter((key) => !keys.includes(key));
missing.push(`Missing Translations for language ${language}: ${diff}`);
} else {
throw e;
}
it(`${language} should contain all base keys (excluding defined exceptions)`, () => {
let filteredBase = base.filter((key) => !COMMON_EXCEPTIONS.includes(key));
let filteredKeys = keys.filter((key) => !COMMON_EXCEPTIONS.includes(key));

if (LANGUAGE_EXCEPTIONS[language]) {
const exceptions = LANGUAGE_EXCEPTIONS[language];
filteredBase = filteredBase.filter((key) => !exceptions.includes(key));
filteredKeys = filteredKeys.filter((key) => !exceptions.includes(key));
}

filteredBase.forEach((baseKey) => {
expect(filteredKeys).toContain(baseKey, `Translation key '${baseKey}' is missing in language '${language}'`);
});
});
});
}
Expand Down
62 changes: 31 additions & 31 deletions translations/translations.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
let translations = {
en: "translations/en.json", // English
nl: "translations/nl.json", // Dutch
af: "translations/af.json", // Afrikaans
bg: "translations/bg.json", // Bulgarian
ca: "translations/ca.json", // Catalan
cs: "translations/cs.json", // Czech
cv: "translations/cv.json", // Chuvash
cy: "translations/cy.json", // Welsh (Cymraeg)
da: "translations/da.json", // Danish
de: "translations/de.json", // German
el: "translations/el.json", // Greek
eo: "translations/eo.json", // Esperanto
es: "translations/es.json", // Spanish
et: "translations/et.json", // Estonian
fi: "translations/fi.json", // Suomi
fr: "translations/fr.json", // French
fy: "translations/fy.json", // Frysk
es: "translations/es.json", // Spanish
ca: "translations/ca.json", // Catalan
cv: "translations/cv.json", // Chuvash
nb: "translations/nb.json", // Norsk bokmål
nn: "translations/nn.json", // Norsk nynorsk
pt: "translations/pt.json", // Português
"pt-br": "translations/pt-br.json", // Português Brasileiro
sv: "translations/sv.json", // Svenska
gl: "translations/gl.json", // Galego
gu: "translations/gu.json", // Gujarati
he: "translations/he.json", // Hebrew
hi: "translations/hi.json", // Hindi
hr: "translations/hr.json", // Croatian
hu: "translations/hu.json", // Hungarian
id: "translations/id.json", // Indonesian
is: "translations/is.json", // Icelandic
it: "translations/it.json", // Italian
"zh-cn": "translations/zh-cn.json", // Simplified Chinese
"zh-tw": "translations/zh-tw.json", // Traditional Chinese
ja: "translations/ja.json", // Japanese
pl: "translations/pl.json", // Polish
el: "translations/el.json", // Greek
da: "translations/da.json", // Danish
tr: "translations/tr.json", // Turkish
ru: "translations/ru.json", // Russian
af: "translations/af.json", // Afrikaans
hu: "translations/hu.json", // Hungarian
is: "translations/is.json", // Icelandic
et: "translations/et.json", // Estonian
ko: "translations/ko.json", // Korean
lt: "translations/lt.json", // Lithuanian
"ms-my": "translations/ms-my.json", // Malay
nb: "translations/nb.json", // Norsk bokmål
nl: "translations/nl.json", // Dutch
nn: "translations/nn.json", // Norsk nynorsk
pl: "translations/pl.json", // Polish
pt: "translations/pt.json", // Português
"pt-br": "translations/pt-br.json", // Português Brasileiro
ro: "translations/ro.json", // Romanian
cy: "translations/cy.json", // Welsh (Cymraeg)
bg: "translations/bg.json", // Bulgarian
cs: "translations/cs.json", // Czech
hr: "translations/hr.json", // Croatian
ru: "translations/ru.json", // Russian
sk: "translations/sk.json", // Slovak
sv: "translations/sv.json", // Svenska
th: "translations/th.json", // Thai
tlh: "translations/tlh.json", // Klingon
"ms-my": "translations/ms-my.json", // Malay
he: "translations/he.json", // Hebrew
tr: "translations/tr.json", // Turkish
uk: "translations/uk.json", // Ukrainian
hi: "translations/hi.json", // Hindi
gu: "translations/gu.json", // Gujarati
gl: "translations/gl.json", // Galego
lt: "translations/lt.json", // Lithuanian
th: "translations/th.json" // Thai
"zh-cn": "translations/zh-cn.json", // Simplified Chinese
"zh-tw": "translations/zh-tw.json" // Traditional Chinese
};

if (typeof module !== "undefined") {
Expand Down