// Locale picker. Option labels come from `Intl.DisplayNames` in each locale's own // language (endonyms), so the list needs no translation catalog of its own. // Core's, never the i18n barrel's: that barrel installs the framework catalog at import, and this // component ships in browser chunks (issue #490). `localizePath` is what i18n's `localizedPath` is. import { localizePath } from '@ultimat3/core'; import type { Locale } from '@ultimat3/i18n'; import type { JSX } from 'solid-js'; import { cx } from '../cx'; import { UI_KEYS } from '../i18n-keys'; import { useUi } from '../theme/context'; import styles from './LocaleSwitcher.module.scss'; import { Select, type SelectOption } from './Select'; export interface LocaleSwitcherProps { /** Every locale to offer, the DEFAULT first — `routedLocales()` from `@ultimat3/i18n`. */ locales: readonly Locale[]; /** Defaults to the context locale. */ value?: Locale | undefined; onLocaleChange?: ((locale: Locale) => void) | undefined; /** * Render as links instead of a select, for a 0kb-JS `site/` route. Defaults, when `path` is * given, to that page in each locale — `localizedPath`: unprefixed for the default, `/en/…` * otherwise — so a site's switcher needs no href of its own. */ hrefFor?: ((locale: Locale) => string) | undefined; /** The page's own path, prefixed or not — `new URL(props.url).pathname`. Turns on links mode. */ path?: string | undefined; /** The unprefixed locale. Defaults to `locales[0]`, which is where `routedLocales()` puts it. */ defaultLocale?: Locale | undefined; class?: string | undefined; } /** Endonym: the language's name in its own language, e.g. `de` -> "Deutsch". */ export function localeLabel(tag: Locale): string { const names = new Intl.DisplayNames([tag], { type: 'language' }); return names.of(tag) ?? tag; } export function LocaleSwitcher(props: LocaleSwitcherProps): JSX.Element { const ui = useUi(); const current = (): Locale => props.value ?? ui.locale; const options = (): SelectOption[] => props.locales.map((tag) => ({ value: tag, label: localeLabel(tag) })); const hrefFor = (): ((locale: Locale) => string) | undefined => { if (props.hrefFor !== undefined) return props.hrefFor; const path = props.path; if (path === undefined) return undefined; const fallback = props.defaultLocale ?? props.locales[0] ?? current(); return (locale) => localizePath(path, locale, props.locales, fallback); }; const linkFor = hrefFor(); if (linkFor !== undefined) { return ( ); } return (