import { MapOf, PromiseOrValue } from "@implab/core-amd/interfaces"; import { argumentNotEmptyString, isPromise, mixin } from "@implab/core-amd/safe"; import { id as mid } from "module"; import { TraceSource } from "@implab/core-amd/log/TraceSource"; const trace = TraceSource.get(mid); export type LocaleProvider = () => PromiseOrValue; type ResolveCallback = () => PromiseOrValue; function when(value: PromiseOrValue, cb: (v: T) => PromiseOrValue): PromiseOrValue { return isPromise(value) ? value.then(cb) : cb(value); } function isCallback(v: ResolveCallback | PromiseOrValue): v is ResolveCallback { return typeof v === "function"; } function defaultResolver(module: string) { return import(module).then(x => x && x.default ? x.default : x); } function chainObjects(o1: T, o2: T) { if (!o1) return o2; if (!o2) return o1; return mixin(Object.create(o1) as T, o2); } export class NlsBundle { private _locales: MapOf>>; private _default: T; private _cache: MapOf>; constructor(defNls: T, locales?: MapOf>>) { this._default = defNls; this._locales = locales || {}; this._cache = {}; } getLocale(locale: string) { argumentNotEmptyString(locale, "locale"); const _loc = locale; // en-US => ["en", "en-US"] const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-")); return this._resolveLocale(locales); } _resolveLocale(locales: string[]): PromiseOrValue { if (!locales.length) return this._default; const locale = locales.pop(); if (!locale) throw new Error("The locale can't be empty"); if (this._cache[locale]) return this._cache[locale]; const data = this._loadPackage(this._locales[locale]); const parent = this._resolveLocale(locales); return this._cache[locale] = when(data, x => { return when(parent, y => this._cache[locale] = chainObjects(y, x)); }); } _loadPackage(localeData: any) { if (isCallback(localeData)) return when(localeData(), data => data && "default" in data ? data.default : data); return localeData; } }