mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-13 06:42:23 +00:00
Merge pull request #1696 from bilawalsidhu/sameh/i18n-entry
feat(i18n): an ./i18n entry for an application with its own language choice
This commit is contained in:
13 files changed
+258
-23
No files matched your search
@@ -0,0 +1,3 @@
|
|||||||
|
- Add an `./i18n` entry so an application with its own language menu can
|
||||||
|
start, follow and switch the interface language as the globe picker does
|
||||||
|
(samehkhamis, #1696).
|
||||||
+3
-1
@@ -224,7 +224,9 @@ leaving out `command-dock` the voice control and its pointer reticle;
|
|||||||
leaving out `context` the Bhote Koshi reconstruction panel; and leaving out
|
leaving out `context` the Bhote Koshi reconstruction panel; and leaving out
|
||||||
`intel-hud` the Intel HUD overlay (`#intel-hud`), whose readouts the HUD
|
`intel-hud` the Intel HUD overlay (`#intel-hud`), whose readouts the HUD
|
||||||
implementation publishes to the `gev-hud` store. An unknown
|
implementation publishes to the `gev-hud` store. An unknown
|
||||||
view name is an error. A host that does not initialize Core's locale passes
|
view name is an error. An application that offers its own language choice
|
||||||
|
starts and switches the language with the `./i18n` entry (`startPageLocale`,
|
||||||
|
`followPageLocale`, `switchLocale`; see [I18N.md](./I18N.md)) and passes
|
||||||
`localePicker: false` to `createApplicationControls` instead of editing the
|
`localePicker: false` to `createApplicationControls` instead of editing the
|
||||||
markup. Node tests that import application modules register the component
|
markup. Node tests that import application modules register the component
|
||||||
loader with `node --import=<core package>/build/register-vue`.
|
loader with `node --import=<core package>/build/register-vue`.
|
||||||
@@ -5080,6 +5080,10 @@ Shipped locales: **en** (source and fallback), **ar**, **de**, **es**, **fr**, *
|
|||||||
views re-render, code that published translated text words it again through
|
views re-render, code that published translated text words it again through
|
||||||
`onLocaleChange`, and `<html lang>`/`dir` and the page's `data-i18n` markup
|
`onLocaleChange`, and `<html lang>`/`dir` and the page's `data-i18n` markup
|
||||||
follow. Nothing is lost; a toast already on screen keeps its words.
|
follow. Nothing is lost; a toast already on screen keeps its words.
|
||||||
|
An application that offers the language elsewhere (a menu) uses the
|
||||||
|
`./i18n` entry (`src/i18n/page.js`): `startPageLocale` at boot,
|
||||||
|
`followPageLocale` for the markup and `switchLocale`, which does what a
|
||||||
|
picker choice does, with `localePicker: false`.
|
||||||
- **Right to left.** Arabic (`ar`) and Hebrew (`he`) are in `RTL_LOCALES`, so
|
- **Right to left.** Arabic (`ar`) and Hebrew (`he`) are in `RTL_LOCALES`, so
|
||||||
`<html dir="rtl">`: the title, style indicator, both panel stacks and their
|
`<html dir="rtl">`: the title, style indicator, both panel stacks and their
|
||||||
icon rails swap sides through logical properties (`inset-inline-*`,
|
icon rails swap sides through logical properties (`inset-inline-*`,
|
||||||
|
|||||||
@@ -59,6 +59,30 @@ The language picker switches in place, without a reload. A view that calls `t()`
|
|||||||
|
|
||||||
A toast already on screen keeps its words.
|
A toast already on screen keeps its words.
|
||||||
|
|
||||||
|
## An application with its own language choice
|
||||||
|
|
||||||
|
An application that composes Core and offers the language somewhere else (a menu, a settings page) imports the `./i18n` entry (`src/i18n/page.js`) and passes `localePicker: false` to `createApplicationControls`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import {
|
||||||
|
LOCALES,
|
||||||
|
LOCALE_NAMES,
|
||||||
|
followPageLocale,
|
||||||
|
getLocale,
|
||||||
|
onLocaleChange,
|
||||||
|
startPageLocale,
|
||||||
|
switchLocale,
|
||||||
|
} from '<core package>/i18n';
|
||||||
|
|
||||||
|
startPageLocale(document); // before any view renders: ?lang=, stored choice, browser, English; translates data-i18n markup
|
||||||
|
const stopFollowing = followPageLocale(document); // <html lang>/dir and data-i18n markup follow each switch
|
||||||
|
|
||||||
|
for (const code of LOCALES) addMenuItem(LOCALE_NAMES[code], code === getLocale());
|
||||||
|
menu.onChoose = (code) => switchLocale(code); // switched in place and stored (in ?lang when storage is blocked)
|
||||||
|
```
|
||||||
|
|
||||||
|
`switchLocale` does what a choice in the globe picker does, and returns `'unchanged'` for the active language. `onLocaleChange` tells the application's own menu to mark the new language.
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- **Pass literals.** `t('Close')`, not `t(label)` (unless `label` holds a `msg()` value) and never a template literal with `${…}`.
|
- **Pass literals.** `t('Close')`, not `t(label)` (unless `label` holds a `msg()` value) and never a template literal with `${…}`.
|
||||||
@@ -109,6 +133,7 @@ Icons (`aria-hidden="true"`, Material Symbols), comments, CSS values and lowerca
|
|||||||
| `src/ui/i18n.js` | the page's vue-i18n instance: the active locale and the catalogs |
|
| `src/ui/i18n.js` | the page's vue-i18n instance: the active locale and the catalogs |
|
||||||
| `src/i18n/marker.js` | `msg` alone, for modules the server or tools import (no catalogs) |
|
| `src/i18n/marker.js` | `msg` alone, for modules the server or tools import (no catalogs) |
|
||||||
| `src/i18n/locale.js` | shipped languages, their names, choosing the language at boot |
|
| `src/i18n/locale.js` | shipped languages, their names, choosing the language at boot |
|
||||||
|
| `src/i18n/page.js` | the `./i18n` entry: start, follow and switch the page's language |
|
||||||
| `src/i18n/locales/<code>.json` | one catalog per language, keyed by English text |
|
| `src/i18n/locales/<code>.json` | one catalog per language, keyed by English text |
|
||||||
| `src/i18n/same-as-english.json` | per language, the entries that stay as in English on purpose |
|
| `src/i18n/same-as-english.json` | per language, the entries that stay as in English on purpose |
|
||||||
| `scripts/i18n.mjs` | `npm run i18n`: extracts messages, checks the catalogs, finds unmarked UI text |
|
| `scripts/i18n.mjs` | `npm run i18n`: extracts messages, checks the catalogs, finds unmarked UI text |
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
"./application": "./src/app/application.js",
|
"./application": "./src/app/application.js",
|
||||||
"./application/viewer": "./src/app/viewer.js",
|
"./application/viewer": "./src/app/viewer.js",
|
||||||
"./time": "./src/time/worldClock.js",
|
"./time": "./src/time/worldClock.js",
|
||||||
|
"./i18n": "./src/i18n/page.js",
|
||||||
"./build/vite": {
|
"./build/vite": {
|
||||||
"node": "./build/vite.js"
|
"node": "./build/vite.js"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1342,6 +1342,8 @@
|
|||||||
"src/data/layerState.js",
|
"src/data/layerState.js",
|
||||||
"src/data/layerState.test.mjs",
|
"src/data/layerState.test.mjs",
|
||||||
"src/data/spriteOrder.js",
|
"src/data/spriteOrder.js",
|
||||||
|
"src/i18n/page.js",
|
||||||
|
"src/i18n/page.test.mjs",
|
||||||
"src/i18n/locales/ar.json",
|
"src/i18n/locales/ar.json",
|
||||||
"src/i18n/locales/de.json",
|
"src/i18n/locales/de.json",
|
||||||
"src/i18n/locales/es.json",
|
"src/i18n/locales/es.json",
|
||||||
|
|||||||
@@ -175,6 +175,28 @@
|
|||||||
],
|
],
|
||||||
"external": ["vue", "pinia", "vue-i18n"]
|
"external": ["vue", "pinia", "vue-i18n"]
|
||||||
},
|
},
|
||||||
|
"i18n": {
|
||||||
|
"exports": ["./i18n"],
|
||||||
|
"modules": [
|
||||||
|
"src/i18n/page.js",
|
||||||
|
"src/i18n/index.js",
|
||||||
|
"src/i18n/locale.js",
|
||||||
|
"src/i18n/locales/ar.json",
|
||||||
|
"src/i18n/locales/de.json",
|
||||||
|
"src/i18n/locales/es.json",
|
||||||
|
"src/i18n/locales/fr.json",
|
||||||
|
"src/i18n/locales/he.json",
|
||||||
|
"src/i18n/locales/it.json",
|
||||||
|
"src/i18n/locales/ja.json",
|
||||||
|
"src/i18n/locales/pt.json",
|
||||||
|
"src/i18n/locales/ru.json",
|
||||||
|
"src/i18n/locales/sv.json",
|
||||||
|
"src/i18n/locales/uk.json",
|
||||||
|
"src/i18n/locales/zh-CN.json",
|
||||||
|
"src/i18n/locales/zh-Hant.json"
|
||||||
|
],
|
||||||
|
"external": []
|
||||||
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"exports": ["./time"],
|
"exports": ["./time"],
|
||||||
"modules": ["src/time/worldClock.js"],
|
"modules": ["src/time/worldClock.js"],
|
||||||
|
|||||||
Vendored
+2
-1
@@ -9,7 +9,8 @@ import { t } from '../i18n/index.js';
|
|||||||
* Construct the existing controls and camera presentation. `initialShareCode`
|
* Construct the existing controls and camera presentation. `initialShareCode`
|
||||||
* opens a share code (the `<code>` of `/share/<code>`) at start-up, for a host
|
* opens a share code (the `<code>` of `/share/<code>`) at start-up, for a host
|
||||||
* whose address does not carry one. `localePicker: false` removes the language
|
* whose address does not carry one. `localePicker: false` removes the language
|
||||||
* picker, for a host that does not initialize Core's locale.
|
* picker, for a host that offers its own language choice (`switchLocale` from
|
||||||
|
* the `./i18n` entry).
|
||||||
*/
|
*/
|
||||||
export function createApplicationControls({
|
export function createApplicationControls({
|
||||||
scene: { viewer, mapStackController, operations },
|
scene: { viewer, mapStackController, operations },
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* @module i18n/page
|
||||||
|
* @description The page's interface language, for an application that
|
||||||
|
* composes Core and offers its own language choice (a menu item, a settings
|
||||||
|
* page) instead of the globe picker. It starts the language the standalone
|
||||||
|
* application starts, keeps `<html lang>`/`<html dir>` and the page's
|
||||||
|
* `data-i18n` markup in step, and switches in place, stored for the next
|
||||||
|
* visit, exactly as the picker does. Guide: docs/I18N.md.
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
DEFAULT_LOCALE,
|
||||||
|
LOCALES,
|
||||||
|
LOCALE_NAMES,
|
||||||
|
applyDocumentLanguage,
|
||||||
|
isRtlLocale,
|
||||||
|
normalizeLocale,
|
||||||
|
resolveLocale,
|
||||||
|
} from './locale.js';
|
||||||
|
import {
|
||||||
|
applyDocumentTranslations,
|
||||||
|
getLocale,
|
||||||
|
onLocaleChange,
|
||||||
|
persistLocale,
|
||||||
|
setLocale,
|
||||||
|
} from './index.js';
|
||||||
|
|
||||||
|
export {
|
||||||
|
DEFAULT_LOCALE,
|
||||||
|
LOCALES,
|
||||||
|
LOCALE_NAMES,
|
||||||
|
applyDocumentTranslations,
|
||||||
|
getLocale,
|
||||||
|
isRtlLocale,
|
||||||
|
onLocaleChange,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the page in its language: `?lang=`, then the stored choice, then the
|
||||||
|
* browser's languages, then English. Call it before any view renders, so
|
||||||
|
* every lookup and `<html lang>`/`<html dir>` agree from the first frame; the
|
||||||
|
* page's static `data-i18n` markup (a loading screen) is translated now.
|
||||||
|
* @param {Document} [doc]
|
||||||
|
* @param {Parameters<typeof resolveLocale>[0]} [inputs] Location, storage
|
||||||
|
* and languages, for tests.
|
||||||
|
* @returns {string} The active locale.
|
||||||
|
*/
|
||||||
|
export function startPageLocale(doc = globalThis.document, inputs) {
|
||||||
|
const locale = setLocale(resolveLocale(inputs));
|
||||||
|
applyDocumentLanguage(doc, locale);
|
||||||
|
applyDocumentTranslations(doc);
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep `<html lang>`/`<html dir>` and the page's `data-i18n` markup in step
|
||||||
|
* with every language switch.
|
||||||
|
* @param {Document} [doc]
|
||||||
|
* @returns {() => void} Stops following.
|
||||||
|
*/
|
||||||
|
export function followPageLocale(doc = globalThis.document) {
|
||||||
|
return onLocaleChange(() => {
|
||||||
|
applyDocumentLanguage(doc, getLocale());
|
||||||
|
applyDocumentTranslations(doc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch the page to `locale` in place and keep it for the next visit. The
|
||||||
|
* active language, or an unknown code that would fall back to it, changes
|
||||||
|
* nothing.
|
||||||
|
* @param {string} locale One of LOCALES.
|
||||||
|
* @param {Parameters<typeof persistLocale>[1]} [options] Location, history
|
||||||
|
* and storage, for tests.
|
||||||
|
* @returns {'unchanged'|'switched'}
|
||||||
|
*/
|
||||||
|
export function switchLocale(locale, options) {
|
||||||
|
const next = normalizeLocale(locale) || DEFAULT_LOCALE;
|
||||||
|
if (next === getLocale()) return 'unchanged';
|
||||||
|
setLocale(persistLocale(next, options));
|
||||||
|
return 'switched';
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// src/i18n/page.test.mjs — an application with its own language choice starts,
|
||||||
|
// follows and switches the page's language as the globe picker does.
|
||||||
|
import { afterEach, test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { LOCALE_STORAGE_KEY } from './locale.js';
|
||||||
|
import {
|
||||||
|
followPageLocale,
|
||||||
|
getLocale,
|
||||||
|
startPageLocale,
|
||||||
|
switchLocale,
|
||||||
|
} from './page.js';
|
||||||
|
import { setLocale } from './index.js';
|
||||||
|
|
||||||
|
afterEach(() => setLocale('en'));
|
||||||
|
|
||||||
|
function memoryStorage() {
|
||||||
|
const data = new Map();
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
getItem: (key) => data.get(key) ?? null,
|
||||||
|
setItem: (key, value) => data.set(key, String(value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function page(elements = []) {
|
||||||
|
return {
|
||||||
|
documentElement: { lang: '', dir: '' },
|
||||||
|
querySelectorAll: (selector) =>
|
||||||
|
selector === '[data-i18n]' ? elements : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A data-i18n element, as applyDocumentTranslations reads one. */
|
||||||
|
function marked(text) {
|
||||||
|
return {
|
||||||
|
textContent: text,
|
||||||
|
hasAttribute: (name) => name === 'data-i18n',
|
||||||
|
getAttribute: () => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('the page starts in ?lang=, else the stored choice, with lang and dir set', () => {
|
||||||
|
const doc = page();
|
||||||
|
const storage = memoryStorage();
|
||||||
|
storage.setItem(LOCALE_STORAGE_KEY, 'de');
|
||||||
|
assert.equal(
|
||||||
|
startPageLocale(doc, {
|
||||||
|
location: { search: '?lang=ar' },
|
||||||
|
storage,
|
||||||
|
languages: ['ru'],
|
||||||
|
}),
|
||||||
|
'ar',
|
||||||
|
);
|
||||||
|
assert.equal(getLocale(), 'ar');
|
||||||
|
assert.deepEqual(doc.documentElement, { lang: 'ar', dir: 'rtl' });
|
||||||
|
assert.equal(
|
||||||
|
startPageLocale(doc, { location: { search: '' }, storage, languages: [] }),
|
||||||
|
'de',
|
||||||
|
);
|
||||||
|
assert.deepEqual(doc.documentElement, { lang: 'de', dir: 'ltr' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("the page's data-i18n markup is in its language from the start", () => {
|
||||||
|
const close = marked('Close');
|
||||||
|
startPageLocale(page([close]), {
|
||||||
|
location: { search: '?lang=de' },
|
||||||
|
storage: memoryStorage(),
|
||||||
|
languages: [],
|
||||||
|
});
|
||||||
|
assert.equal(close.textContent, 'Schließen');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a switch is stored, drops ?lang, and the followed page takes it', () => {
|
||||||
|
const doc = page();
|
||||||
|
const stop = followPageLocale(doc);
|
||||||
|
const storage = memoryStorage();
|
||||||
|
const replaced = [];
|
||||||
|
const options = {
|
||||||
|
location: { href: 'https://example.test/?lang=fr#view' },
|
||||||
|
history: { replaceState: (_s, _t, url) => replaced.push(url) },
|
||||||
|
storage,
|
||||||
|
};
|
||||||
|
assert.equal(switchLocale('he', options), 'switched');
|
||||||
|
assert.equal(getLocale(), 'he');
|
||||||
|
assert.equal(storage.data.get(LOCALE_STORAGE_KEY), 'he');
|
||||||
|
assert.deepEqual(replaced, ['https://example.test/#view']);
|
||||||
|
assert.deepEqual(doc.documentElement, { lang: 'he', dir: 'rtl' });
|
||||||
|
stop();
|
||||||
|
switchLocale('ja', options);
|
||||||
|
assert.deepEqual(doc.documentElement, { lang: 'he', dir: 'rtl' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('choosing the active language, or one that falls back to it, changes nothing', () => {
|
||||||
|
const storage = memoryStorage();
|
||||||
|
const options = {
|
||||||
|
location: { href: 'https://example.test/' },
|
||||||
|
history: { replaceState() {} },
|
||||||
|
storage,
|
||||||
|
};
|
||||||
|
assert.equal(switchLocale('en', options), 'unchanged');
|
||||||
|
assert.equal(switchLocale('xx', options), 'unchanged');
|
||||||
|
assert.equal(storage.data.size, 0);
|
||||||
|
assert.equal(switchLocale('pt-BR', options), 'switched');
|
||||||
|
assert.equal(getLocale(), 'pt');
|
||||||
|
assert.equal(switchLocale('pt', options), 'unchanged');
|
||||||
|
});
|
||||||
@@ -64,12 +64,9 @@ async function standaloneStartup(Cesium, credentials, fetchImpl) {
|
|||||||
document: {
|
document: {
|
||||||
getElementById: () => ({ querySelector: () => ({}) }),
|
getElementById: () => ({ querySelector: () => ({}) }),
|
||||||
},
|
},
|
||||||
resolveLocale: () => 'en',
|
startPageLocale: () => 'en',
|
||||||
setLocale() {},
|
followPageLocale: () => () => {},
|
||||||
getLocale: () => 'en',
|
|
||||||
onLocaleChange: () => () => {},
|
|
||||||
mountApplicationViews() {},
|
mountApplicationViews() {},
|
||||||
applyDocumentLanguage() {},
|
|
||||||
applyDocumentTranslations() {},
|
applyDocumentTranslations() {},
|
||||||
CITY_POIS: [],
|
CITY_POIS: [],
|
||||||
createApplication: (options) => options,
|
createApplication: (options) => options,
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ import { createStandaloneScene } from './scene.js';
|
|||||||
import { createStandaloneControls } from './controls.js';
|
import { createStandaloneControls } from './controls.js';
|
||||||
import { createStandaloneData } from './data.js';
|
import { createStandaloneData } from './data.js';
|
||||||
import { createStandaloneTools } from './tools.js';
|
import { createStandaloneTools } from './tools.js';
|
||||||
import { resolveLocale, applyDocumentLanguage } from '../i18n/locale.js';
|
|
||||||
import {
|
import {
|
||||||
applyDocumentTranslations,
|
applyDocumentTranslations,
|
||||||
getLocale,
|
followPageLocale,
|
||||||
onLocaleChange,
|
startPageLocale,
|
||||||
setLocale,
|
} from '../i18n/page.js';
|
||||||
} from '../i18n/index.js';
|
|
||||||
import { mountApplicationViews } from '../ui/views/index.js';
|
import { mountApplicationViews } from '../ui/views/index.js';
|
||||||
import { createGoogleTokenSource } from '../maps/googleTokens.js';
|
import { createGoogleTokenSource } from '../maps/googleTokens.js';
|
||||||
|
|
||||||
@@ -37,9 +35,7 @@ export function createStandaloneApplication({
|
|||||||
// scripts execute after parsing, so the DOM is ready here: the views
|
// scripts execute after parsing, so the DOM is ready here: the views
|
||||||
// translate as they render, and the page's static data-i18n* markup (the
|
// translate as they render, and the page's static data-i18n* markup (the
|
||||||
// loading screen) is translated here and after every language switch.
|
// loading screen) is translated here and after every language switch.
|
||||||
const activeLocale = resolveLocale();
|
startPageLocale(document);
|
||||||
setLocale(activeLocale);
|
|
||||||
applyDocumentLanguage(document, activeLocale);
|
|
||||||
// Views render the page's markup; wiring reads it.
|
// Views render the page's markup; wiring reads it.
|
||||||
mountApplicationViews(document);
|
mountApplicationViews(document);
|
||||||
applyDocumentTranslations(document);
|
applyDocumentTranslations(document);
|
||||||
@@ -50,12 +46,7 @@ export function createStandaloneApplication({
|
|||||||
return createApplication({
|
return createApplication({
|
||||||
createScene: async (context) => {
|
createScene: async (context) => {
|
||||||
// A language switch applies in place; the page's own markup follows.
|
// A language switch applies in place; the page's own markup follows.
|
||||||
context.defer(
|
context.defer(followPageLocale(document));
|
||||||
onLocaleChange(() => {
|
|
||||||
applyDocumentLanguage(document, getLocale());
|
|
||||||
applyDocumentTranslations(document);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
placeSearch = createStandalonePlaceSearch({
|
placeSearch = createStandalonePlaceSearch({
|
||||||
// The bundled city and landmark data the offline name provider reads.
|
// The bundled city and landmark data the offline name provider reads.
|
||||||
// The search package takes it as plain data rather than importing it,
|
// The search package takes it as plain data rather than importing it,
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export class StyleManager extends ShellFacade {
|
|||||||
* @param {Cesium.Viewer} viewer - The CesiumJS viewer instance.
|
* @param {Cesium.Viewer} viewer - The CesiumJS viewer instance.
|
||||||
* @param {object} [options]
|
* @param {object} [options]
|
||||||
* @param {boolean} [options.localePicker=true] - false removes the language
|
* @param {boolean} [options.localePicker=true] - false removes the language
|
||||||
* picker, for a host that does not initialize Core's locale.
|
* picker, for a host that offers its own language choice.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
viewer,
|
viewer,
|
||||||
@@ -764,7 +764,7 @@ export class StyleManager extends ShellFacade {
|
|||||||
this._lifetime.own(
|
this._lifetime.own(
|
||||||
onLocaleChange(() => nextTick(() => this._sizeDisplayLabelColumn())),
|
onLocaleChange(() => nextTick(() => this._sizeDisplayLabelColumn())),
|
||||||
);
|
);
|
||||||
// A host that does not initialize Core's locale shows no picker.
|
// A host that offers its own language choice shows no picker.
|
||||||
if (this._localePickerEnabled === false) return;
|
if (this._localePickerEnabled === false) return;
|
||||||
this._publishLocalePicker();
|
this._publishLocalePicker();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user