forked from MapComplete/MapComplete
UI: fix #1848, explicitly add element to help screen readers pronounce everything correctly
This commit is contained in:
parent
cecfaebf5b
commit
9832aa45f0
6 changed files with 94 additions and 35 deletions
|
@ -6,19 +6,25 @@
|
|||
import WeblateLink from "./WeblateLink.svelte"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import FromHtml from "./FromHtml.svelte"
|
||||
import { Utils } from "../../Utils"
|
||||
|
||||
export let t: Translation
|
||||
export let cls: string = ""
|
||||
// Text for the current language
|
||||
let txt: Store<string | undefined> = t?.current
|
||||
let lang = t?.currentLang
|
||||
$: {
|
||||
txt = t?.current
|
||||
lang = t?.currentLang
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if $txt}
|
||||
<span class={cls}>
|
||||
<FromHtml src={$txt} />
|
||||
<span lang={$lang}>
|
||||
{@html Utils.purify($txt)}
|
||||
</span>
|
||||
<WeblateLink context={t?.context} />
|
||||
</span>
|
||||
{/if}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import Locale from "../../i18n/Locale"
|
||||
import type {
|
||||
RenderingSpecification,
|
||||
SpecialVisualizationState,
|
||||
SpecialVisualizationState
|
||||
} from "../../SpecialVisualization"
|
||||
import { Utils } from "../../../Utils.js"
|
||||
import type { Feature } from "geojson"
|
||||
|
@ -25,11 +25,13 @@
|
|||
export let layer: LayerConfig | undefined
|
||||
|
||||
let language = Locale.language
|
||||
let lang = t.actualLanguage($language)
|
||||
let txt: string = t.textFor($language)
|
||||
let specs: RenderingSpecification[] = []
|
||||
$: {
|
||||
try {
|
||||
txt = t.textFor($language)
|
||||
lang = t.actualLanguage($language)
|
||||
if (txt !== undefined) {
|
||||
let key = "cached_special_spec_" + $language
|
||||
specs = t[key]
|
||||
|
@ -61,13 +63,15 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
{#each specs as specpart}
|
||||
{#if typeof specpart === "string"}
|
||||
<span>
|
||||
<FromHtml src={Utils.SubstituteKeys(specpart, $tags)} />
|
||||
<WeblateLink context={t.context} />
|
||||
</span>
|
||||
{:else if $tags !== undefined}
|
||||
<ToSvelte construct={() => createVisualisation(specpart)} />
|
||||
{/if}
|
||||
{/each}
|
||||
<span {lang}>
|
||||
{#each specs as specpart}
|
||||
{#if typeof specpart === "string"}
|
||||
<span>
|
||||
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags)) }
|
||||
<WeblateLink context={t.context} />
|
||||
</span>
|
||||
{:else if $tags !== undefined}
|
||||
<ToSvelte construct={() => createVisualisation(specpart)} />
|
||||
{/if}
|
||||
{/each}
|
||||
</span>
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { Translation } from "./i18n/Translation"
|
||||
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
||||
|
||||
const m = new Map<string, string>()
|
||||
m.set("nl", "Nederlands")
|
||||
const tr = Translation.fromMap(m, true)
|
||||
</script>
|
||||
|
||||
<LanguagePicker/>
|
||||
<h1>Svelte native</h1>
|
||||
<Tr t={tr}/>
|
||||
<h1>ToSvelte</h1>
|
||||
<ToSvelte construct={tr}/>
|
||||
|
|
|
@ -118,6 +118,7 @@ export default class WikidataPreviewBox extends VariableUiElement {
|
|||
return new FixedUiElement(maybeWikidata["error"]).SetClass("alert")
|
||||
}
|
||||
const wikidata = <WikidataResponse>maybeWikidata["success"]
|
||||
console.log(">>>> got wikidata", wikidata)
|
||||
return WikidataPreviewBox.WikidataResponsePreview(wikidata, options)
|
||||
})
|
||||
)
|
||||
|
@ -131,6 +132,8 @@ export default class WikidataPreviewBox extends VariableUiElement {
|
|||
extraItems?: (BaseUIElement | string)[]
|
||||
}
|
||||
): BaseUIElement {
|
||||
console.log(">>> constructing wikidata preview box", wikidata.labels)
|
||||
|
||||
const link = new Link(
|
||||
new Combine([
|
||||
wikidata.id,
|
||||
|
@ -143,13 +146,12 @@ export default class WikidataPreviewBox extends VariableUiElement {
|
|||
Wikidata.IdToArticle(wikidata.id),
|
||||
true
|
||||
)?.SetClass("must-link")
|
||||
|
||||
let info = new Combine([
|
||||
new Combine([
|
||||
Translation.fromMap(wikidata.labels)?.SetClass("font-bold"),
|
||||
link,
|
||||
]).SetClass("flex justify-between flex-wrap-reverse"),
|
||||
Translation.fromMap(wikidata.descriptions),
|
||||
Translation.fromMap(wikidata.descriptions, true),
|
||||
WikidataPreviewBox.QuickFacts(wikidata, options),
|
||||
...(options?.extraItems ?? []),
|
||||
]).SetClass("flex flex-col link-underline")
|
||||
|
|
|
@ -10,9 +10,21 @@ export class Translation extends BaseUIElement {
|
|||
public readonly translations: Record<string, string>
|
||||
public readonly context?: string
|
||||
private onDestroy: () => void
|
||||
/**
|
||||
* If a text is needed to display and is not available in the requested language,
|
||||
* it will default to english and - if this is not available - give any language it has available.
|
||||
*
|
||||
* If strictLanguages is set, it'll return undefined instead
|
||||
* @private
|
||||
*/
|
||||
private _strictLanguages: boolean
|
||||
|
||||
constructor(translations: string | Record<string, string>, context?: string) {
|
||||
constructor(translations: string | Record<string, string>, context?: string, strictLanguages?: boolean) {
|
||||
super()
|
||||
this._strictLanguages = strictLanguages
|
||||
if(strictLanguages){
|
||||
console.log(">>> strict:", translations)
|
||||
}
|
||||
if (translations === undefined) {
|
||||
console.error("Translation without content at " + context)
|
||||
throw `Translation without content (${context})`
|
||||
|
@ -67,17 +79,29 @@ export class Translation extends BaseUIElement {
|
|||
}
|
||||
|
||||
private _current: Store<string>
|
||||
private _currentLanguage: Store<string>
|
||||
|
||||
get current(): Store<string> {
|
||||
if (!this._current) {
|
||||
this._current = Locale.language.map(
|
||||
(l) => this.textFor(l),
|
||||
/**
|
||||
* Indicates what language is effectively returned by `current`.
|
||||
* In most cases, this will be the language of choice, but if no translation is available, this will probably be `en`
|
||||
*/
|
||||
get currentLang(): Store<string>{
|
||||
if (!this._currentLanguage) {
|
||||
this._currentLanguage = Locale.language.map(
|
||||
(l) => this.actualLanguage(l),
|
||||
[],
|
||||
(f) => {
|
||||
this.onDestroy = f
|
||||
}
|
||||
)
|
||||
}
|
||||
return this._currentLanguage
|
||||
}
|
||||
|
||||
get current(): Store<string> {
|
||||
if (!this._current) {
|
||||
this._current = this.currentLang.map(l => this.translations[l])
|
||||
}
|
||||
return this._current
|
||||
}
|
||||
|
||||
|
@ -108,8 +132,9 @@ export class Translation extends BaseUIElement {
|
|||
return allTranslations
|
||||
}
|
||||
|
||||
static fromMap(transl: Map<string, string>) {
|
||||
static fromMap(transl: Map<string, string>, strictLanguages: boolean = false) {
|
||||
const translations = {}
|
||||
console.log("Strict:", strictLanguages)
|
||||
let hasTranslation = false
|
||||
transl?.forEach((value, key) => {
|
||||
translations[key] = value
|
||||
|
@ -118,7 +143,7 @@ export class Translation extends BaseUIElement {
|
|||
if (!hasTranslation) {
|
||||
return undefined
|
||||
}
|
||||
return new Translation(translations)
|
||||
return new Translation(translations, undefined, strictLanguages)
|
||||
}
|
||||
|
||||
public toString() {
|
||||
|
@ -131,33 +156,39 @@ export class Translation extends BaseUIElement {
|
|||
this.isDestroyed = true
|
||||
}
|
||||
|
||||
public textFor(language: string): string {
|
||||
/**
|
||||
* Which language will be effectively used for the given language of choice?
|
||||
*/
|
||||
public actualLanguage(language: string): "*" | string | undefined {
|
||||
if (this.translations["*"]) {
|
||||
return this.translations["*"]
|
||||
return "*"
|
||||
}
|
||||
const txt = this.translations[language]
|
||||
if (txt !== undefined) {
|
||||
return txt
|
||||
if(txt === undefined && this._strictLanguages){
|
||||
return undefined
|
||||
}
|
||||
const en = this.translations["en"]
|
||||
if (en !== undefined) {
|
||||
return en
|
||||
if (txt !== undefined ) {
|
||||
return language
|
||||
}
|
||||
if (this.translations["en"] !== undefined) {
|
||||
return "en"
|
||||
}
|
||||
for (const i in this.translations) {
|
||||
if (!this.translations.hasOwnProperty(i)) {
|
||||
continue
|
||||
}
|
||||
return this.translations[i] // Return a random language
|
||||
}
|
||||
console.error("Missing language ", Locale.language.data, "for", this.translations)
|
||||
return ""
|
||||
}
|
||||
public textFor(language: string): string | undefined {
|
||||
return this.translations[this.actualLanguage(language)]
|
||||
}
|
||||
|
||||
InnerConstructElement(): HTMLElement {
|
||||
const el = document.createElement("span")
|
||||
const self = this
|
||||
|
||||
el.innerHTML = self.txt
|
||||
if(self.txt){
|
||||
el.innerHTML = self.txt
|
||||
}
|
||||
if (self.translations["*"] !== undefined) {
|
||||
return el
|
||||
}
|
||||
|
@ -166,7 +197,11 @@ export class Translation extends BaseUIElement {
|
|||
if (self.isDestroyed) {
|
||||
return true
|
||||
}
|
||||
el.innerHTML = self.txt
|
||||
if(self.txt === undefined){
|
||||
el.innerHTML = ""
|
||||
}else{
|
||||
el.innerHTML = self.txt
|
||||
}
|
||||
})
|
||||
|
||||
if (self.context === undefined || self.context?.indexOf(":") < 0) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import SvelteUIElement from "./UI/Base/SvelteUIElement"
|
||||
import Test from "./UI/Test.svelte"
|
||||
import NameSuggestionIndex from "./Logic/Web/NameSuggestionIndex"
|
||||
|
||||
new SvelteUIElement(Test).AttachTo("maindiv")
|
||||
|
|
Loading…
Reference in a new issue