Posterverse: the shell language menu in the phone bar and at the bottom of the rail
Fills VerticalTabsLayout's #phone-end and #rail-end slots with the shared ShellLanguageMenu. A pick first asks the host (doxa-language-request); only an uncancelled pick reaches setLocale, which keeps the pick with the host language value it was made over and saves it. The pick wins only while that value is unchanged; a new value from the host takes over and drops the pick. The saved choice is never read as a source, since it falls back to the browser's language and would override the host page.
This commit is contained in:
parent
448ef0ac70
commit
067979ae71
1 changed files with 27 additions and 4 deletions
|
|
@ -57,6 +57,11 @@
|
|||
</template>
|
||||
</section>
|
||||
|
||||
<!-- The language menu: the end of the phone bar, and the bottom of the desktop rail. A pick asks the
|
||||
host first (doxa-language-request); only an uncancelled pick reaches setLocale. -->
|
||||
<template #phone-end><ShellLanguageMenu :current="uiLocale" @switch="setLocale" /></template>
|
||||
<template #rail-end="{ collapsed }"><ShellLanguageMenu :current="uiLocale" :collapsed="collapsed" @switch="setLocale" /></template>
|
||||
|
||||
<!-- The pane is the scroller (overflow-y:auto), so back-to-top follows the active pane. -->
|
||||
<BackToTopButton :target="activePaneEl" :labels="{ backToTop: tr('backToTop') }" />
|
||||
</VerticalTabsLayout>
|
||||
|
|
@ -66,6 +71,8 @@
|
|||
import { defineComponent, inject } from 'vue'
|
||||
import VerticalTabsLayout from '@map/layouts/VerticalTabsLayout.vue'
|
||||
import BackToTopButton from '@map/components/BackToTopButton.vue'
|
||||
import ShellLanguageMenu from '@map/components/ShellLanguageMenu.vue'
|
||||
import { saveLocale } from '@map/layouts/app-wrapper-material/languages.js'
|
||||
import { loadScriptOnce, whenTagDefined } from '@map/utils/embedScript.js'
|
||||
import { translate, LOCALES } from './src/strings.js'
|
||||
import { SECTIONS, TABS, resolveTabs } from './src/tabs.js'
|
||||
|
|
@ -93,7 +100,7 @@ export default defineComponent({
|
|||
// The host's attributes stay on the host: an inline style copied onto the layout root beats the
|
||||
// layout's own rules (a styled host once collapsed a nested map to 76px).
|
||||
inheritAttrs: false,
|
||||
components: { VerticalTabsLayout, BackToTopButton },
|
||||
components: { VerticalTabsLayout, BackToTopButton, ShellLanguageMenu },
|
||||
props: {
|
||||
treeBase: { type: String, default: '' },
|
||||
profileConfig: { type: [String, Object], default: '' },
|
||||
|
|
@ -128,6 +135,11 @@ export default defineComponent({
|
|||
// The manifest's `runtime` block (the build's apiBaseUrl + public token), for foreign-page embeds.
|
||||
runtime: {},
|
||||
hostLang: detectHostLocale(),
|
||||
// A language picked in THIS session through the menu, and the host's language value it was picked
|
||||
// over. The pick wins only while that value is unchanged; the saved choice (loadLocale) is never
|
||||
// read, since it falls back to the browser's language and would override the host page.
|
||||
localPick: '',
|
||||
pickBase: null,
|
||||
// Until the manifest lands, the rail shows the rows that are expected to exist; provisional rows
|
||||
// appear only once the manifest proves their bundle is in this tree.
|
||||
tabs: TABS.filter((d) => !d.provisional).map(freshTab),
|
||||
|
|
@ -153,10 +165,13 @@ export default defineComponent({
|
|||
return this.manifestBase ? this.manifestBase.replace(/\/+$/, '') + '/assets' : ''
|
||||
},
|
||||
headingText() { return String(this.cfg.heading || this.heading || '').trim() || 'Posterverse' },
|
||||
// Language precedence: what the host says, then the host page's own language, then English.
|
||||
// The language the host passed: the `language` prop, else profile-config language / locale / lang.
|
||||
hostLanguage() { return String(this.language || this.cfg.language || this.cfg.locale || this.cfg.lang || '').trim() },
|
||||
// Language precedence: this session's menu pick (while the host's value is the one it was made over),
|
||||
// then what the host says, then the host page's own language, then English.
|
||||
effectiveLocale() {
|
||||
const fromProp = String(this.language || this.cfg.language || this.cfg.locale || this.cfg.lang || '').trim()
|
||||
return (fromProp || this.hostLang || 'en').split('-')[0].toLowerCase()
|
||||
if (this.localPick && this.hostLanguage === this.pickBase) return this.localPick
|
||||
return (this.hostLanguage || this.hostLang || 'en').split('-')[0].toLowerCase()
|
||||
},
|
||||
uiLocale() { return LOCALES.includes(this.effectiveLocale) ? this.effectiveLocale : 'en' },
|
||||
uiDir() { return this.uiLocale === 'ar' ? 'rtl' : 'ltr' },
|
||||
|
|
@ -201,6 +216,8 @@ export default defineComponent({
|
|||
this.mountElement(this.activeIndex)
|
||||
},
|
||||
tab(id) { if (id) this.onSelect(id) },
|
||||
// A host that sets a new language takes over again: the session pick is dropped.
|
||||
hostLanguage(v) { if (v !== this.pickBase) { this.localPick = ''; this.pickBase = null } },
|
||||
},
|
||||
mounted() {
|
||||
this.watchHostLang()
|
||||
|
|
@ -213,6 +230,12 @@ export default defineComponent({
|
|||
},
|
||||
methods: {
|
||||
tr(key, params) { return translate(this.uiLocale, key, params) },
|
||||
// The menu's 'switch': no host cancelled the request, so the shell switches itself.
|
||||
setLocale(code) {
|
||||
this.pickBase = this.hostLanguage
|
||||
this.localPick = code
|
||||
saveLocale(code)
|
||||
},
|
||||
tabLabel(t) { return this.tr('tab.' + t.id) },
|
||||
tabSub(t) { return t.section === 'experimental' ? this.tr('experimentalSub') : this.tr('sub.' + t.id) },
|
||||
setSlot(i, el) { if (el) this._slots = Object.assign(this._slots || {}, { [i]: el }) },
|
||||
|
|
|
|||
Loading…
Reference in a new issue