Add some statistics on translations in script
This commit is contained in:
parent
796ee40f3b
commit
e22ce4d5b1
3 changed files with 135 additions and 73 deletions
|
@ -19,11 +19,97 @@ import Svg from "../../Svg";
|
||||||
class TranslatorsPanelContent extends Combine {
|
class TranslatorsPanelContent extends Combine {
|
||||||
constructor(layout: LayoutConfig, isTranslator: UIEventSource<boolean>) {
|
constructor(layout: LayoutConfig, isTranslator: UIEventSource<boolean>) {
|
||||||
const t = Translations.t.translations
|
const t = Translations.t.translations
|
||||||
const completeness = new Map<string, number>()
|
|
||||||
|
const {completeness, untranslated, total} = TranslatorsPanel.MissingTranslationsFor(layout)
|
||||||
|
|
||||||
|
const seed = t.completeness
|
||||||
|
for (const ln of Array.from(completeness.keys())) {
|
||||||
|
if(ln === "*"){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (seed.translations[ln] === undefined) {
|
||||||
|
seed.translations[ln] = seed.translations["en"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const completenessTr = {}
|
||||||
|
const completenessPercentage = {}
|
||||||
|
seed.SupportedLanguages().forEach(ln => {
|
||||||
|
completenessTr[ln] = ""+(completeness.get(ln) ?? 0)
|
||||||
|
completenessPercentage[ln] = ""+Math.round(100 * (completeness.get(ln) ?? 0) / total)
|
||||||
|
})
|
||||||
|
|
||||||
|
const missingTranslationsFor = (ln: string) => Utils.NoNull(untranslated.get(ln) ?? [])
|
||||||
|
.map(ctx => ctx.replace(/note_import_[a-zA-Z0-9_]*/, "note_import"))
|
||||||
|
.map(context => new Link(context, LinkToWeblate.hrefToWeblate(ln, context), true))
|
||||||
|
|
||||||
|
|
||||||
|
// "translationCompleteness": "Translations for {theme} in {language} are at {percentage}: {translated} out of {total}",
|
||||||
|
const translated = seed.Subs({total, theme: layout.title,
|
||||||
|
percentage: new Translation(completenessPercentage),
|
||||||
|
translated: new Translation(completenessTr)
|
||||||
|
})
|
||||||
|
|
||||||
|
super([
|
||||||
|
new Title(
|
||||||
|
Translations.t.translations.activateButton,
|
||||||
|
),
|
||||||
|
new Toggle(t.isTranslator.SetClass("thanks block"), undefined, isTranslator),
|
||||||
|
t.help,
|
||||||
|
translated,
|
||||||
|
/*Disable button:*/
|
||||||
|
new SubtleButton(undefined, t.deactivate)
|
||||||
|
.onClick(() => {
|
||||||
|
Locale.showLinkToWeblate.setData(false)
|
||||||
|
}),
|
||||||
|
|
||||||
|
new VariableUiElement(Locale.language.map(ln => {
|
||||||
|
|
||||||
|
const missing = missingTranslationsFor(ln)
|
||||||
|
if (missing.length === 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return new Toggleable(
|
||||||
|
new Title(Translations.t.translations.missing.Subs({count: missing.length})),
|
||||||
|
new Combine(missing).SetClass("flex flex-col")
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TranslatorsPanel extends Toggle {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(state: { layoutToUse: LayoutConfig, isTranslator: UIEventSource<boolean> }, iconStyle?: string) {
|
||||||
|
const t = Translations.t.translations
|
||||||
|
super(
|
||||||
|
new Lazy(() => new TranslatorsPanelContent(state.layoutToUse, state.isTranslator)
|
||||||
|
).SetClass("flex flex-col"),
|
||||||
|
new SubtleButton(Svg.translate_ui().SetStyle(iconStyle), t.activateButton).onClick(() => Locale.showLinkToWeblate.setData(true)),
|
||||||
|
Locale.showLinkToWeblate
|
||||||
|
)
|
||||||
|
this.SetClass("hidden-on-mobile")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static MissingTranslationsFor(layout: LayoutConfig) : {completeness: Map<string, number>, untranslated: Map<string, string[]>, total: number} {
|
||||||
let total = 0
|
let total = 0
|
||||||
|
const completeness = new Map<string, number>()
|
||||||
const untranslated = new Map<string, string[]>()
|
const untranslated = new Map<string, string[]>()
|
||||||
Utils.WalkObject(layout, (o, path) => {
|
Utils.WalkObject(layout, (o, path) => {
|
||||||
const translation = <Translation><any>o;
|
const translation = <Translation><any>o;
|
||||||
|
if(translation.translations["*"] !== undefined){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(translation.context === undefined || translation.context.indexOf(":") < 0){
|
||||||
|
// no source given - lets ignore
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for (const lang of translation.SupportedLanguages()) {
|
for (const lang of translation.SupportedLanguages()) {
|
||||||
completeness.set(lang, 1 + (completeness.get(lang) ?? 0))
|
completeness.set(lang, 1 + (completeness.get(lang) ?? 0))
|
||||||
}
|
}
|
||||||
|
@ -49,76 +135,6 @@ class TranslatorsPanelContent extends Combine {
|
||||||
return o instanceof Translation;
|
return o instanceof Translation;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return {completeness, untranslated, total}
|
||||||
const seed = t.completeness
|
|
||||||
for (const ln of Array.from(completeness.keys())) {
|
|
||||||
if(ln === "*"){
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (seed.translations[ln] === undefined) {
|
|
||||||
seed.translations[ln] = seed.translations["en"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const completenessTr = {}
|
|
||||||
const completenessPercentage = {}
|
|
||||||
seed.SupportedLanguages().forEach(ln => {
|
|
||||||
completenessTr[ln] = ""+(completeness.get(ln) ?? 0)
|
|
||||||
completenessPercentage[ln] = ""+Math.round(100 * (completeness.get(ln) ?? 0) / total)
|
|
||||||
})
|
|
||||||
|
|
||||||
// "translationCompleteness": "Translations for {theme} in {language} are at {percentage}: {translated} out of {total}",
|
|
||||||
const translated = seed.Subs({total, theme: layout.title,
|
|
||||||
percentage: new Translation(completenessPercentage),
|
|
||||||
translated: new Translation(completenessTr)
|
|
||||||
})
|
|
||||||
|
|
||||||
const missingTranslationsFor = (ln: string) => Utils.NoNull(untranslated.get(ln) ?? [])
|
|
||||||
.filter(ctx => ctx.indexOf(':') > 0)
|
|
||||||
.map(ctx => ctx.replace(/note_import_[a-zA-Z0-9_]*/, "note_import"))
|
|
||||||
.map(context => new Link(context, LinkToWeblate.hrefToWeblate(ln, context), true))
|
|
||||||
|
|
||||||
const disable = new SubtleButton(undefined, t.deactivate)
|
|
||||||
.onClick(() => {
|
|
||||||
Locale.showLinkToWeblate.setData(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
super([
|
|
||||||
new Title(
|
|
||||||
Translations.t.translations.activateButton,
|
|
||||||
),
|
|
||||||
new Toggle(t.isTranslator.SetClass("thanks block"), undefined, isTranslator),
|
|
||||||
t.help,
|
|
||||||
translated,
|
|
||||||
disable,
|
|
||||||
new VariableUiElement(Locale.language.map(ln => {
|
|
||||||
|
|
||||||
const missing = missingTranslationsFor(ln)
|
|
||||||
if (missing.length === 0) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
return new Toggleable(
|
|
||||||
new Title(Translations.t.translations.missing.Subs({count: missing.length})),
|
|
||||||
new Combine(missing).SetClass("flex flex-col")
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
])
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class TranslatorsPanel extends Toggle {
|
|
||||||
|
|
||||||
|
|
||||||
constructor(state: { layoutToUse: LayoutConfig, isTranslator: UIEventSource<boolean> }, iconStyle?: string) {
|
|
||||||
const t = Translations.t.translations
|
|
||||||
super(
|
|
||||||
new Lazy(() => new TranslatorsPanelContent(state.layoutToUse, state.isTranslator)
|
|
||||||
).SetClass("flex flex-col"),
|
|
||||||
new SubtleButton(Svg.translate_ui().SetStyle(iconStyle), t.activateButton).onClick(() => Locale.showLinkToWeblate.setData(true)),
|
|
||||||
Locale.showLinkToWeblate
|
|
||||||
)
|
|
||||||
this.SetClass("hidden-on-mobile")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
Utils.ts
8
Utils.ts
|
@ -183,6 +183,14 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
}
|
}
|
||||||
return str.substr(0, l - 3) + "...";
|
return str.substr(0, l - 3) + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FixedLength(str: string, l: number) {
|
||||||
|
str = Utils.EllipsesAfter(str, l)
|
||||||
|
while(str.length < l){
|
||||||
|
str = " "+str
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
public static Dedup(arr: string[]): string[] {
|
public static Dedup(arr: string[]): string[] {
|
||||||
if (arr === undefined) {
|
if (arr === undefined) {
|
||||||
|
|
|
@ -2,6 +2,8 @@ import * as fs from "fs";
|
||||||
import {readFileSync, writeFileSync} from "fs";
|
import {readFileSync, writeFileSync} from "fs";
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
import ScriptUtils from "./ScriptUtils";
|
import ScriptUtils from "./ScriptUtils";
|
||||||
|
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||||
|
import TranslatorsPanel from "../UI/BigComponents/TranslatorsPanel";
|
||||||
|
|
||||||
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"];
|
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"];
|
||||||
|
|
||||||
|
@ -534,7 +536,7 @@ const l1 = generateTranslationsObjectFrom(ScriptUtils.getLayerFiles(), "layers")
|
||||||
const l2 = generateTranslationsObjectFrom(ScriptUtils.getThemeFiles().filter(th => th.parsed.mustHaveLanguage === undefined), "themes")
|
const l2 = generateTranslationsObjectFrom(ScriptUtils.getThemeFiles().filter(th => th.parsed.mustHaveLanguage === undefined), "themes")
|
||||||
const l3 = generateTranslationsObjectFrom([{path: questionsPath, parsed: questionsParsed}], "shared-questions")
|
const l3 = generateTranslationsObjectFrom([{path: questionsPath, parsed: questionsParsed}], "shared-questions")
|
||||||
|
|
||||||
const usedLanguages = Utils.Dedup(l1.concat(l2).concat(l3)).filter(v => v !== "*")
|
const usedLanguages: string[] = Utils.Dedup(l1.concat(l2).concat(l3)).filter(v => v !== "*")
|
||||||
usedLanguages.sort()
|
usedLanguages.sort()
|
||||||
fs.writeFileSync("./assets/generated/used_languages.json", JSON.stringify({languages: usedLanguages}))
|
fs.writeFileSync("./assets/generated/used_languages.json", JSON.stringify({languages: usedLanguages}))
|
||||||
|
|
||||||
|
@ -555,3 +557,39 @@ TranslationPart.fromDirectory("./langs").validateStrict("./langs")
|
||||||
TranslationPart.fromDirectory("./langs/layers").validateStrict("layers")
|
TranslationPart.fromDirectory("./langs/layers").validateStrict("layers")
|
||||||
TranslationPart.fromDirectory("./langs/themes").validateStrict("themes")
|
TranslationPart.fromDirectory("./langs/themes").validateStrict("themes")
|
||||||
TranslationPart.fromDirectory("./langs/shared-questions").validateStrict("shared-questions")
|
TranslationPart.fromDirectory("./langs/shared-questions").validateStrict("shared-questions")
|
||||||
|
|
||||||
|
|
||||||
|
// Some statistics
|
||||||
|
console.log(Utils.FixedLength("",12)+" "+usedLanguages.map(l => Utils.FixedLength(l, 6)).join(""))
|
||||||
|
const all = new Map<string, number[]>()
|
||||||
|
|
||||||
|
usedLanguages.forEach(ln => all.set(ln, []))
|
||||||
|
|
||||||
|
for (const layoutId of Array.from(AllKnownLayouts.allKnownLayouts.keys())) {
|
||||||
|
const layout = AllKnownLayouts.allKnownLayouts.get(layoutId)
|
||||||
|
|
||||||
|
const {completeness, total} = TranslatorsPanel.MissingTranslationsFor(layout)
|
||||||
|
process.stdout.write(Utils.FixedLength(layout.id, 12)+" ")
|
||||||
|
for (const language of usedLanguages) {
|
||||||
|
const compl = completeness.get(language)
|
||||||
|
all.get(language).push((compl ?? 0) / total)
|
||||||
|
if(compl === undefined){
|
||||||
|
process.stdout.write(" ")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const percentage = Math.round(100 * compl / total)
|
||||||
|
process.stdout.write(Utils.FixedLength(percentage+"%", 6))
|
||||||
|
}
|
||||||
|
process.stdout.write("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(Utils.FixedLength("average", 12)+" ")
|
||||||
|
for (const language of usedLanguages) {
|
||||||
|
const ratios = all.get(language)
|
||||||
|
let sum = 0
|
||||||
|
ratios.forEach(x => sum += x)
|
||||||
|
const percentage = Math.round(100 * (sum / ratios.length))
|
||||||
|
process.stdout.write(Utils.FixedLength(percentage+"% ", 6))
|
||||||
|
}
|
||||||
|
process.stdout.write("\n")
|
||||||
|
console.log(Utils.FixedLength("",12)+" "+usedLanguages.map(l => Utils.FixedLength(l, 6)).join(""))
|
||||||
|
|
Loading…
Reference in a new issue