mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-06-08 22:02:35 +02:00
feat(extract-translations): add extract translations script
This commit is contained in:
parent
7907c07034
commit
0ab270f50b
7 changed files with 324 additions and 10969 deletions
85
extra/extract-translations.js
Normal file
85
extra/extract-translations.js
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
const findInFiles = require("find-in-files");
|
||||||
|
const _ = require("lodash");
|
||||||
|
const fs = require("fs/promises");
|
||||||
|
const JSON5 = require("json5");
|
||||||
|
|
||||||
|
// Extract translations from $t() functions in the source code and add the missing translations to all language files in src/languages/*.js
|
||||||
|
async function extractTranslations() {
|
||||||
|
// Load all es6 module translation files into a commonJS process
|
||||||
|
const languageList = {};
|
||||||
|
const filesNames = await fs.readdir("src/languages");
|
||||||
|
for (let fileName of filesNames) {
|
||||||
|
if (fileName.endsWith("js") && fileName !== "index.js") {
|
||||||
|
const content = (await fs.readFile("src/languages/" + fileName)).toString("utf-8");
|
||||||
|
const json = content.replace("export default {", "{").replace("};", "}");
|
||||||
|
languageList[fileName.split(".")[0]] = JSON5.parse(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const en = languageList.en;
|
||||||
|
|
||||||
|
// Search the source code for usages of $t()
|
||||||
|
const results = await findInFiles.find({
|
||||||
|
term: "\\$t\\(([^)]+?)\\)",
|
||||||
|
flags: "g",
|
||||||
|
}, "./src", "\\.(vue|js)");
|
||||||
|
|
||||||
|
const englishExtracted = [];
|
||||||
|
|
||||||
|
// Make a list of all the found strings
|
||||||
|
const warnings = [];
|
||||||
|
Object.values(results).map(result => {
|
||||||
|
result.matches.map(match => {
|
||||||
|
const functionParams = match.substring(3, match.length - 1).trim();
|
||||||
|
const firstChar = functionParams[0];
|
||||||
|
if (!["\"", "'"].includes(firstChar)) {
|
||||||
|
// Variable => cannot extract => output warning
|
||||||
|
warnings.push("Cannot extract non string values in " + match);
|
||||||
|
} else {
|
||||||
|
// Actual string
|
||||||
|
const content = _.trim(functionParams.split(firstChar)[1], "\"' ");
|
||||||
|
englishExtracted.push(content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update all languages with the missing strings
|
||||||
|
for (let extractedTranslation of englishExtracted) {
|
||||||
|
for (let langDict of Object.values(languageList)) {
|
||||||
|
if (!Object.keys(langDict).includes(extractedTranslation)) {
|
||||||
|
langDict[extractedTranslation] = extractedTranslation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for translations in other language files that are not in the english file and delete them
|
||||||
|
const englishKeys = Object.keys(en);
|
||||||
|
for (let langName of Object.keys(languageList)) {
|
||||||
|
if (langName !== "en") {
|
||||||
|
const langKeys = Object.keys(languageList[langName]);
|
||||||
|
const unusedKeys = _.without(langKeys, ...englishKeys);
|
||||||
|
if (unusedKeys.length) {
|
||||||
|
warnings.push(`Language file ${langName} contains keys that are not used: ["${unusedKeys.join("\", \"")}"]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(languageList, warnings);
|
||||||
|
|
||||||
|
for (let langName of Object.keys(languageList)) {
|
||||||
|
const translationsString = JSON5.stringify(languageList[langName], {
|
||||||
|
quote: "\"",
|
||||||
|
space: 4,
|
||||||
|
})
|
||||||
|
.replace(/"$/m, "\","); // Add comma to the last line
|
||||||
|
await fs.writeFile(`./src/languages/${_.kebabCase(langName)}.js`, `export default ${translationsString};\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnings.length) {
|
||||||
|
console.log("Extraction successful with warnings: \n\t" + warnings.join("\n\t"));
|
||||||
|
} else {
|
||||||
|
console.log("Extraction successful");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extractTranslations();
|
11162
package-lock.json
generated
11162
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -46,7 +46,8 @@
|
||||||
"test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
|
"test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
|
||||||
"simple-dns-server": "node extra/simple-dns-server.js",
|
"simple-dns-server": "node extra/simple-dns-server.js",
|
||||||
"update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix",
|
"update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix",
|
||||||
"update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix"
|
"update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix",
|
||||||
|
"extract-translations": "node extra/extract-translations.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "~1.2.36",
|
"@fortawesome/fontawesome-svg-core": "~1.2.36",
|
||||||
|
@ -107,8 +108,11 @@
|
||||||
"dns2": "~2.0.1",
|
"dns2": "~2.0.1",
|
||||||
"eslint": "~7.32.0",
|
"eslint": "~7.32.0",
|
||||||
"eslint-plugin-vue": "~7.18.0",
|
"eslint-plugin-vue": "~7.18.0",
|
||||||
|
"find-in-files": "^0.5.0",
|
||||||
"jest": "~27.2.4",
|
"jest": "~27.2.4",
|
||||||
"jest-puppeteer": "~6.0.0",
|
"jest-puppeteer": "~6.0.0",
|
||||||
|
"json5": "^2.2.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"puppeteer": "~10.4.0",
|
"puppeteer": "~10.4.0",
|
||||||
"sass": "~1.42.1",
|
"sass": "~1.42.1",
|
||||||
"stylelint": "~13.13.1",
|
"stylelint": "~13.13.1",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="notification-type" class="form-label">{{ $t("Notification Type") }}</label>
|
<label for="notification-type" class="form-label">{{ $t("Notification Type") }}</label>
|
||||||
<select id="notification-type" v-model="notification.type" class="form-select">
|
<select id="notification-type" v-model="notification.type" class="form-select">
|
||||||
<option v-for="type in notificationTypes" :key="type" :value="type">{{ $t(type) }}</option>
|
<option v-for="type in notificationTypes" :key="type" :value="type">{{ translatedType }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -102,6 +102,27 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return NotificationFormList[this.notification.type];
|
return NotificationFormList[this.notification.type];
|
||||||
|
},
|
||||||
|
translatedType() {
|
||||||
|
return {
|
||||||
|
"telegram": $t("telegram"),
|
||||||
|
"webhook": $t("webhook"),
|
||||||
|
"smtp": $t("smtp"),
|
||||||
|
"discord": $t("discord"),
|
||||||
|
"teams": $t("teams"),
|
||||||
|
"signal": $t("signal"),
|
||||||
|
"gotify": $t("gotify"),
|
||||||
|
"slack": $t("slack"),
|
||||||
|
"rocket.chat": $t("rocket.chat"),
|
||||||
|
"pushover": $t("pushover"),
|
||||||
|
"pushy": $t("pushy"),
|
||||||
|
"octopush": $t("octopush"),
|
||||||
|
"lunasea": $t("lunasea"),
|
||||||
|
"apprise": $t("apprise"),
|
||||||
|
"pushbullet": $t("pushbullet"),
|
||||||
|
"line": $t("line"),
|
||||||
|
"mattermost": $t("mattermostt")
|
||||||
|
}[type];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import STMP from "./SMTP.vue"
|
import STMP from "./SMTP.vue";
|
||||||
import Telegram from "./Telegram.vue";
|
import Telegram from "./Telegram.vue";
|
||||||
import Discord from "./Discord.vue";
|
import Discord from "./Discord.vue";
|
||||||
import Webhook from "./Webhook.vue";
|
import Webhook from "./Webhook.vue";
|
||||||
|
@ -39,6 +39,6 @@ const NotificationFormList = {
|
||||||
"pushbullet": Pushbullet,
|
"pushbullet": Pushbullet,
|
||||||
"line": Line,
|
"line": Line,
|
||||||
"mattermost": Mattermost
|
"mattermost": Mattermost
|
||||||
}
|
};
|
||||||
|
|
||||||
export default NotificationFormList
|
export default NotificationFormList;
|
||||||
|
|
|
@ -352,16 +352,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
pingTitle(average = false) {
|
pingTitle(average = false) {
|
||||||
let translationPrefix = "";
|
|
||||||
if (average) {
|
|
||||||
translationPrefix = "Avg. ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.monitor.type === "http") {
|
if (this.monitor.type === "http") {
|
||||||
return this.$t(translationPrefix + "Response");
|
return average ? this.$t("Avg. Response") : this.$t("Response");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$t(translationPrefix + "Ping");
|
return average ? this.$t("Avg. Ping") : this.$t("Ping");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -272,7 +272,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
pageName() {
|
pageName() {
|
||||||
return this.$t((this.isAdd) ? "Add New Monitor" : "Edit");
|
return this.isAdd ? this.$t("Add New Monitor") : this.$t("Edit");
|
||||||
},
|
},
|
||||||
|
|
||||||
isAdd() {
|
isAdd() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue