forked from MapComplete/MapComplete
		
	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 ignoreTerms = ["searchTerms"]
 | 
			
		||||
 | 
			
		||||
class TranslationPart {
 | 
			
		||||
    contents: Map<string, TranslationPart | string> = new Map<string, TranslationPart | string>()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +15,8 @@ class TranslationPart {
 | 
			
		|||
        const rootTranslation = new TranslationPart()
 | 
			
		||||
        for (const file of files) {
 | 
			
		||||
            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
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -46,10 +48,6 @@ class TranslationPart {
 | 
			
		|||
            return
 | 
			
		||||
        }
 | 
			
		||||
        for (const translationsKey in translations) {
 | 
			
		||||
            if (!translations.hasOwnProperty(translationsKey)) {
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const v = translations[translationsKey]
 | 
			
		||||
            if (typeof v != "string") {
 | 
			
		||||
                console.error(
 | 
			
		||||
| 
						 | 
				
			
			@ -104,9 +102,6 @@ class TranslationPart {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        for (let key in object) {
 | 
			
		||||
            if (!object.hasOwnProperty(key)) {
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
            if (ignoreTerms.indexOf(key) >= 0) {
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -155,13 +150,13 @@ class 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[] {
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
            if (typeof value === "string") {
 | 
			
		||||
| 
						 | 
				
			
			@ -180,20 +175,20 @@ class TranslationPart {
 | 
			
		|||
        const parts = []
 | 
			
		||||
        let keys = Array.from(this.contents.keys())
 | 
			
		||||
        keys = keys.sort()
 | 
			
		||||
        for (let key of keys) {
 | 
			
		||||
        for (const key of keys) {
 | 
			
		||||
            let value = this.contents.get(key)
 | 
			
		||||
 | 
			
		||||
            if (typeof value === "string") {
 | 
			
		||||
                value = value.replace(/"/g, '\\"').replace(/\n/g, "\\n")
 | 
			
		||||
                value = value.replace(/"/g, "\\\"").replace(/\n/g, "\\n")
 | 
			
		||||
                if (neededLanguage === undefined) {
 | 
			
		||||
                    parts.push(`\"${key}\": \"${value}\"`)
 | 
			
		||||
                    parts.push(`"${key}": "${value}"`)
 | 
			
		||||
                } else if (key === neededLanguage) {
 | 
			
		||||
                    return `"${value}"`
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                const sub = (value as TranslationPart).toJson(neededLanguage)
 | 
			
		||||
                if (sub !== "") {
 | 
			
		||||
                    parts.push(`\"${key}\": ${sub}`)
 | 
			
		||||
                    parts.push(`"${key}": ${sub}`)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -234,7 +229,7 @@ class TranslationPart {
 | 
			
		|||
            } else if (!isLeaf) {
 | 
			
		||||
                errors.push({
 | 
			
		||||
                    error: "Mixed node: non-leaf node has translation strings",
 | 
			
		||||
                    path: path,
 | 
			
		||||
                    path: path
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +280,7 @@ class TranslationPart {
 | 
			
		|||
                                value +
 | 
			
		||||
                                "\n" +
 | 
			
		||||
                                fixLink,
 | 
			
		||||
                            path: path,
 | 
			
		||||
                            path: path
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                    return
 | 
			
		||||
| 
						 | 
				
			
			@ -297,7 +292,7 @@ class TranslationPart {
 | 
			
		|||
                            error: `The translation for ${key} does not have the required subpart ${part} (in ${usedByLanguage}).
 | 
			
		||||
    \tThe full translation is ${value}
 | 
			
		||||
    \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'.
 | 
			
		||||
| 
						 | 
				
			
			@ -361,9 +338,10 @@ function isTranslation(tr: any): boolean {
 | 
			
		|||
function transformTranslation(
 | 
			
		||||
    obj: any,
 | 
			
		||||
    path: string[] = [],
 | 
			
		||||
    languageWhitelist: string[] = undefined
 | 
			
		||||
    languageWhitelist: string[] = undefined,
 | 
			
		||||
    shortNotation = false
 | 
			
		||||
) {
 | 
			
		||||
    if (isTranslation(obj)) {
 | 
			
		||||
    if (GenerateTranslations.isTranslation(obj)) {
 | 
			
		||||
        return `new Translation( ${JSON.stringify(obj)} )`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -380,7 +358,7 @@ function transformTranslation(
 | 
			
		|||
        }
 | 
			
		||||
        let value = obj[key]
 | 
			
		||||
 | 
			
		||||
        if (isTranslation(value)) {
 | 
			
		||||
        if (GenerateTranslations.isTranslation(value)) {
 | 
			
		||||
            if (languageWhitelist !== undefined) {
 | 
			
		||||
                const nv = {}
 | 
			
		||||
                for (const ln of languageWhitelist) {
 | 
			
		||||
| 
						 | 
				
			
			@ -395,7 +373,7 @@ function transformTranslation(
 | 
			
		|||
                )}.${key}\n\tThe translations in other languages are ${JSON.stringify(value)}`
 | 
			
		||||
            }
 | 
			
		||||
            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}")`
 | 
			
		||||
            if (subParts !== null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -409,12 +387,16 @@ function transformTranslation(
 | 
			
		|||
                        "."
 | 
			
		||||
                    )}: A subpart contains invalid characters: ${subParts.join(", ")}`
 | 
			
		||||
                }
 | 
			
		||||
                expr = `return new TypedTranslation<{ ${types.join(", ")} }>(${JSON.stringify(
 | 
			
		||||
                expr = `new TypedTranslation<{ ${types.join(", ")} }>(${JSON.stringify(
 | 
			
		||||
                    value
 | 
			
		||||
                )}, "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 {
 | 
			
		||||
            values.push(
 | 
			
		||||
                spaces + key + ": " + transformTranslation(value, [...path, key], languageWhitelist)
 | 
			
		||||
| 
						 | 
				
			
			@ -469,54 +451,11 @@ function formatFile(path) {
 | 
			
		|||
    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'.
 | 
			
		||||
 * 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
 | 
			
		||||
| 
						 | 
				
			
			@ -608,7 +547,7 @@ function MergeTranslation(source: any, target: any, language: string, context: s
 | 
			
		|||
            if (targetV[language] !== undefined && targetV[language] !== sourceV) {
 | 
			
		||||
                was = " (overwritten " + targetV[language] + ")"
 | 
			
		||||
            }
 | 
			
		||||
            console.log("   + ", context + "." + language, "-->", sourceV, was)
 | 
			
		||||
            // console.log("   + ", context + "." + language, "-->", sourceV, was)
 | 
			
		||||
            continue
 | 
			
		||||
        }
 | 
			
		||||
        if (typeof sourceV === "object") {
 | 
			
		||||
| 
						 | 
				
			
			@ -697,7 +636,7 @@ function removeNonEnglishTranslations(object: any) {
 | 
			
		|||
            leaf["en"] = en
 | 
			
		||||
        },
 | 
			
		||||
        (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")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -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> {
 | 
			
		||||
        if (!existsSync("./langs/themes")) {
 | 
			
		||||
            mkdirSync("./langs/themes")
 | 
			
		||||
        }
 | 
			
		||||
        const themeOverwritesWeblate = args[0] === "--ignore-weblate"
 | 
			
		||||
        const englishOnly = args[0] === "--english-only"
 | 
			
		||||
        if (englishOnly) {
 | 
			
		||||
            console.log("ENGLISH ONLY")
 | 
			
		||||
        }
 | 
			
		||||
        if (!themeOverwritesWeblate) {
 | 
			
		||||
            mergeLayerTranslations()
 | 
			
		||||
            mergeThemeTranslations()
 | 
			
		||||
            compileTranslationsFromWeblate()
 | 
			
		||||
            mergeLayerTranslations(englishOnly)
 | 
			
		||||
            mergeThemeTranslations(englishOnly)
 | 
			
		||||
            this.compileTranslationsFromWeblate(englishOnly)
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log("Ignore weblate")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.detectUsedLanguages()
 | 
			
		||||
        genTranslations()
 | 
			
		||||
        this.genTranslations(englishOnly)
 | 
			
		||||
        {
 | 
			
		||||
            const allTranslationFiles = ScriptUtils.readDirRecSync("langs").filter((path) =>
 | 
			
		||||
                path.endsWith(".json")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue