import Toggle from "../Input/Toggle"; import Lazy from "../Base/Lazy"; import {Utils} from "../../Utils"; import Translations from "../i18n/Translations"; import Combine from "../Base/Combine"; import Locale from "../i18n/Locale"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {Translation} from "../i18n/Translation"; import {VariableUiElement} from "../Base/VariableUIElement"; import Link from "../Base/Link"; import LinkToWeblate from "../Base/LinkToWeblate"; import Toggleable from "../Base/Toggleable"; import Title from "../Base/Title"; import {UIEventSource} from "../../Logic/UIEventSource"; import {SubtleButton} from "../Base/SubtleButton"; import Svg from "../../Svg"; class TranslatorsPanelContent extends Combine { constructor(layout: LayoutConfig, isTranslator: UIEventSource) { const t = Translations.t.translations 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) ?? []) .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)) // "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 }, 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, 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){ return } 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 => { const trans = translation.translations if (trans["*"] !== undefined) { return; } if (trans[ln] === undefined) { if (!untranslated.has(ln)) { untranslated.set(ln, []) } untranslated.get(ln).push(translation.context) } }) if(translation.translations["*"] === undefined){ total++ } }, o => { if (o === undefined || o === null) { return false; } return o instanceof Translation; }) return {completeness, untranslated, total} } }