UNPKG

@shoelace-style/localize

Version:

A micro library for localizing custom elements using Lit's Reactive Controller model.

112 lines (111 loc) 4.08 kB
const connectedElements = new Set(); const translations = new Map(); let fallback; let documentDirection = 'ltr'; let documentLanguage = 'en'; const isClient = (typeof MutationObserver !== "undefined" && typeof document !== "undefined" && typeof document.documentElement !== "undefined"); if (isClient) { const documentElementObserver = new MutationObserver(update); documentDirection = document.documentElement.dir || 'ltr'; documentLanguage = document.documentElement.lang || navigator.language; documentElementObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir', 'lang'] }); } export function registerTranslation(...translation) { translation.map(t => { const code = t.$code.toLowerCase(); if (translations.has(code)) { translations.set(code, Object.assign(Object.assign({}, translations.get(code)), t)); } else { translations.set(code, t); } if (!fallback) { fallback = t; } }); update(); } export function update() { if (isClient) { documentDirection = document.documentElement.dir || 'ltr'; documentLanguage = document.documentElement.lang || navigator.language; } [...connectedElements.keys()].map((el) => { if (typeof el.requestUpdate === 'function') { el.requestUpdate(); } }); } export class LocalizeController { constructor(host) { this.host = host; this.host.addController(this); } hostConnected() { connectedElements.add(this.host); } hostDisconnected() { connectedElements.delete(this.host); } dir() { return `${this.host.dir || documentDirection}`.toLowerCase(); } lang() { return `${this.host.lang || documentLanguage}`.toLowerCase(); } getTranslationData(lang) { var _a, _b; const locale = new Intl.Locale(lang.replace(/_/g, '-')); const language = locale === null || locale === void 0 ? void 0 : locale.language.toLowerCase(); const region = (_b = (_a = locale === null || locale === void 0 ? void 0 : locale.region) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; const primary = translations.get(`${language}-${region}`); const secondary = translations.get(language); return { locale, language, region, primary, secondary }; } exists(key, options) { var _a; const { primary, secondary } = this.getTranslationData((_a = options.lang) !== null && _a !== void 0 ? _a : this.lang()); options = Object.assign({ includeFallback: false }, options); if ((primary && primary[key]) || (secondary && secondary[key]) || (options.includeFallback && fallback && fallback[key])) { return true; } return false; } term(key, ...args) { const { primary, secondary } = this.getTranslationData(this.lang()); let term; if (primary && primary[key]) { term = primary[key]; } else if (secondary && secondary[key]) { term = secondary[key]; } else if (fallback && fallback[key]) { term = fallback[key]; } else { console.error(`No translation found for: ${String(key)}`); return String(key); } if (typeof term === 'function') { return term(...args); } return term; } date(dateToFormat, options) { dateToFormat = new Date(dateToFormat); return new Intl.DateTimeFormat(this.lang(), options).format(dateToFormat); } number(numberToFormat, options) { numberToFormat = Number(numberToFormat); return isNaN(numberToFormat) ? '' : new Intl.NumberFormat(this.lang(), options).format(numberToFormat); } relativeTime(value, unit, options) { return new Intl.RelativeTimeFormat(this.lang(), options).format(value, unit); } }