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 WeblateLink from "./WeblateLink.svelte"
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
import { Store } from "../../Logic/UIEventSource"
|
||||||
import FromHtml from "./FromHtml.svelte"
|
import FromHtml from "./FromHtml.svelte"
|
||||||
|
import { Utils } from "../../Utils"
|
||||||
|
|
||||||
export let t: Translation
|
export let t: Translation
|
||||||
export let cls: string = ""
|
export let cls: string = ""
|
||||||
// Text for the current language
|
// Text for the current language
|
||||||
let txt: Store<string | undefined> = t?.current
|
let txt: Store<string | undefined> = t?.current
|
||||||
|
let lang = t?.currentLang
|
||||||
$: {
|
$: {
|
||||||
txt = t?.current
|
txt = t?.current
|
||||||
|
lang = t?.currentLang
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $txt}
|
{#if $txt}
|
||||||
<span class={cls}>
|
<span class={cls}>
|
||||||
<FromHtml src={$txt} />
|
<span lang={$lang}>
|
||||||
|
{@html Utils.purify($txt)}
|
||||||
|
</span>
|
||||||
<WeblateLink context={t?.context} />
|
<WeblateLink context={t?.context} />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import Locale from "../../i18n/Locale"
|
import Locale from "../../i18n/Locale"
|
||||||
import type {
|
import type {
|
||||||
RenderingSpecification,
|
RenderingSpecification,
|
||||||
SpecialVisualizationState,
|
SpecialVisualizationState
|
||||||
} from "../../SpecialVisualization"
|
} from "../../SpecialVisualization"
|
||||||
import { Utils } from "../../../Utils.js"
|
import { Utils } from "../../../Utils.js"
|
||||||
import type { Feature } from "geojson"
|
import type { Feature } from "geojson"
|
||||||
|
@ -25,11 +25,13 @@
|
||||||
export let layer: LayerConfig | undefined
|
export let layer: LayerConfig | undefined
|
||||||
|
|
||||||
let language = Locale.language
|
let language = Locale.language
|
||||||
|
let lang = t.actualLanguage($language)
|
||||||
let txt: string = t.textFor($language)
|
let txt: string = t.textFor($language)
|
||||||
let specs: RenderingSpecification[] = []
|
let specs: RenderingSpecification[] = []
|
||||||
$: {
|
$: {
|
||||||
try {
|
try {
|
||||||
txt = t.textFor($language)
|
txt = t.textFor($language)
|
||||||
|
lang = t.actualLanguage($language)
|
||||||
if (txt !== undefined) {
|
if (txt !== undefined) {
|
||||||
let key = "cached_special_spec_" + $language
|
let key = "cached_special_spec_" + $language
|
||||||
specs = t[key]
|
specs = t[key]
|
||||||
|
@ -61,13 +63,15 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each specs as specpart}
|
<span {lang}>
|
||||||
{#if typeof specpart === "string"}
|
{#each specs as specpart}
|
||||||
<span>
|
{#if typeof specpart === "string"}
|
||||||
<FromHtml src={Utils.SubstituteKeys(specpart, $tags)} />
|
<span>
|
||||||
<WeblateLink context={t.context} />
|
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags)) }
|
||||||
</span>
|
<WeblateLink context={t.context} />
|
||||||
{:else if $tags !== undefined}
|
</span>
|
||||||
<ToSvelte construct={() => createVisualisation(specpart)} />
|
{:else if $tags !== undefined}
|
||||||
{/if}
|
<ToSvelte construct={() => createVisualisation(specpart)} />
|
||||||
{/each}
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</span>
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
<script lang="ts">
|
<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>
|
</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")
|
return new FixedUiElement(maybeWikidata["error"]).SetClass("alert")
|
||||||
}
|
}
|
||||||
const wikidata = <WikidataResponse>maybeWikidata["success"]
|
const wikidata = <WikidataResponse>maybeWikidata["success"]
|
||||||
|
console.log(">>>> got wikidata", wikidata)
|
||||||
return WikidataPreviewBox.WikidataResponsePreview(wikidata, options)
|
return WikidataPreviewBox.WikidataResponsePreview(wikidata, options)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -131,6 +132,8 @@ export default class WikidataPreviewBox extends VariableUiElement {
|
||||||
extraItems?: (BaseUIElement | string)[]
|
extraItems?: (BaseUIElement | string)[]
|
||||||
}
|
}
|
||||||
): BaseUIElement {
|
): BaseUIElement {
|
||||||
|
console.log(">>> constructing wikidata preview box", wikidata.labels)
|
||||||
|
|
||||||
const link = new Link(
|
const link = new Link(
|
||||||
new Combine([
|
new Combine([
|
||||||
wikidata.id,
|
wikidata.id,
|
||||||
|
@ -143,13 +146,12 @@ export default class WikidataPreviewBox extends VariableUiElement {
|
||||||
Wikidata.IdToArticle(wikidata.id),
|
Wikidata.IdToArticle(wikidata.id),
|
||||||
true
|
true
|
||||||
)?.SetClass("must-link")
|
)?.SetClass("must-link")
|
||||||
|
|
||||||
let info = new Combine([
|
let info = new Combine([
|
||||||
new Combine([
|
new Combine([
|
||||||
Translation.fromMap(wikidata.labels)?.SetClass("font-bold"),
|
Translation.fromMap(wikidata.labels)?.SetClass("font-bold"),
|
||||||
link,
|
link,
|
||||||
]).SetClass("flex justify-between flex-wrap-reverse"),
|
]).SetClass("flex justify-between flex-wrap-reverse"),
|
||||||
Translation.fromMap(wikidata.descriptions),
|
Translation.fromMap(wikidata.descriptions, true),
|
||||||
WikidataPreviewBox.QuickFacts(wikidata, options),
|
WikidataPreviewBox.QuickFacts(wikidata, options),
|
||||||
...(options?.extraItems ?? []),
|
...(options?.extraItems ?? []),
|
||||||
]).SetClass("flex flex-col link-underline")
|
]).SetClass("flex flex-col link-underline")
|
||||||
|
|
|
@ -10,9 +10,21 @@ export class Translation extends BaseUIElement {
|
||||||
public readonly translations: Record<string, string>
|
public readonly translations: Record<string, string>
|
||||||
public readonly context?: string
|
public readonly context?: string
|
||||||
private onDestroy: () => void
|
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()
|
super()
|
||||||
|
this._strictLanguages = strictLanguages
|
||||||
|
if(strictLanguages){
|
||||||
|
console.log(">>> strict:", translations)
|
||||||
|
}
|
||||||
if (translations === undefined) {
|
if (translations === undefined) {
|
||||||
console.error("Translation without content at " + context)
|
console.error("Translation without content at " + context)
|
||||||
throw `Translation without content (${context})`
|
throw `Translation without content (${context})`
|
||||||
|
@ -67,17 +79,29 @@ export class Translation extends BaseUIElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _current: Store<string>
|
private _current: Store<string>
|
||||||
|
private _currentLanguage: Store<string>
|
||||||
|
|
||||||
get current(): Store<string> {
|
/**
|
||||||
if (!this._current) {
|
* Indicates what language is effectively returned by `current`.
|
||||||
this._current = Locale.language.map(
|
* In most cases, this will be the language of choice, but if no translation is available, this will probably be `en`
|
||||||
(l) => this.textFor(l),
|
*/
|
||||||
|
get currentLang(): Store<string>{
|
||||||
|
if (!this._currentLanguage) {
|
||||||
|
this._currentLanguage = Locale.language.map(
|
||||||
|
(l) => this.actualLanguage(l),
|
||||||
[],
|
[],
|
||||||
(f) => {
|
(f) => {
|
||||||
this.onDestroy = 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
|
return this._current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +132,9 @@ export class Translation extends BaseUIElement {
|
||||||
return allTranslations
|
return allTranslations
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromMap(transl: Map<string, string>) {
|
static fromMap(transl: Map<string, string>, strictLanguages: boolean = false) {
|
||||||
const translations = {}
|
const translations = {}
|
||||||
|
console.log("Strict:", strictLanguages)
|
||||||
let hasTranslation = false
|
let hasTranslation = false
|
||||||
transl?.forEach((value, key) => {
|
transl?.forEach((value, key) => {
|
||||||
translations[key] = value
|
translations[key] = value
|
||||||
|
@ -118,7 +143,7 @@ export class Translation extends BaseUIElement {
|
||||||
if (!hasTranslation) {
|
if (!hasTranslation) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return new Translation(translations)
|
return new Translation(translations, undefined, strictLanguages)
|
||||||
}
|
}
|
||||||
|
|
||||||
public toString() {
|
public toString() {
|
||||||
|
@ -131,33 +156,39 @@ export class Translation extends BaseUIElement {
|
||||||
this.isDestroyed = true
|
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["*"]) {
|
if (this.translations["*"]) {
|
||||||
return this.translations["*"]
|
return "*"
|
||||||
}
|
}
|
||||||
const txt = this.translations[language]
|
const txt = this.translations[language]
|
||||||
if (txt !== undefined) {
|
if(txt === undefined && this._strictLanguages){
|
||||||
return txt
|
return undefined
|
||||||
}
|
}
|
||||||
const en = this.translations["en"]
|
if (txt !== undefined ) {
|
||||||
if (en !== undefined) {
|
return language
|
||||||
return en
|
}
|
||||||
|
if (this.translations["en"] !== undefined) {
|
||||||
|
return "en"
|
||||||
}
|
}
|
||||||
for (const i in this.translations) {
|
for (const i in this.translations) {
|
||||||
if (!this.translations.hasOwnProperty(i)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return this.translations[i] // Return a random language
|
return this.translations[i] // Return a random language
|
||||||
}
|
}
|
||||||
console.error("Missing language ", Locale.language.data, "for", this.translations)
|
console.error("Missing language ", Locale.language.data, "for", this.translations)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
public textFor(language: string): string | undefined {
|
||||||
|
return this.translations[this.actualLanguage(language)]
|
||||||
|
}
|
||||||
|
|
||||||
InnerConstructElement(): HTMLElement {
|
InnerConstructElement(): HTMLElement {
|
||||||
const el = document.createElement("span")
|
const el = document.createElement("span")
|
||||||
const self = this
|
const self = this
|
||||||
|
if(self.txt){
|
||||||
el.innerHTML = self.txt
|
el.innerHTML = self.txt
|
||||||
|
}
|
||||||
if (self.translations["*"] !== undefined) {
|
if (self.translations["*"] !== undefined) {
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
@ -166,7 +197,11 @@ export class Translation extends BaseUIElement {
|
||||||
if (self.isDestroyed) {
|
if (self.isDestroyed) {
|
||||||
return true
|
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) {
|
if (self.context === undefined || self.context?.indexOf(":") < 0) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import SvelteUIElement from "./UI/Base/SvelteUIElement"
|
import SvelteUIElement from "./UI/Base/SvelteUIElement"
|
||||||
import Test from "./UI/Test.svelte"
|
import Test from "./UI/Test.svelte"
|
||||||
import NameSuggestionIndex from "./Logic/Web/NameSuggestionIndex"
|
|
||||||
|
|
||||||
new SvelteUIElement(Test).AttachTo("maindiv")
|
new SvelteUIElement(Test).AttachTo("maindiv")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue