Speak every language in the component, not just English

The component bundled English alone: the standalone app fetches its
locale files at runtime from URLs its own build emits, which a host
serving the library from elsewhere could not resolve, so bundling one
language was the self-contained option. Now every locale is a chunk of
its own that the host's bundler loads the first time it is needed, with
English still bundled in so that the fallback never waits.

Element Call starts in the browser's language and follows the host's own
setting through a `language` prop; `supportedLanguages` says what it
accepts. Translations are shared by every Element Call on the page, so
the most recently set language wins for all of them. The harness gets a
language picker, and the app and the component share the parsing of
locale paths.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Timo K.
2026-09-08 16:03:48 +02:00
co-authored by Claude Fable 5.1
parent b7285064fd
commit f994586eeb
7 changed files with 192 additions and 30 deletions
+2 -12
View File
@@ -37,7 +37,7 @@ import {
type AnalyticsConfig,
PosthogAnalytics,
} from "./analytics/PosthogAnalytics.ts";
import { i18n } from "./utils/i18n.ts";
import { i18n, languageOfLocalePath } from "./utils/i18n.ts";
// This generates a map of locale names to their URL (based on import.meta.url), which looks like this:
// {
@@ -56,17 +56,7 @@ const getLocaleUrl = (
): string | undefined => locales[`../locales/${language}/${namespace}.json`];
const supportedLngs = [
...new Set(
Object.keys(locales).map((url) => {
// The URLs are of the form ../locales/en/app.json
// This extracts the language code from the URL
const lang = url.match(/\/([^/]+)\/[^/]+\.json$/)?.[1];
if (!lang) {
throw new Error(`Could not parse locale URL ${url}`);
}
return lang;
}),
),
...new Set(Object.keys(locales).map(languageOfLocalePath)),
];
// A backend that fetches the locale files from the URLs generated by the glob above
+11
View File
@@ -10,6 +10,17 @@ import i18next, { type i18n as I18nInstance } from "i18next";
// Custom marker function to allow i18next extraction
export const i18nKey = (key: string): string => key;
/**
* The language a locale file under `locales/` is for, from its path as a
* bundler glob reports it: `../locales/zh-Hans/app.json` is for `zh-Hans`.
*/
export function languageOfLocalePath(path: string): string {
const language = path.match(/\/([^/]+)\/[^/]+\.json$/)?.[1];
if (language === undefined)
throw new Error(`Could not parse locale path ${path}`);
return language;
}
/**
* Element Call's own i18next instance.
*