language-resolution
NPM 1.9.0
Overview
The LanguageResolutionController is a Lit reactive controller that automatically resolves and tracks the language/locale context of a web component. It detects language changes up the DOM tree, including across shadow DOM boundaries, making it essential for internationalized applications.
Usage
yarn add @spectrum-web-components/reactive-controllers
Import the LanguageResolutionController via:
import { LanguageResolutionController } from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js';
Key features
- Automatic language detection: Resolves
langattribute from DOM tree - Locale change tracking: Triggers updates when language context changes
- Shadow DOM support: Works across shadow boundaries via event bubbling
- Fallback handling: Uses
navigator.languageor defaults toen-US - Validation: Ensures locale is supported by
IntlAPIs
When to use
Use LanguageResolutionController when your component needs to:
- Format numbers based on locale
- Format dates and times according to locale conventions
- Display localized content or messages
- Determine text direction (RTL/LTR)
- Apply locale-specific formatting rules
Examples
Automatic language detection
The controller automatically detects the language from the DOM tree without requiring manual configuration:
import { LitElement, html } from 'lit'; import { LanguageResolutionController } from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js'; class LocalizedGreeting extends LitElement { private languageResolver = new LanguageResolutionController(this); render() { const greetings = { en: 'Hello', es: 'Hola', fr: 'Bonjour', de: 'Guten Tag', ja: 'こんにちは', }; // Get base language code (e.g., 'en' from 'en-US') const lang = this.languageResolver.language.split('-')[0]; const greeting = greetings[lang] || greetings['en']; return html` <p> ${greeting}, World! <span class="locale">(${this.languageResolver.language})</span> </p> `; } } customElements.define('localized-greeting', LocalizedGreeting);
Usage:
<!-- Spanish context --> <div lang="es-ES"> <localized-greeting></localized-greeting> <!-- Renders: Hola, World! (es-ES) --> </div>
Locale change tracking
The controller automatically re-renders components when the language context changes:
import { LitElement, html } from 'lit'; import { LanguageResolutionController, languageResolverUpdatedSymbol, } from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js'; class LanguageTracker extends LitElement { private languageResolver = new LanguageResolutionController(this); private updateCount = 0; protected updated(changedProperties: Map<PropertyKey, unknown>): void { super.updated(changedProperties); // Detect when language has changed if (changedProperties.has(languageResolverUpdatedSymbol)) { this.updateCount++; console.log('Language changed to:', this.languageResolver.language); } } render() { return html` <div> <p> Current language: <strong>${this.languageResolver.language}</strong> </p> <p>Change count: ${this.updateCount}</p> </div> `; } } customElements.define('language-tracker', LanguageTracker);
Supports shadow DOM
The controller works across shadow DOM boundaries using event bubbling:
import { LitElement, html } from 'lit'; import { LanguageResolutionController } from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js'; // Component with shadow DOM class LocalizedCard extends LitElement { private languageResolver = new LanguageResolutionController(this); render() { const lang = this.languageResolver.language; return html` <div class="card"> <h3>Language: ${lang}</h3> <slot></slot> </div> `; } } customElements.define('localized-card', LocalizedCard);
Bubbles up DOM tree
The controller searches up through parent elements to find language context:
<!-- Finds lang attribute several levels up --> <article lang="ja-JP"> <section> <div> <aside> <localized-greeting></localized-greeting> <!-- Output: こんにちは, World! (ja-JP) --> </aside> </div> </section> </article> <!-- Uses closest ancestor with lang attribute --> <div lang="en-US"> <section lang="es-ES"> <localized-greeting></localized-greeting> <!-- Output: Hola, World! (es-ES) - uses closest lang --> </section> </div> <!-- Bubbles through custom elements --> <sp-theme lang="de-DE"> <sp-card> <sp-dialog> <localized-greeting></localized-greeting> <!-- Output: Guten Tag, World! (de-DE) --> </sp-dialog> </sp-card> </sp-theme>
How it works
The controller follows this resolution process:
- On connection: Dispatches
sp-language-contextevent that bubbles up the DOM - Theme provider response:
<sp-theme>or other context providers respond with theirlangvalue - Callback registration: Provider calls the callback with language and unsubscribe function
- Validation: Language is validated using
Intl.DateTimeFormat.supportedLocalesOf() - Fallback: If validation fails, falls back to
document.documentElement.lang,navigator.language, oren-US - Updates: When language changes, triggers a component update via
requestUpdate()
Related components
Components in Spectrum Web Components that use LanguageResolutionController:
- Number input with locale formatting<sp-number-field> - Slider with localized values<sp-slider> - Meter with formatted values<sp-meter> - Progress with formatted percentage<sp-progress-bar> - Color picker with locale support<sp-color-wheel> - Color slider with formatted values<sp-color-slider> - Color area with locale support<sp-color-area>
Resources
Intl.NumberFormat - MDN Intl.DateTimeFormat - MDN Language tags (BCP 47) WCAG - Language of Page