Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/core/I18nInjector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { LocaleData } from './interfaces';
import { setLocale, getComputedLocale } from '../i18n/i18n';
import { Injector } from './Injector';
import Registry from './Registry';
import { isThenable } from '../shim/Promise';

export const INJECTOR_KEY = '__i18n_injector';

export class I18nInjector extends Injector {
set(localeData: LocaleData = {}) {
const result = setLocale({ locale: localeData.locale || getComputedLocale() });
if (isThenable(result)) {
result.then(() => {
super.set(localeData);
});
return;
}
super.set(localeData);
}
}

export function registerI18nInjector(localeData: LocaleData, registry: Registry): I18nInjector {
const injector = new I18nInjector(localeData);
registry.defineInjector(INJECTOR_KEY, (invalidator) => {
injector.setInvalidator(invalidator);
return () => injector;
});
return injector;
}

export default I18nInjector;
31 changes: 6 additions & 25 deletions src/core/middleware/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import { localizeBundle, Bundle, Messages, setLocale, getCurrentLocale } from '../../i18n/i18n';
import { create, invalidator, getRegistry, diffProperty } from '../vdom';
import injector from './injector';
import Injector from '../Injector';
import Registry from '../Registry';
import { I18nProperties, LocalizedMessages, LocaleData } from '../interfaces';
import { isThenable } from '../../shim/Promise';
import I18nInjector, { INJECTOR_KEY, registerI18nInjector } from '../I18nInjector';

export const INJECTOR_KEY = '__i18n_injector';

export function registerI18nInjector(localeData: LocaleData, registry: Registry): Injector {
const injector = new Injector(localeData);
registry.defineInjector(INJECTOR_KEY, (invalidator) => {
injector.setInvalidator(invalidator);
return () => injector;
});
return injector;
}
export { INJECTOR_KEY, registerI18nInjector } from '../I18nInjector';

const factory = create({ invalidator, injector, getRegistry, diffProperty }).properties<I18nProperties>();

