Linting of 'generate translations'
This commit is contained in:
parent
c9751a3eb5
commit
5302ec3342
1 changed files with 107 additions and 97 deletions
|
@ -6,6 +6,7 @@ import Script from "./Script"
|
||||||
|
|
||||||
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"]
|
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"]
|
||||||
const ignoreTerms = ["searchTerms"]
|
const ignoreTerms = ["searchTerms"]
|
||||||
|
|
||||||
class TranslationPart {
|
class TranslationPart {
|
||||||
contents: Map<string, TranslationPart | string> = new Map<string, TranslationPart | string>()
|
contents: Map<string, TranslationPart | string> = new Map<string, TranslationPart | string>()
|
||||||
|
|
||||||
|
@ -14,7 +15,8 @@ class TranslationPart {
|
||||||
const rootTranslation = new TranslationPart()
|
const rootTranslation = new TranslationPart()
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const content = JSON.parse(readFileSync(file, { encoding: "utf8" }))
|
const content = JSON.parse(readFileSync(file, { encoding: "utf8" }))
|
||||||
rootTranslation.addTranslation(file.substr(0, file.length - ".json".length), content)
|
const language = file.substr(0, file.length - ".json".length)
|
||||||
|
rootTranslation.addTranslation(language, content)
|
||||||
}
|
}
|
||||||
return rootTranslation
|
return rootTranslation
|
||||||
}
|
}
|
||||||
|
@ -46,10 +48,6 @@ class TranslationPart {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for (const translationsKey in translations) {
|
for (const translationsKey in translations) {
|
||||||
if (!translations.hasOwnProperty(translationsKey)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const v = translations[translationsKey]
|
const v = translations[translationsKey]
|
||||||
if (typeof v != "string") {
|
if (typeof v != "string") {
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -104,9 +102,6 @@ class TranslationPart {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let key in object) {
|
for (let key in object) {
|
||||||
if (!object.hasOwnProperty(key)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (ignoreTerms.indexOf(key) >= 0) {
|
if (ignoreTerms.indexOf(key) >= 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -155,13 +150,13 @@ class TranslationPart {
|
||||||
this.contents.set(key, new TranslationPart())
|
this.contents.set(key, new TranslationPart())
|
||||||
}
|
}
|
||||||
|
|
||||||
;(this.contents.get(key) as TranslationPart).recursiveAdd(v, context + "." + key)
|
(this.contents.get(key) as TranslationPart).recursiveAdd(v, context + "." + key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
knownLanguages(): string[] {
|
knownLanguages(): string[] {
|
||||||
const languages = []
|
const languages = []
|
||||||
for (let key of Array.from(this.contents.keys())) {
|
for (const key of Array.from(this.contents.keys())) {
|
||||||
const value = this.contents.get(key)
|
const value = this.contents.get(key)
|
||||||
|
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
|
@ -180,20 +175,20 @@ class TranslationPart {
|
||||||
const parts = []
|
const parts = []
|
||||||
let keys = Array.from(this.contents.keys())
|
let keys = Array.from(this.contents.keys())
|
||||||
keys = keys.sort()
|
keys = keys.sort()
|
||||||
for (let key of keys) {
|
for (const key of keys) {
|
||||||
let value = this.contents.get(key)
|
let value = this.contents.get(key)
|
||||||
|
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
value = value.replace(/"/g, '\\"').replace(/\n/g, "\\n")
|
value = value.replace(/"/g, "\\\"").replace(/\n/g, "\\n")
|
||||||
if (neededLanguage === undefined) {
|
if (neededLanguage === undefined) {
|
||||||
parts.push(`\"${key}\": \"${value}\"`)
|
parts.push(`"${key}": "${value}"`)
|
||||||
} else if (key === neededLanguage) {
|
} else if (key === neededLanguage) {
|
||||||
return `"${value}"`
|
return `"${value}"`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const sub = (value as TranslationPart).toJson(neededLanguage)
|
const sub = (value as TranslationPart).toJson(neededLanguage)
|
||||||
if (sub !== "") {
|
if (sub !== "") {
|
||||||
parts.push(`\"${key}\": ${sub}`)
|
parts.push(`"${key}": ${sub}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +229,7 @@ class TranslationPart {
|
||||||
} else if (!isLeaf) {
|
} else if (!isLeaf) {
|
||||||
errors.push({
|
errors.push({
|
||||||
error: "Mixed node: non-leaf node has translation strings",
|
error: "Mixed node: non-leaf node has translation strings",
|
||||||
path: path,
|
path: path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +280,7 @@ class TranslationPart {
|
||||||
value +
|
value +
|
||||||
"\n" +
|
"\n" +
|
||||||
fixLink,
|
fixLink,
|
||||||
path: path,
|
path: path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -297,7 +292,7 @@ class TranslationPart {
|
||||||
error: `The translation for ${key} does not have the required subpart ${part} (in ${usedByLanguage}).
|
error: `The translation for ${key} does not have the required subpart ${part} (in ${usedByLanguage}).
|
||||||
\tThe full translation is ${value}
|
\tThe full translation is ${value}
|
||||||
\t${fixLink}`,
|
\t${fixLink}`,
|
||||||
path: path,
|
path: path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,24 +329,6 @@ class TranslationPart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the given object only contains string-values
|
|
||||||
* @param tr
|
|
||||||
*/
|
|
||||||
function isTranslation(tr: any): boolean {
|
|
||||||
if (tr["#"] === "no-translations") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (tr["special"]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for (const key in tr) {
|
|
||||||
if (typeof tr[key] !== "string") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a translation object into something that can be added to the 'generated translations'.
|
* Converts a translation object into something that can be added to the 'generated translations'.
|
||||||
|
@ -361,9 +338,10 @@ function isTranslation(tr: any): boolean {
|
||||||
function transformTranslation(
|
function transformTranslation(
|
||||||
obj: any,
|
obj: any,
|
||||||
path: string[] = [],
|
path: string[] = [],
|
||||||
languageWhitelist: string[] = undefined
|
languageWhitelist: string[] = undefined,
|
||||||
|
shortNotation = false
|
||||||
) {
|
) {
|
||||||
if (isTranslation(obj)) {
|
if (GenerateTranslations.isTranslation(obj)) {
|
||||||
return `new Translation( ${JSON.stringify(obj)} )`
|
return `new Translation( ${JSON.stringify(obj)} )`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +358,7 @@ function transformTranslation(
|
||||||
}
|
}
|
||||||
let value = obj[key]
|
let value = obj[key]
|
||||||
|
|
||||||
if (isTranslation(value)) {
|
if (GenerateTranslations.isTranslation(value)) {
|
||||||
if (languageWhitelist !== undefined) {
|
if (languageWhitelist !== undefined) {
|
||||||
const nv = {}
|
const nv = {}
|
||||||
for (const ln of languageWhitelist) {
|
for (const ln of languageWhitelist) {
|
||||||
|
@ -395,7 +373,7 @@ function transformTranslation(
|
||||||
)}.${key}\n\tThe translations in other languages are ${JSON.stringify(value)}`
|
)}.${key}\n\tThe translations in other languages are ${JSON.stringify(value)}`
|
||||||
}
|
}
|
||||||
const subParts: string[] = value["en"].match(/{[^}]*}/g)
|
const subParts: string[] = value["en"].match(/{[^}]*}/g)
|
||||||
let expr = `return new Translation(${JSON.stringify(value)}, "core:${path.join(
|
let expr = `new Translation(${JSON.stringify(value)}, "core:${path.join(
|
||||||
"."
|
"."
|
||||||
)}.${key}")`
|
)}.${key}")`
|
||||||
if (subParts !== null) {
|
if (subParts !== null) {
|
||||||
|
@ -409,12 +387,16 @@ function transformTranslation(
|
||||||
"."
|
"."
|
||||||
)}: A subpart contains invalid characters: ${subParts.join(", ")}`
|
)}: A subpart contains invalid characters: ${subParts.join(", ")}`
|
||||||
}
|
}
|
||||||
expr = `return new TypedTranslation<{ ${types.join(", ")} }>(${JSON.stringify(
|
expr = `new TypedTranslation<{ ${types.join(", ")} }>(${JSON.stringify(
|
||||||
value
|
value
|
||||||
)}, "core:${path.join(".")}.${key}")`
|
)}, "core:${path.join(".")}.${key}")`
|
||||||
}
|
}
|
||||||
|
if (shortNotation) {
|
||||||
|
values.push(`${spaces} ${key}: ${expr}`)
|
||||||
|
|
||||||
values.push(`${spaces}get ${key}() { ${expr} }`)
|
} else {
|
||||||
|
values.push(`${spaces}get ${key}() { return ${expr} }`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
values.push(
|
values.push(
|
||||||
spaces + key + ": " + transformTranslation(value, [...path, key], languageWhitelist)
|
spaces + key + ": " + transformTranslation(value, [...path, key], languageWhitelist)
|
||||||
|
@ -469,54 +451,11 @@ function formatFile(path) {
|
||||||
writeFileSync(path, JSON.stringify(contents, null, " ") + (endsWithNewline ? "\n" : ""))
|
writeFileSync(path, JSON.stringify(contents, null, " ") + (endsWithNewline ? "\n" : ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the big compiledTranslations file
|
|
||||||
*/
|
|
||||||
function genTranslations() {
|
|
||||||
if (!fs.existsSync("./src/assets/generated/")) {
|
|
||||||
fs.mkdirSync("./src/assets/generated/")
|
|
||||||
}
|
|
||||||
const translations = JSON.parse(
|
|
||||||
fs.readFileSync("./src/assets/generated/translations.json", "utf-8")
|
|
||||||
)
|
|
||||||
const transformed = transformTranslation(translations)
|
|
||||||
|
|
||||||
let module = `import {Translation, TypedTranslation} from "../../UI/i18n/Translation"\n\nexport default class CompiledTranslations {\n\n`
|
|
||||||
module += " public static t = " + transformed
|
|
||||||
module += "\n }"
|
|
||||||
|
|
||||||
fs.writeFileSync("./src/assets/generated/CompiledTranslations.ts", module)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads 'lang/*.json', writes them into to 'assets/generated/translations.json'.
|
* Reads 'lang/*.json', writes them into to 'assets/generated/translations.json'.
|
||||||
* This is only for the core translations
|
* This is only for the core translations
|
||||||
*/
|
*/
|
||||||
function compileTranslationsFromWeblate() {
|
|
||||||
const translations = ScriptUtils.readDirRecSync("./langs", 1).filter(
|
|
||||||
(path) => path.indexOf(".json") > 0
|
|
||||||
)
|
|
||||||
|
|
||||||
const allTranslations = new TranslationPart()
|
|
||||||
|
|
||||||
allTranslations.validateStrict()
|
|
||||||
|
|
||||||
for (const translationFile of translations) {
|
|
||||||
try {
|
|
||||||
const contents = JSON.parse(readFileSync(translationFile, "utf-8"))
|
|
||||||
let language = translationFile.substring(translationFile.lastIndexOf("/") + 1)
|
|
||||||
language = language.substring(0, language.length - 5)
|
|
||||||
allTranslations.add(language, contents)
|
|
||||||
} catch (e) {
|
|
||||||
throw "Could not read file " + translationFile + " due to " + e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFileSync(
|
|
||||||
"./src/assets/generated/translations.json",
|
|
||||||
JSON.stringify(JSON.parse(allTranslations.toJson()), null, " ")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the strings out of the layers; writes them onto the weblate paths
|
* Get all the strings out of the layers; writes them onto the weblate paths
|
||||||
|
@ -608,7 +547,7 @@ function MergeTranslation(source: any, target: any, language: string, context: s
|
||||||
if (targetV[language] !== undefined && targetV[language] !== sourceV) {
|
if (targetV[language] !== undefined && targetV[language] !== sourceV) {
|
||||||
was = " (overwritten " + targetV[language] + ")"
|
was = " (overwritten " + targetV[language] + ")"
|
||||||
}
|
}
|
||||||
console.log(" + ", context + "." + language, "-->", sourceV, was)
|
// console.log(" + ", context + "." + language, "-->", sourceV, was)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (typeof sourceV === "object") {
|
if (typeof sourceV === "object") {
|
||||||
|
@ -697,7 +636,7 @@ function removeNonEnglishTranslations(object: any) {
|
||||||
leaf["en"] = en
|
leaf["en"] = en
|
||||||
},
|
},
|
||||||
(possibleLeaf) =>
|
(possibleLeaf) =>
|
||||||
possibleLeaf !== null && typeof possibleLeaf === "object" && isTranslation(possibleLeaf)
|
possibleLeaf !== null && typeof possibleLeaf === "object" && GenerateTranslations.isTranslation(possibleLeaf)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,6 +671,25 @@ class GenerateTranslations extends Script {
|
||||||
super("Syncs translations from/to the theme and layer files")
|
super("Syncs translations from/to the theme and layer files")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the given object only contains string-values
|
||||||
|
* @param tr
|
||||||
|
*/
|
||||||
|
static isTranslation(tr: Record<string, string | object>): boolean {
|
||||||
|
if (tr["#"] === "no-translations") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (tr["special"]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (const key in tr) {
|
||||||
|
if (typeof tr[key] !== "string") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OUtputs the 'used_languages.json'-file
|
* OUtputs the 'used_languages.json'-file
|
||||||
*/
|
*/
|
||||||
|
@ -754,22 +712,74 @@ class GenerateTranslations extends Script {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the big compiledTranslations file based on 'translations.json'
|
||||||
|
*/
|
||||||
|
genTranslations(englishOnly?: boolean) {
|
||||||
|
if (!fs.existsSync("./src/assets/generated/")) {
|
||||||
|
fs.mkdirSync("./src/assets/generated/")
|
||||||
|
}
|
||||||
|
const translations = JSON.parse(
|
||||||
|
fs.readFileSync("./src/assets/generated/translations.json", "utf-8")
|
||||||
|
)
|
||||||
|
const transformed = transformTranslation(translations, undefined, englishOnly ? ["en"] : undefined, englishOnly)
|
||||||
|
|
||||||
|
let module = `import {Translation, TypedTranslation} from "../../UI/i18n/Translation"\n\nexport default class CompiledTranslations {\n\n`
|
||||||
|
module += " public static t = " + transformed
|
||||||
|
module += "\n }"
|
||||||
|
|
||||||
|
fs.writeFileSync("./src/assets/generated/CompiledTranslations.ts", module)
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTranslationsFromWeblate(englishOnly: boolean) {
|
||||||
|
const translations = ScriptUtils.readDirRecSync("./langs", 1).filter(
|
||||||
|
(path) => path.indexOf(".json") > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const allTranslations = new TranslationPart()
|
||||||
|
|
||||||
|
allTranslations.validateStrict()
|
||||||
|
|
||||||
|
for (const translationFile of translations) {
|
||||||
|
try {
|
||||||
|
const contents = JSON.parse(readFileSync(translationFile, "utf-8"))
|
||||||
|
let language = translationFile.substring(translationFile.lastIndexOf("/") + 1)
|
||||||
|
language = language.substring(0, language.length - 5)
|
||||||
|
if (englishOnly && language !== "en") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allTranslations.add(language, contents)
|
||||||
|
} catch (e) {
|
||||||
|
throw "Could not read file " + translationFile + " due to " + e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
"./src/assets/generated/translations.json",
|
||||||
|
JSON.stringify(JSON.parse(allTranslations.toJson()), null, " ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async main(args: string[]): Promise<void> {
|
async main(args: string[]): Promise<void> {
|
||||||
if (!existsSync("./langs/themes")) {
|
if (!existsSync("./langs/themes")) {
|
||||||
mkdirSync("./langs/themes")
|
mkdirSync("./langs/themes")
|
||||||
}
|
}
|
||||||
const themeOverwritesWeblate = args[0] === "--ignore-weblate"
|
const themeOverwritesWeblate = args[0] === "--ignore-weblate"
|
||||||
const englishOnly = args[0] === "--english-only"
|
const englishOnly = args[0] === "--english-only"
|
||||||
|
if (englishOnly) {
|
||||||
|
console.log("ENGLISH ONLY")
|
||||||
|
}
|
||||||
if (!themeOverwritesWeblate) {
|
if (!themeOverwritesWeblate) {
|
||||||
mergeLayerTranslations()
|
mergeLayerTranslations(englishOnly)
|
||||||
mergeThemeTranslations()
|
mergeThemeTranslations(englishOnly)
|
||||||
compileTranslationsFromWeblate()
|
this.compileTranslationsFromWeblate(englishOnly)
|
||||||
} else {
|
} else {
|
||||||
console.log("Ignore weblate")
|
console.log("Ignore weblate")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.detectUsedLanguages()
|
this.detectUsedLanguages()
|
||||||
genTranslations()
|
this.genTranslations(englishOnly)
|
||||||
{
|
{
|
||||||
const allTranslationFiles = ScriptUtils.readDirRecSync("langs").filter((path) =>
|
const allTranslationFiles = ScriptUtils.readDirRecSync("langs").filter((path) =>
|
||||||
path.endsWith(".json")
|
path.endsWith(".json")
|
||||||
|
|
Loading…
Reference in a new issue