##// END OF EJS Templates
i18n corrected default locale selection, more verbose error reporting
cin -
r55:dd0d589acfbb v1.0.8 default
parent child
Show More
@@ -1,82 +1,81
1 import { MapOf } from "@implab/core-amd/interfaces";
1 import { MapOf, PromiseOrValue } from "@implab/core-amd/interfaces";
2 import { isPromise, mixin } from "@implab/core-amd/safe";
2 import { argumentNotEmptyString, isPromise, mixin } from "@implab/core-amd/safe";
3 import { locale as sysLocale } from "dojo/_base/kernel";
4 import { id as mid } from "module";
3 import { id as mid } from "module";
5 import { TraceSource } from "@implab/core-amd/log/TraceSource";
4 import { TraceSource } from "@implab/core-amd/log/TraceSource";
6
5
7 const trace = TraceSource.get(mid);
6 const trace = TraceSource.get(mid);
8
7
9 type PromiseOrValue<T> = PromiseLike<T> | T;
8 export type LocaleProvider<T> = () => PromiseOrValue<T | { default: T }>;
9
10 type ResolveCallback<T> = () => PromiseOrValue<T>;
10 type ResolveCallback<T> = () => PromiseOrValue<T>;
11
11
12 trace.debug("Current sysLocale: {0}", sysLocale);
13
14 function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> {
12 function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> {
15 return isPromise(value) ?
13 return isPromise(value) ?
16 value.then(cb) :
14 value.then(cb) :
17 cb(value);
15 cb(value);
18 }
16 }
19
17
20 function isCallback<T>(v: ResolveCallback<T> | PromiseOrValue<T>): v is ResolveCallback<T> {
18 function isCallback<T>(v: ResolveCallback<T> | PromiseOrValue<T>): v is ResolveCallback<T> {
21 return typeof v === "function";
19 return typeof v === "function";
22 }
20 }
23
21
24 function defaultResolver(module: string) {
22 function defaultResolver(module: string) {
25 return import(module).then(x => x && x.default ? x.default : x);
23 return import(module).then(x => x && x.default ? x.default : x);
26 }
24 }
27
25
28 function chainObjects<T extends object>(o1: T, o2: T) {
26 function chainObjects<T extends object>(o1: T, o2: T) {
29 if (!o1)
27 if (!o1)
30 return o2;
28 return o2;
31 if (!o2)
29 if (!o2)
32 return o1;
30 return o1;
33
31
34 return mixin(Object.create(o1) as T, o2);
32 return mixin(Object.create(o1) as T, o2);
35 }
33 }
36
34
37 export class NlsBundle<T extends object> {
35 export class NlsBundle<T extends object> {
38 private _locales: MapOf<ResolveCallback<T> | PromiseOrValue<T>>;
36 private _locales: MapOf<LocaleProvider<Partial<T>>>;
39
37
40 private _default: T;
38 private _default: T;
41
39
42 private _cache: MapOf<PromiseOrValue<T>>;
40 private _cache: MapOf<PromiseOrValue<T>>;
43
41
44 constructor(defNls: T, locales?: MapOf<any>) {
42 constructor(defNls: T, locales?: MapOf<LocaleProvider<Partial<T>>>) {
45 this._default = defNls;
43 this._default = defNls;
46 this._locales = locales || {};
44 this._locales = locales || {};
47 this._cache = {};
45 this._cache = {};
48 }
46 }
49
47
50 getLocale(locale?: string) {
48 getLocale(locale: string) {
51 const _loc = locale ?? sysLocale;
49 argumentNotEmptyString(locale, "locale");
50 const _loc = locale;
52
51
53 // en-US => ["en", "en-US"]
52 // en-US => ["en", "en-US"]
54 const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-"));
53 const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-"));
55 return this._resolveLocale(locales);
54 return this._resolveLocale(locales);
56 }
55 }
57
56
58 _resolveLocale(locales: string[]): PromiseOrValue<T> {
57 _resolveLocale(locales: string[]): PromiseOrValue<T> {
59 if (!locales.length)
58 if (!locales.length)
60 return this._default;
59 return this._default;
61
60
62 const locale = locales.pop();
61 const locale = locales.pop();
63 if (!locale)
62 if (!locale)
64 throw new Error("The locale can't be empty");
63 throw new Error("The locale can't be empty");
65
64
66 if (this._cache[locale])
65 if (this._cache[locale])
67 return this._cache[locale];
66 return this._cache[locale];
68
67
69 const data = this._loadPackage(this._locales[locale]);
68 const data = this._loadPackage(this._locales[locale]);
70 const parent = this._resolveLocale(locales);
69 const parent = this._resolveLocale(locales);
71
70
72 return this._cache[locale] = when(data, x => {
71 return this._cache[locale] = when(data, x => {
73 return when(parent, y => this._cache[locale] = chainObjects(y, x));
72 return when(parent, y => this._cache[locale] = chainObjects(y, x));
74 });
73 });
75 }
74 }
76
75
77 _loadPackage(localeData: any) {
76 _loadPackage(localeData: any) {
78 if (isCallback(localeData))
77 if (isCallback(localeData))
79 return when(localeData(), data => data && "default" in data ? data.default : data);
78 return when(localeData(), data => data && "default" in data ? data.default : data);
80 return localeData;
79 return localeData;
81 }
80 }
82 }
81 }
@@ -1,38 +1,51
1 import { id as mid} from "module";
1 import { MapOf } from "@implab/core-amd/interfaces";
2 import { MapOf } from "@implab/core-amd/interfaces";
2 import { NlsBundle } from "./NlsBundle";
3 import { NlsBundle } from "./NlsBundle";
3 import { isPromise } from "@implab/core-amd/safe";
4 import { isPromise } from "@implab/core-amd/safe";
5 import { locale as sysLocale } from "dojo/_base/kernel";
6 import { TraceSource } from "@implab/core-amd/log/TraceSource";
7
8 const trace = TraceSource.get(mid);
9
10 trace.debug("Current sysLocale: {0}", sysLocale);
4
11
5 export interface OnLoad {
12 export interface OnLoad {
6 (result?: any): void;
13 (result?: any): void;
7 error(err: any): void;
14 error(err: any): void;
8 }
15 }
9
16
10 export function bundle<T extends object>(nls: T, locales?: MapOf<any>) {
17 export function bundle<T extends object>(nls: T, locales?: MapOf<any>) {
11 const nlsBundle = new NlsBundle(nls, locales);
18 const nlsBundle = new NlsBundle(nls, locales);
12
19
13 const fn = (locale?: string) => {
20 const fn = (_locale?: string) => {
21 const locale = _locale || sysLocale;
14 const result = nlsBundle.getLocale(locale);
22 const result = nlsBundle.getLocale(locale);
15
23
16 if (isPromise(result))
24 if (isPromise(result))
17 throw new Error(`The bundle '${locale}' isn't loaded`);
25 throw new Error(`The bundle '${locale}' isn't loaded`);
18 else
26 else
19 return result;
27 return result;
20 };
28 };
21
29
22 fn.define = (pack: Partial<T>) => pack;
30 fn.define = (pack: Partial<T>) => pack;
23 fn.load = async (id: string, require: Require, cb: OnLoad, config: any) => {
31 fn.load = async (id: string, require: Require, cb: OnLoad, config: any) => {
32 const locale = id || sysLocale;
24 if (config && config.isBuild) {
33 if (config && config.isBuild) {
25 cb();
34 cb();
26 } else {
35 } else {
27 try {
36 try {
28 await nlsBundle.getLocale(id);
37 await nlsBundle.getLocale(locale);
29 cb();
38 cb();
30 } catch (e) {
39 } catch (e) {
31 if(cb.error)
40 if(cb.error) {
32 cb.error(e);
41 cb.error(e);
42 } else {
43 // in case the loader doesn't support error reporting
44 trace.error("Error loading {0}: {1}", locale, e);
45 }
33 }
46 }
34 }
47 }
35 };
48 };
36
49
37 return fn;
50 return fn;
38 }
51 }
@@ -1,8 +1,9
1 import { bundle } from "../i18n";
1 import { bundle } from "../i18n";
2
2
3 export default bundle({
3 export default bundle({
4 greeting: (name: string) => `Hello, ${name}!`,
4 greeting: (name: string) => `Hello, ${name}!`,
5 goodbye: (name: string) => `Bye, ${name}!`
5 goodbye: (name: string) => `Bye, ${name}!`
6 }, {
6 }, {
7 ru: () => import("./ru/foo")
7 ru: () => import("./ru/foo")
8 });
8 });
9
General Comments 0
You need to be logged in to leave comments. Login now