From 7d7abc303be0604d46394d9f7da77a2ec3a273ea Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 15 Apr 2022 00:10:04 +0200 Subject: [PATCH] Better translators panel --- UI/Base/LinkToWeblate.ts | 7 +++ UI/BigComponents/TranslatorsPanel.ts | 80 +++++++++++++++++----------- langs/en.json | 1 + 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/UI/Base/LinkToWeblate.ts b/UI/Base/LinkToWeblate.ts index d088658ab..1239409a2 100644 --- a/UI/Base/LinkToWeblate.ts +++ b/UI/Base/LinkToWeblate.ts @@ -4,6 +4,7 @@ import Link from "./Link"; import Svg from "../../Svg"; export default class LinkToWeblate extends VariableUiElement { + private static URI: any; constructor(context: string, availableTranslations: object) { super( Locale.language.map(ln => { if (Locale.showLinkToWeblate.data === false) { @@ -36,4 +37,10 @@ export default class LinkToWeblate extends VariableUiElement { const baseUrl = "https://hosted.weblate.org/translate/mapcomplete/" return baseUrl + category + "/" + language + "/?offset=1&q=context%3A%3D%22" + key + "%22" } + + public static hrefToWeblateZen(language: string, category: string, searchKey: string): string{ + const baseUrl = "https://hosted.weblate.org/zen/mapcomplete/" + // ?offset=1&q=+state%3A%3Ctranslated+context%3Acampersite&sort_by=-priority%2Cposition&checksum= + return baseUrl + category + "/" + language + "?offset=1&q=+state%3A%3Ctranslated+context%3A"+encodeURIComponent(searchKey)+"&sort_by=-priority%2Cposition&checksum=" + } } \ No newline at end of file diff --git a/UI/BigComponents/TranslatorsPanel.ts b/UI/BigComponents/TranslatorsPanel.ts index 311f7527f..37e80eadb 100644 --- a/UI/BigComponents/TranslatorsPanel.ts +++ b/UI/BigComponents/TranslatorsPanel.ts @@ -15,6 +15,8 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import {SubtleButton} from "../Base/SubtleButton"; import Svg from "../../Svg"; import * as native_languages from "../../assets/language_native.json" +import * as used_languages from "../../assets/generated/used_languages.json" +import BaseUIElement from "../BaseUIElement"; class TranslatorsPanelContent extends Combine { constructor(layout: LayoutConfig, isTranslator: UIEventSource) { @@ -24,37 +26,53 @@ class TranslatorsPanelContent extends Combine { const seed = t.completeness for (const ln of Array.from(completeness.keys())) { - if(ln === "*"){ + 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) + 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) ?? []) - .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)) + function missingTranslationsFor(language: string): BaseUIElement[] { + // e.g. "themes:.layers.0.tagRenderings..., or "layers:.description + const missingKeys = Utils.NoNull(untranslated.get(language) ?? []) + .filter(ctx => ctx.indexOf(":") >= 0) + .map(ctx => ctx.replace(/note_import_[a-zA-Z0-9_]*/, "note_import")) + + const hasMissingTheme = missingKeys.some(k => k.startsWith("themes:")) + const missingLayers = Utils.Dedup( missingKeys.filter(k => k.startsWith("layers:")) + .map(k => k.slice("layers:".length).split(".")[0])) + + console.log("Getting untranslated string for",language,"raw:",missingKeys,"hasMissingTheme:",hasMissingTheme,"missingLayers:",missingLayers) + return [ + hasMissingTheme ? new Link("themes:" + layout.id + ".* (zen mode)", LinkToWeblate.hrefToWeblateZen(language, "themes", layout.id), true) : undefined, + ...missingLayers.map(id => new Link("layer:" + id + ".* (zen mode)", LinkToWeblate.hrefToWeblateZen(language, "layers", id), true)), + ...missingKeys.map(context => new Link(context, LinkToWeblate.hrefToWeblate(language, context), true)) + ] + } + // + // // "translationCompleteness": "Translations for {theme} in {language} are at {percentage}: {translated} out of {total}", - const translated = seed.Subs({total, theme: layout.title, + const translated = seed.Subs({ + total, theme: layout.title, percentage: new Translation(completenessPercentage), translated: new Translation(completenessTr), - language: seed.OnEveryLanguage((_, lng) => native_languages[lng]) + language: seed.OnEveryLanguage((_, lng) => native_languages[lng] ?? lng) }) - + super([ new Title( - Translations.t.translations.activateButton, + Translations.t.translations.activateButton, ), new Toggle(t.isTranslator.SetClass("thanks block"), undefined, isTranslator), t.help, @@ -64,15 +82,18 @@ class TranslatorsPanelContent extends Combine { .onClick(() => { Locale.showLinkToWeblate.setData(false) }), - - new VariableUiElement(Locale.language.map(ln => { + new VariableUiElement(Locale.language.map(ln => { const missing = missingTranslationsFor(ln) if (missing.length === 0) { return undefined } + let title = Translations.t.translations.allMissing; + if(untranslated.get(ln) !== undefined){ + title = Translations.t.translations.missing.Subs({count: untranslated.get(ln).length}) + } return new Toggleable( - new Title(Translations.t.translations.missing.Subs({count: missing.length})), + new Title(title), new Combine(missing).SetClass("flex flex-col") ) })) @@ -84,38 +105,37 @@ class TranslatorsPanelContent extends Combine { export default class TranslatorsPanel extends Toggle { - + constructor(state: { layoutToUse: LayoutConfig, isTranslator: UIEventSource }, iconStyle?: string) { const t = Translations.t.translations super( - new Lazy(() => new TranslatorsPanelContent(state.layoutToUse, state.isTranslator) + 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 + Locale.showLinkToWeblate ) this.SetClass("hidden-on-mobile") - + } - public static MissingTranslationsFor(layout: LayoutConfig) : {completeness: Map, untranslated: Map, total: number} { + public static MissingTranslationsFor(layout: LayoutConfig): { completeness: Map, untranslated: Map, total: number } { let total = 0 const completeness = new Map() const untranslated = new Map() + Utils.WalkObject(layout, (o, path) => { const translation = o; - if(translation.translations["*"] !== undefined){ + if (translation.translations["*"] !== undefined) { return } - if(translation.context === undefined || translation.context.indexOf(":") < 0){ + if (translation.context === undefined || translation.context.indexOf(":") < 0) { // no source given - lets ignore return } - - for (const lang of translation.SupportedLanguages()) { - completeness.set(lang, 1 + (completeness.get(lang) ?? 0)) - } - layout.title.SupportedLanguages().forEach(ln => { + + total ++ + used_languages.languages.forEach(ln => { const trans = translation.translations if (trans["*"] !== undefined) { return; @@ -125,11 +145,11 @@ export default class TranslatorsPanel extends Toggle { untranslated.set(ln, []) } untranslated.get(ln).push(translation.context) + }else{ + completeness.set(ln, 1 + (completeness.get(ln) ?? 0)) } }) - if(translation.translations["*"] === undefined){ - total++ - } + }, o => { if (o === undefined || o === null) { return false; diff --git a/langs/en.json b/langs/en.json index e4d501476..7719f7938 100644 --- a/langs/en.json +++ b/langs/en.json @@ -532,6 +532,7 @@ }, "translations": { "activateButton": "Help to translate MapComplete", + "allMissing": "No translations yet", "completeness": "Translations for {theme} in {language} are at {percentage}%: {translated} strings out of {total} are translated", "deactivate": "Disable translation buttons", "help": "Click the 'translate'-icon next to a string to enter or update a piece of text. You need a Weblate-account for this. Create one with your OSM-username to automatically unlock translation mode.",