Skip to content

Commit

Permalink
feat(i18n): all binding behavior
Browse files Browse the repository at this point in the history
This commit adds the `t`, `df`, `nf`, and `rt` binding behavior.
The e2e tests are addd for all. THe integration tests sare added
only for `t` binding behavior. For the rest, the test will be added
in future commits.
  • Loading branch information
Sayan751 committed Aug 12, 2019
1 parent e4dfb10 commit f002dd7
Show file tree
Hide file tree
Showing 19 changed files with 225 additions and 45 deletions.
29 changes: 15 additions & 14 deletions packages/__tests__/e2e/src/plugins/sut-i18n.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,26 @@

<span id="i18n-prepend-append" t="[prepend]pretest;[append]post-test">Blue</span><br><br>

<img id="i18n-img" t="imgPath" />
<img id="i18n-img" t="imgPath" /><br><br>

<span id="i18n-t-vc"> ${'itemWithCount' | t : {count: 10}} </span>
<!--<span id="i18n-t-bb"> ${'itemWithCount' & t : {count: 100}} </span>-->
<span id="i18n-t-vc"> ${'itemWithCount' | t : {count: 10}} </span><br>
<span id="i18n-t-bb"> ${'itemWithCount' & t : {count: 100}} </span><br>
<span id="i18n-t-bb-attr" title.bind="'itemWithCount' & t : {count: 100}"></span><br><br>

<span id="i18n-nf-vc"> ${ 123456789.12 | nf } </span>
<!--<span id="i18n-nf-bb"> ${ 123456789.12 & nf : undefined : 'de'} </span>-->
<span id="i18n-nf-vc"> ${ 123456789.12 | nf } </span><br>
<span id="i18n-nf-bb"> ${ 123456789.12 & nf : undefined : 'de'} </span><br><br>

<span id="i18n-nf-vc-cur"> ${ 123456789.12 | nf: {style:'currency', currency: 'EUR' } : 'de' } </span>
<!--<span id="i18n-nf-bb-cur"> ${ 123456789.12 & nf: {style:'currency', currency: 'USD' } : locale } </span>-->
<span id="i18n-nf-vc-cur"> ${ 123456789.12 | nf: {style:'currency', currency: 'EUR' } : 'de' } </span><br>
<span id="i18n-nf-bb-cur"> ${ 123456789.12 & nf: {style:'currency', currency: 'USD' } : locale } </span><br><br>

<span id="i18n-df-vc"> ${ dispatchedOn | df } </span>
<span id="i18n-df-vc-iso"> ${ '2019-08-10T13:42:35.209Z' | df } </span>
<span id="i18n-df-vc-int"> ${ 0 | df } </span>
<span id="i18n-df-vc-int-str"> ${ '0' | df } </span>
<!-- <span id="i18n-df-bb"> ${ dispatchedOn & df : undefined : 'de'} </span>-->
<span id="i18n-df-vc"> ${ dispatchedOn | df } </span><br>
<span id="i18n-df-vc-iso"> ${ '2019-08-10T13:42:35.209Z' | df } </span><br>
<span id="i18n-df-vc-int"> ${ 0 | df } </span><br>
<span id="i18n-df-vc-int-str"> ${ '0' | df } </span><br>
<span id="i18n-df-bb"> ${ dispatchedOn & df : undefined : 'de'} </span><br><br>

<span id="i18n-rt-vc"> ${ myDate | rt} </span>
<!-- <span id="i18n-rt-bb"> ${ myDate & rt} </span>-->
<span id="i18n-rt-vc"> ${ myDate | rt} </span><br>
<span id="i18n-rt-bb"> ${ myDate & rt} </span><br><br>