Expand All @@ -29,7 +19,7 @@ export const i18n = factory(({ properties, middleware: { invalidator, injector,
}

diffProperty('locale', properties, (current, next) => {
const localeDataInjector = injector.get<Injector<LocaleData | undefined>>(INJECTOR_KEY);
const localeDataInjector = injector.get<I18nInjector>(INJECTOR_KEY);
let injectedLocale: string | undefined;
if (localeDataInjector) {
const injectLocaleData = localeDataInjector.get();
Expand Down Expand Up @@ -66,23 +56,14 @@ export const i18n = factory(({ properties, middleware: { invalidator, injector,
}
return localizeBundle(bundle, { locale, invalidator });
},
set(localeData?: LocaleData) {
const localeDataInjector = injector.get<Injector<LocaleData | undefined>>(INJECTOR_KEY);
set(localeData: LocaleData = {}) {
const localeDataInjector = injector.get<I18nInjector>(INJECTOR_KEY);
if (localeDataInjector) {
if (localeData && localeData.locale) {
const result = setLocale({ locale: localeData.locale });
if (isThenable(result)) {
result.then(() => {
localeDataInjector.set(localeData);
});
return;
}
}
localeDataInjector.set(localeData);
}
},
get() {
const localeDataInjector = injector.get<Injector<LocaleData | undefined>>(INJECTOR_KEY);
const localeDataInjector = injector.get<I18nInjector>(INJECTOR_KEY);
if (localeDataInjector) {
return localeDataInjector.get();
}
Expand Down
29 changes: 2 additions & 27 deletions src/core/mixins/I18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import { afterRender } from './../decorators/afterRender';
import { getInjector } from './../decorators/inject';
import { Constructor, DNode, VNodeProperties, LocalizedMessages, I18nProperties, LocaleData } from './../interfaces';
import { Injector } from './../Injector';
import { Registry } from './../Registry';
import { WidgetBase } from './../WidgetBase';
import { decorate } from '../util';
import { isThenable } from '../../shim/Promise';
import beforeProperties from '../decorators/beforeProperties';
import { INJECTOR_KEY } from '../I18nInjector';

export { LocalizedMessages, I18nProperties, LocaleData } from './../interfaces';

export const INJECTOR_KEY = '__i18n_injector';
export { INJECTOR_KEY, registerI18nInjector } from '../I18nInjector';

/**
* interface for I18n functionality
Expand Down Expand Up @@ -45,30 +44,6 @@ interface I18nVNodeProperties extends VNodeProperties {
lang: string;
}

class I18nInjector extends Injector {
set(localeData: LocaleData) {
if (localeData.locale) {
const result = setLocale({ locale: localeData.locale });
if (isThenable(result)) {
result.then(() => {
super.set(localeData);
});
return;
}
super.set(localeData);
}
}
}

export function registerI18nInjector(localeData: LocaleData, registry: Registry): I18nInjector {
const injector = new I18nInjector(localeData);
registry.defineInjector(INJECTOR_KEY, (invalidator) => {
injector.setInvalidator(invalidator);
return () => injector;
});
return injector;
}

const previousLocaleMap: WeakMap<WidgetBase, string> = new WeakMap();

export function I18nMixin<T extends Constructor<WidgetBase<any>>>(Base: T): T & Constructor<I18nMixin> {
Expand Down
1 change: 1 addition & 0 deletions src/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ function registerBundle<T extends Messages>(bundle: Bundle<T>): string {
Globalize.loadMessages({
root: { [bundleId]: bundle.messages },
[computedLocale]: { [bundleId]: bundle.messages },
[defaultLocale]: { [bundleId]: bundle.messages },
...messageBundles
});
Cldr.load({ dojo: lookup });
Expand Down
24 changes: 19 additions & 5 deletions tests/core/unit/middleware/i18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,11 @@ describe('i18n middleware', () => {
es: overrideEs.loader
}
};
const App = factory(({ middleware: { i18n } }) => {
const App = factory(({ properties, middleware: { i18n } }) => {
const { messages, format, isPlaceholder } = i18n.localize(bundle);
return (
<div>
<div>{properties().locale}</div>
<div>{JSON.stringify(messages)}</div>
<div>{format('foo', { name: 'John' })}</div>
<div>{`${isPlaceholder}`}</div>
Expand All @@ -279,6 +280,13 @@ describe('i18n middleware', () => {
>
es
</button>
<button
onclick={() => {
i18n.set();
}}
>
reset
</button>
</div>
);
});
Expand All @@ -288,21 +296,27 @@ describe('i18n middleware', () => {
r.mount({ domNode: root });
assert.strictEqual(
root.innerHTML,
'<div><div>{"foo":"Oi, {name}"}</div><div>Oi, John</div><div>false</div><div>{}</div><button>es</button></div>'
'<div><div>en</div><div>{"foo":"Oi, {name}"}</div><div>Oi, John</div><div>false</div><div>{}</div><button>es</button><button>reset</button></div>'
);
root.children[0].children[4].click();
root.children[0].children[5].click();
await localeLoader;
resolvers.resolveRAF();
assert.strictEqual(
root.innerHTML,
'<div><div>{"foo":""}</div><div></div><div>true</div><div>{"locale":"es"}</div><button>es</button></div>'
'<div><div>es</div><div>{"foo":""}</div><div></div><div>true</div><div>{"locale":"es"}</div><button>es</button><button>reset</button></div>'
);
overrideEs.resolver({ default: { foo: 'bonjour, {name}' } });
await overrideEs.promise;
resolvers.resolveRAF();
assert.strictEqual(
root.innerHTML,
'<div><div>{"foo":"bonjour, {name}"}</div><div>bonjour, John</div><div>false</div><div>{"locale":"es"}</div><button>es</button></div>'
'<div><div>es</div><div>{"foo":"bonjour, {name}"}</div><div>bonjour, John</div><div>false</div><div>{"locale":"es"}</div><button>es</button><button>reset</button></div>'
);
root.children[0].children[6].click();
resolvers.resolveRAF();
assert.strictEqual(
root.innerHTML,
'<div><div>en</div><div>{"foo":"Oi, {name}"}</div><div>Oi, John</div><div>false</div><div>{}</div><button>es</button><button>reset</button></div>'
);
});
});