mapcomplete/UI/BigComponents/TranslatorsPanel.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

199 lines
7.5 KiB
TypeScript
Raw Normal View History

2022-04-01 12:51:55 +02:00
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 { Store } from "../../Logic/UIEventSource"
2022-04-01 12:51:55 +02:00
import { SubtleButton } from "../Base/SubtleButton"
import Svg from "../../Svg"
import * as native_languages from "../../assets/language_native.json"
2022-04-15 00:10:04 +02:00
import * as used_languages from "../../assets/generated/used_languages.json"
import BaseUIElement from "../BaseUIElement"
2022-04-01 12:51:55 +02:00
class TranslatorsPanelContent extends Combine {
constructor(layout: LayoutConfig, isTranslator: Store<boolean>) {
2022-04-01 12:51:55 +02:00
const t = Translations.t.translations
const { completeness, untranslated, total } =
TranslatorsPanel.MissingTranslationsFor(layout)
2022-04-01 12:51:55 +02:00
const seed = t.completeness
for (const ln of Array.from(completeness.keys())) {
2022-04-15 00:10:04 +02:00
if (ln === "*") {
2022-04-01 12:51:55 +02:00
continue
}
if (seed.translations[ln] === undefined) {
seed.translations[ln] = seed.translations["en"]
}
}
2022-04-15 00:10:04 +02:00
2022-04-01 12:51:55 +02:00
const completenessTr = {}
const completenessPercentage = {}
seed.SupportedLanguages().forEach((ln) => {
2022-04-15 00:10:04 +02:00
completenessTr[ln] = "" + (completeness.get(ln) ?? 0)
completenessPercentage[ln] =
"" + Math.round((100 * (completeness.get(ln) ?? 0)) / total)
2022-04-01 12:51:55 +02:00
})
2022-04-15 00:10:04 +02:00
function missingTranslationsFor(language: string): BaseUIElement[] {
// e.g. "themes:<themename>.layers.0.tagRenderings..., or "layers:<layername>.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"))
2022-09-08 21:40:48 +02:00
2022-04-15 00:10:04 +02:00
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])
)
2022-09-08 21:40:48 +02:00
2022-04-15 00:10:04 +02:00
console.log(
"Getting untranslated string for",
language,
"raw:",
missingKeys,
"hasMissingTheme:",
hasMissingTheme,
"missingLayers:",
missingLayers
2022-09-08 21:40:48 +02:00
)
2022-04-15 00:10:04 +02:00
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
2022-09-08 21:40:48 +02:00
)
2022-04-15 00:10:04 +02:00
),
...missingKeys.map(
(context) =>
new Link(context, LinkToWeblate.hrefToWeblate(language, context), true)
2022-09-08 21:40:48 +02:00
),
2022-04-15 00:10:04 +02:00
]
}
2022-04-01 12:51:55 +02:00
// "translationCompleteness": "Translations for {theme} in {language} are at {percentage}: {translated} out of {total}",
2022-04-15 00:10:04 +02:00
const translated = seed.Subs({
total,
theme: layout.title,
percentage: new Translation(completenessPercentage),
translated: new Translation(completenessTr),
2022-04-15 00:10:04 +02:00
language: seed.OnEveryLanguage((_, lng) => native_languages[lng] ?? lng),
})
2022-04-15 00:10:04 +02:00
2022-04-01 12:51:55 +02:00
super([
2022-04-15 00:10:04 +02:00
new Title(Translations.t.translations.activateButton),
2022-04-01 12:51:55 +02:00
new Toggle(t.isTranslator.SetClass("thanks block"), undefined, isTranslator),
t.help,
translated,
/*Disable button:*/
new SubtleButton(undefined, t.deactivate).onClick(() => {
Locale.showLinkToWeblate.setData(false)
}),
2022-09-08 21:40:48 +02:00
2022-04-15 00:10:04 +02:00
new VariableUiElement(
Locale.language.map((ln) => {
2022-04-01 12:51:55 +02:00
const missing = missingTranslationsFor(ln)
if (missing.length === 0) {
return undefined
}
2022-04-15 00:10:04 +02:00
let title = Translations.t.translations.allMissing
if (untranslated.get(ln) !== undefined) {
title = Translations.t.translations.missing.Subs({
count: untranslated.get(ln).length,
})
}
2022-04-01 12:51:55 +02:00
return new Toggleable(
2022-04-15 00:10:04 +02:00
new Title(title),
2022-04-01 12:51:55 +02:00
new Combine(missing).SetClass("flex flex-col")
)
})
2022-09-08 21:40:48 +02:00
),
2022-04-01 12:51:55 +02:00
])
}
}
export default class TranslatorsPanel extends Toggle {
constructor(
state: { layoutToUse: LayoutConfig; isTranslator: Store<boolean> },
iconStyle?: string
) {
2022-04-01 12:51:55 +02:00
const t = Translations.t.translations
super(
2022-04-15 00:10:04 +02:00
new Lazy(
() => new TranslatorsPanelContent(state.layoutToUse, state.isTranslator)
2022-04-01 12:51:55 +02:00
).SetClass("flex flex-col"),
new SubtleButton(Svg.translate_ui().SetStyle(iconStyle), t.activateButton).onClick(() =>
Locale.showLinkToWeblate.setData(true)
),
2022-04-15 00:10:04 +02:00
Locale.showLinkToWeblate
2022-04-01 12:51:55 +02:00
)
this.SetClass("hidden-on-mobile")
}
2022-04-15 00:10:04 +02:00
public static MissingTranslationsFor(layout: LayoutConfig): {
completeness: Map<string, number>
untranslated: Map<string, string[]>
total: number
} {
let total = 0
const completeness = new Map<string, number>()
const untranslated = new Map<string, string[]>()
2022-04-15 00:10:04 +02:00
Utils.WalkObject(
layout,
(o) => {
const translation = <Translation>(<any>o)
2022-04-15 00:10:04 +02:00
if (translation.translations["*"] !== undefined) {
return
}
2022-04-15 00:10:04 +02:00
if (translation.context === undefined || translation.context.indexOf(":") < 0) {
// no source given - lets ignore
return
}
2022-09-08 21:40:48 +02:00
total++
used_languages.languages.forEach((ln) => {
const trans = translation.translations
if (trans["*"] !== undefined) {
return
}
2022-04-01 12:51:55 +02:00
if (trans[ln] === undefined) {
if (!untranslated.has(ln)) {
untranslated.set(ln, [])
2022-09-08 21:40:48 +02:00
}
untranslated.get(ln).push(translation.context)
2022-04-15 00:10:04 +02:00
} else {
completeness.set(ln, 1 + (completeness.get(ln) ?? 0))
2022-09-08 21:40:48 +02:00
}
})
},
(o) => {
if (o === undefined || o === null) {
return false
}
return o instanceof Translation
}
)
return { completeness, untranslated, total }
}
2022-04-01 12:51:55 +02:00
}