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