${this._t("marker.preview.title")}
diff --git a/docs/images/screenshots.json b/docs/images/screenshots.json
index ae16ab78..99a57df9 100644
--- a/docs/images/screenshots.json
+++ b/docs/images/screenshots.json
@@ -2,7 +2,7 @@
"version": 1,
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
- "sourceFingerprint": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceFingerprint": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"captureScriptSha256": "ce2e9542fed9dade3085be87d16f69adb2ac8262893ad78ad966b1b9673f2983",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -14,7 +14,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0"
},
"view-touch": {
@@ -25,7 +25,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "41e3ba67f8db0e98f26f484293af83ef937c369ca5ca6a59a3350d8954c906f4"
},
"space-create": {
@@ -36,7 +36,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "0dc024726327f60f4a9ceaf3044381691f81f1090af81d1812e870f22d9343ba"
},
"room-contour-close": {
@@ -47,7 +47,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "44cfaa95bd51e2cd628400e28db0ad8b2f0cd904385845bd402494f3f5c0d93c"
},
"plan-context-tray": {
@@ -58,7 +58,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "e0662429b423fb74151b583dcc2c8635b001b637d03bbf7a6b16aec46399c3f8"
},
"device-editor": {
@@ -69,7 +69,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "d0ffd31ce80bfde21ab75da356a5fc1af38246f2b301030880320620c228d89d"
},
"device-display-preview": {
@@ -80,7 +80,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160"
},
"background-editor": {
@@ -91,7 +91,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a"
},
"room-card": {
@@ -102,7 +102,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
},
"device-info": {
@@ -113,7 +113,7 @@
},
"theme": "dark",
"language": "en",
- "sourceSha256": "63fd6b17b9c24cb2f35753e6632c7be08208a5c279978306f7c6d101a0c779d1",
+ "sourceSha256": "3e3153d6bbd75a97ba780b58b76a7231df7d1734a35bbcdce7d456cc9d7cc5e0",
"imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517"
}
}
diff --git a/src/i18n/registry.ts b/src/i18n/registry.ts
index 813b3d23..1053db39 100644
--- a/src/i18n/registry.ts
+++ b/src/i18n/registry.ts
@@ -23,10 +23,6 @@ export type Lang = (typeof LANGUAGE_REGISTRY)[number]['code'];
export const FALLBACK_LANGUAGE_CODE: Lang = 'en';
export const FALLBACK_DICTIONARY = en;
-const LANGUAGE_BY_CODE = new Map(
- LANGUAGE_REGISTRY.map((entry) => [entry.code, entry]),
-);
-
/** Normalize HA locale tags for registry lookup. */
export function normalizeLanguageTag(value: unknown): string {
return typeof value === 'string'
@@ -34,6 +30,15 @@ export function normalizeLanguageTag(value: unknown): string {
: '';
}
+/** Build a case-insensitive BCP 47 lookup while retaining canonical codes. */
+export function buildLanguageLookup(
+ entries: readonly T[],
+): ReadonlyMap {
+ return new Map(entries.map((entry) => [normalizeLanguageTag(entry.code), entry]));
+}
+
+const LANGUAGE_BY_CODE = buildLanguageLookup(LANGUAGE_REGISTRY);
+
/** Return the canonical registry entry for a locale code, if it is shipped. */
export function languageEntry(value: unknown): LanguageEntry | undefined {
return LANGUAGE_BY_CODE.get(normalizeLanguageTag(value));
diff --git a/test/i18n.test.mjs b/test/i18n.test.mjs
index 6cc31854..d64e5956 100644
--- a/test/i18n.test.mjs
+++ b/test/i18n.test.mjs
@@ -5,6 +5,8 @@ import { readdirSync, readFileSync } from 'node:fs';
import {
FALLBACK_LANGUAGE_CODE,
LANGUAGE_REGISTRY,
+ buildLanguageLookup,
+ languageEntry,
languageOptions,
normalizeLanguageTag,
resolveLanguageCode,
@@ -38,6 +40,16 @@ test('i18n: registry codes and English fallback are valid', () => {
}
});
+test('i18n: canonical regional codes use normalized lookup keys', () => {
+ const regional = { code: 'pt-BR', nativeLabel: 'Português (Brasil)', dictionary: {} };
+ const lookup = buildLanguageLookup([regional]);
+ assert.equal(lookup.get('pt-br'), regional);
+ assert.equal(lookup.get(normalizeLanguageTag('PT_br')), regional);
+ for (const entry of LANGUAGE_REGISTRY) {
+ assert.equal(languageEntry(entry.code.toUpperCase()), entry);
+ }
+});
+
test('i18n: registry matches frontend and backend locale files', () => {
const registryCodes = LANGUAGE_REGISTRY.map(({ code }) => code).sort();
const localeCodes = (url) => readdirSync(url)