<!-- translations via code -->
<div>
Expand Down
3 changes: 3 additions & 0 deletions packages/__tests__/e2e/src/plugins/sut-i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ export class SutI18N {
public params = { context: 'delivered', date: this.deliveredOn };
public translations: { [key: string]: string | number };
private readonly myDate: Date;
private locale: string;
constructor(
@I18N private readonly i18n: I18nService,
@ISignaler private readonly signaler: ISignaler
) {
this.locale = this.i18n.getLocale();
this.myDate = new Date();
this.myDate.setHours(this.myDate.getHours() - 2);

Expand Down Expand Up @@ -43,6 +45,7 @@ export class SutI18N {
}
public async changeLocale(locale) {
await this.i18n.setLocale(locale);
this.locale = locale;
}

public changeMyDate() {
Expand Down
31 changes: 21 additions & 10 deletions packages/__tests__/e2e/tests/integration/i18n.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ describe('i18n', () => {
name: 'should work with "t" value converter',
suts: [{ selector: `#i18n-t-vc`, expected: ' 10 items ', expectedDe: ' 10 Artikel ' }]
},
{
name: 'should work with "t" binding behavior',
suts: [{ selector: `#i18n-t-bb`, expected: ' 100 items ', expectedDe: ' 100 Artikel ' },]
},
{
name: 'should work with "df" value converter',
suts: [{ selector: `#i18n-df-vc`, expected: ' 2/10/2020 ', expectedDe: ' 10.2.2020 ' }]
Expand All @@ -153,26 +157,34 @@ describe('i18n', () => {
name: 'should work with "df" value converter for integer string',
suts: [{ selector: `#i18n-df-vc-int-str`, expected: ' 1/1/1970 ', expectedDe: ' 1.1.1970 ' }]
},
{
name: 'should work with "df" binding behavior',
suts: [{ selector: `#i18n-df-bb`, expected: " 10.2.2020 ", expectedDe: " 10.2.2020 " },]
},
{
name: 'should work with "nf" value converter',
suts: [{ selector: `#i18n-nf-vc`, expected: ' 123,456,789.12 ', expectedDe: ' 123.456.789,12 ' }]
},
// {
// name: 'should work with "nf" binding behavior',
// suts: [{ selector: `#i18n-nf-bb`, expected: " 123.456.789,12 ", expectedDe: " 123.456.789,12 " },]
// },
{
name: 'should work with "nf" binding behavior',
suts: [{ selector: `#i18n-nf-bb`, expected: ' 123.456.789,12 ', expectedDe: ' 123.456.789,12 ' }]
},
{
name: 'should work with "nf" value converter for currency',
suts: [{ selector: `#i18n-nf-vc-cur`, expected: ' 123.456.789,12\u{00a0}€ ', expectedDe: ' 123.456.789,12\u{00a0}€ ' }]
},
// {
// name: 'should work with "nf" binding behavior for currency',
// suts: [{ selector: `#i18n-nf-bb-cur`, expected: " $123,456,789.12 ", expectedDe: " 123.456.789,12\u{00a0}$ " },]
// },
{
name: 'should work with "nf" binding behavior for currency',
suts: [{ selector: `#i18n-nf-bb-cur`, expected: ' $123,456,789.12 ', expectedDe: ' 123.456.789,12\u{00a0}$ ' }]
},
{
name: 'should work with "rt" value converter',
suts: [{ selector: `#i18n-rt-vc`, expected: ' 2 hours ago ', expectedDe: ' vor 2 Stunden ' }]
},
{
name: 'should work with "rt" binding behavior',
suts: [{ selector: `#i18n-rt-bb`, expected: " 2 hours ago ", expectedDe: " vor 2 Stunden " },]
},
];

describe('translates via HTML that', () => {
Expand All @@ -185,9 +197,8 @@ describe('i18n', () => {
}
it('reacts to \'aurelia-relativetime-signal\' signal for relative time binding behavior', () => {
cy.get('#rt-changer').click();
// rt VC does not react to the signal, thus the content should stay same
assertContent('#i18n-rt-vc', ' 1 year ago ');
// assertContent('#i18n-rt-bb', ' 1 year ago ');
assertContent('#i18n-rt-bb', ' 1 year ago ');
});
});

Expand Down
59 changes: 59 additions & 0 deletions packages/__tests__/i18n/t/translation-integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,13 +542,15 @@ describe.only('translation-integration', function () {
name: 'app', template: `
<span id="a" title.bind="'simple.text' | t">t-vc-attr-target</span>
<span id="b" title="\${'simple.text' | t}">t-vc-attr-target</span>
<span id="c" title.bind="'itemWithCount' | t : {count:10}">t-vc-attr-target</span>
` })
class App { }

const host = DOM.createElement('app');
const { en: translation } = await setup(host, new App());
assertTextContent(host, `span#a[title='${translation.simple.text}']`, 't-vc-attr-target');
assertTextContent(host, `span#b[title='${translation.simple.text}']`, 't-vc-attr-target');
assertTextContent(host, `span#c[title='${translation.itemWithCount_plural.replace('{{count}}', '10')}']`, 't-vc-attr-target');
});
it.skip('locale is changed', async function () {

Expand All @@ -563,6 +565,63 @@ describe.only('translation-integration', function () {
});
});

describe('`t` binding-behavior works for', function () {
it('key as string literal', async function () {

@customElement({ name: 'app', template: `<span>\${'simple.text' & t}</span>` })
class App { }

const host = DOM.createElement('app');
const { en: translation } = await setup(host, new App());
assertTextContent(host, 'span', translation.simple.text);
});
it('key bound from vm property', async function () {

@customElement({ name: 'app', template: `<span>\${key & t}</span>` })
class App { key = 'simple.text'; }

const host = DOM.createElement('app');
const { en: translation } = await setup(host, new App());
assertTextContent(host, 'span', translation.simple.text);
});
it('with `t-params`', async function () {

@customElement({ name: 'app', template: `<span>\${'itemWithCount' & t : {count:10}}</span>` })
class App { }

const host = DOM.createElement('app');
const { en: translation } = await setup(host, new App());
assertTextContent(host, 'span', translation.itemWithCount_plural.replace('{{count}}', '10'));
});
it('attribute translation', async function () {

@customElement({
name: 'app', template: `
<span id="a" title.bind="'simple.text' & t">t-vc-attr-target</span>
<span id="b" title="\${'simple.text' & t}">t-vc-attr-target</span>
<span id="c" title.bind="'itemWithCount' & t : {count:10}">t-vc-attr-target</span>
` })
class App { }

const host = DOM.createElement('app');
const { en: translation } = await setup(host, new App());
assertTextContent(host, `span#a[title='${translation.simple.text}']`, 't-vc-attr-target');
assertTextContent(host, `span#b[title='${translation.simple.text}']`, 't-vc-attr-target');
assertTextContent(host, `span#c[title='${translation.itemWithCount_plural.replace('{{count}}', '10')}']`, 't-vc-attr-target');
});
it.skip('locale is changed', async function () {

@customElement({ name: 'app', template: `<span>\${'simple.text' & t}</span>` })
class App { }

const host = DOM.createElement('app');
const { i18n, de } = await setup(host, new App());

await i18n.setLocale('de');
assertTextContent(host, 'span', de.simple.text);
});
});

describe('`df` value-converter', function () {
const cases = [
{ name: 'works for date object', input: new Date(2019, 7, 20), output: '8/20/2019' },
Expand Down
55 changes: 42 additions & 13 deletions packages/i18n/src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,52 @@
import { IContainer, Registration } from '@aurelia/kernel';
import { DateFormatBindingBehavior } from './df/date-format-binding-behavior';
import { DateFormatValueConverter } from './df/date-format-value-converter';
import { I18N, I18nService } from './i18n';
import { I18nConfigurationOptions, I18nInitOptions } from './i18n-configuration-options';
import { I18nextWrapper, I18nWrapper } from './i18next-wrapper';
import { NumberFormatBindingBehavior } from './nf/number-format-binding-behavior';
import { NumberFormatValueConverter } from './nf/number-format-value-converter';
import { RelativeTimeBindingBehavior } from './rt/relative-time-binding-behavior';
import { RelativeTimeValueConverter } from './rt/relative-time-value-converter';
import { TranslationBindingBehavior } from './t/translation-binding-behavior';
import { TranslationParametersAttributePattern, TranslationParametersBindingCommand, TranslationParametersBindingRenderer } from './t/translation-parameters-renderer';
import { TranslationAttributePattern, TranslationBindAttributePattern, TranslationBindBindingCommand, TranslationBindBindingRenderer, TranslationBindingCommand, TranslationBindingRenderer } from './t/translation-renderer';
import { TranslationValueConverter } from './t/translation-value-converter';

export type I18NConfigOptionsProvider = (options: I18nConfigurationOptions) => void;

const renderers = [
TranslationAttributePattern,
TranslationBindingCommand,
TranslationBindingRenderer,
TranslationBindAttributePattern,
TranslationBindBindingCommand,
TranslationBindBindingRenderer,
TranslationParametersAttributePattern,
TranslationParametersBindingCommand,
TranslationParametersBindingRenderer
];

const translation = [
TranslationValueConverter,
TranslationBindingBehavior,
];

const dateFormat = [
DateFormatValueConverter,
DateFormatBindingBehavior,
];

const numberFormat = [
NumberFormatValueConverter,
NumberFormatBindingBehavior,
];

const relativeTimeFormat = [
RelativeTimeValueConverter,
RelativeTimeBindingBehavior,
];

function createI18nConfiguration(optionsProvider: I18NConfigOptionsProvider) {
return {
optionsProvider,
Expand All @@ -25,22 +61,15 @@ function createI18nConfiguration(optionsProvider: I18NConfigOptionsProvider) {
}

return container.register(
TranslationAttributePattern,
TranslationBindingCommand,
TranslationBindingRenderer,
TranslationBindAttributePattern,
TranslationBindBindingCommand,
TranslationBindBindingRenderer,
TranslationParametersAttributePattern,
TranslationParametersBindingCommand,
TranslationParametersBindingRenderer,
Registration.callback(I18nInitOptions, () => options.initOptions),
Registration.singleton(I18nWrapper, I18nextWrapper),
Registration.singleton(I18N, I18nService),
TranslationValueConverter,
DateFormatValueConverter,
NumberFormatValueConverter,
RelativeTimeValueConverter

...renderers,
...translation,
...dateFormat,
...numberFormat,
...relativeTimeFormat
);
},
customize(cb?: I18NConfigOptionsProvider) {
Expand Down
11 changes: 11 additions & 0 deletions packages/i18n/src/df/date-format-binding-behavior.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { bindingBehavior, IScope, LifecycleFlags } from '@aurelia/runtime';
import { BindingWithBehavior, createIntlFormatValueConverterExpression } from '../utils';
import { dateFormatValueConverterName } from './date-format-value-converter';

@bindingBehavior(dateFormatValueConverterName)
export class DateFormatBindingBehavior {

public bind(flags: LifecycleFlags, scope: IScope, binding: BindingWithBehavior) {
createIntlFormatValueConverterExpression(dateFormatValueConverterName, binding);
}
}
4 changes: 3 additions & 1 deletion packages/i18n/src/df/date-format-value-converter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { valueConverter } from '@aurelia/runtime';
import { I18N, I18N_SIGNAL, I18nService } from '../i18n';

@valueConverter('df')
export const dateFormatValueConverterName = 'df';

@valueConverter(dateFormatValueConverterName)
export class DateFormatValueConverter {
public readonly signals: string[] = [I18N_SIGNAL];

Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/df/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './date-format-binding-behavior';
export * from './date-format-value-converter';
1 change: 1 addition & 0 deletions packages/i18n/src/nf/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './number-format-binding-behavior';
export * from './number-format-value-converter';
11 changes: 11 additions & 0 deletions packages/i18n/src/nf/number-format-binding-behavior.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { bindingBehavior, IScope, LifecycleFlags } from '@aurelia/runtime';
import { BindingWithBehavior, createIntlFormatValueConverterExpression } from '../utils';
import { numberFormatValueConverterName } from './number-format-value-converter';

@bindingBehavior(numberFormatValueConverterName)
export class NumberFormatBindingBehavior {

public bind(flags: LifecycleFlags, scope: IScope, binding: BindingWithBehavior) {
createIntlFormatValueConverterExpression(numberFormatValueConverterName, binding);
}
}
5 changes: 3 additions & 2 deletions packages/i18n/src/nf/number-format-value-converter.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { valueConverter } from '@aurelia/runtime';
import { I18N, I18N_SIGNAL, I18nService } from '../i18n';

@valueConverter('nf')
export const numberFormatValueConverterName = 'nf';

@valueConverter(numberFormatValueConverterName)
export class NumberFormatValueConverter {
public readonly signals: string[] = [I18N_SIGNAL];

constructor(@I18N private readonly i18n: I18nService) { }

public toView(value: unknown, options?: Intl.NumberFormatOptions, locale?: string) {

if (typeof value !== 'number') {
return value;
}
Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/rt/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './relative-time-binding-behavior';
export * from './relative-time-value-converter';
11 changes: 11 additions & 0 deletions packages/i18n/src/rt/relative-time-binding-behavior.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { bindingBehavior, IScope, LifecycleFlags } from '@aurelia/runtime';
import { BindingWithBehavior, createIntlFormatValueConverterExpression } from '../utils';
import { relativeTimeValueConverterName } from './relative-time-value-converter';

@bindingBehavior(relativeTimeValueConverterName)
export class RelativeTimeBindingBehavior {

public bind(flags: LifecycleFlags, scope: IScope, binding: BindingWithBehavior) {
createIntlFormatValueConverterExpression(relativeTimeValueConverterName, binding);
}
}
3 changes: 2 additions & 1 deletion packages/i18n/src/rt/relative-time-value-converter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { valueConverter } from '@aurelia/runtime';
import { I18N, I18N_SIGNAL, I18nService } from '../i18n';

export const relativeTimeValueConverterName = 'rt';
export const RT_SIGNAL: string = 'aurelia-relativetime-signal';

@valueConverter('rt')
@valueConverter(relativeTimeValueConverterName)
export class RelativeTimeValueConverter {
public readonly signals: string[] = [I18N_SIGNAL, RT_SIGNAL];

Expand Down
5 changes: 3 additions & 2 deletions packages/i18n/src/t/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './translation-renderer';
export * from './translation-parameters-renderer';
export * from './translation-binding';
export * from './translation-binding-behavior';
export * from './translation-parameters-renderer';
export * from './translation-renderer';
export * from './translation-value-converter';
Loading

0 comments on commit f002dd7

Please sign in to comment.