UI: fix #1848, explicitly add element to help screen readers pronounce everything correctly

This commit is contained in:
Pieter Vander Vennet 2024-05-26 22:48:59 +02:00
parent cecfaebf5b
commit 9832aa45f0
6 changed files with 94 additions and 35 deletions

View file

@ -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}

View file

@ -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>

View file

@ -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}/>

View file

@ -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")

View file

@ -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) {

View file

@ -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")