From a9f7e17e2d5b82a722a6af8d87d1ca2dfd26fd40 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 15 Sep 2023 10:41:17 +0200 Subject: [PATCH] core-app-api: add failing formatting tests for TranslationApi Signed-off-by: Patrik Oldsberg --- .../I18nextTranslationApi.test.ts | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts index 4b0861af30..9929bacfd1 100644 --- a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts +++ b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts @@ -374,4 +374,135 @@ describe('I18nextTranslationApi', () => { expect(translations).toEqual(['foo', null, 'Föö', null, 'Føø']); }); + + describe('formatting', () => { + function snapshotWithMessages< + TMessages extends { [key in string]: string }, + >(messages: TMessages) { + const translationApi = I18nextTranslationApi.create({ + languageApi: AppLanguageSelector.create(), + }); + const ref = createTranslationRef({ + id: 'test', + messages, + }); + return assertReady(translationApi.getTranslation(ref)); + } + + it('should format plain messages', () => { + const snapshot = snapshotWithMessages({ + foo: 'Foo', + bar: 'Bar', + baz: 'Baz', + }); + + expect(snapshot.t('foo')).toBe('Foo'); + expect(snapshot.t('bar')).toBe('Bar'); + expect(snapshot.t('baz')).toBe('Baz'); + }); + + it('should support interpolation', () => { + const snapshot = snapshotWithMessages({ + shallow: 'Foo {{ bar }}', + multiple: 'Foo {{ bar }} {{ baz }}', + deep: 'Foo {{ bar.baz }}', + }); + + expect(snapshot.t('shallow')).toBe('Foo {{ bar }}'); + expect(snapshot.t('shallow', { bar: 'Bar' })).toBe('Foo Bar'); + + expect(snapshot.t('multiple')).toBe('Foo {{ bar }} {{ baz }}'); + expect(snapshot.t('multiple', { bar: 'Bar' })).toBe('Foo Bar {{ baz }}'); + expect(snapshot.t('multiple', { bar: 'Bar', baz: 'Baz' })).toBe( + 'Foo Bar Baz', + ); + + expect(snapshot.t('deep')).toBe('Foo {{ bar.baz }}'); + expect(snapshot.t('deep', { bar: { baz: 'Baz' } })).toBe('Foo Baz'); + }); + + // Escaping isn't as useful in React, since we don't need to escape HTML in strings + it('should not escape by default', () => { + const snapshot = snapshotWithMessages({ + foo: 'Foo {{ foo }}', + }); + + expect(snapshot.t('foo', { foo: '
' })).toBe('Foo
'); + expect( + snapshot.t('foo', { + foo: '
', + interpolation: { escapeValue: true }, + }), + ).toBe('Foo <div>'); + }); + + it('should support nesting', () => { + const snapshot = snapshotWithMessages({ + foo: 'Foo $t(bar) $t(baz)', + bar: 'Nested', + baz: 'Baz {{ qux }}', + }); + + expect(snapshot.t('foo', { qux: 'Deep' })).toBe('Foo Nested Baz Deep'); + }); + + it('should support formatting', () => { + const snapshot = snapshotWithMessages({ + plain: '= {{ x }}', + number: '= {{ x, number }}', + numberFixed: '= {{ x, number(minimumFractionDigits: 2) }}', + relativeTime: '= {{ x, relativeTime }}', + relativeSeconds: '= {{ x, relativeTime(second) }}', + relativeSecondsShort: + '= {{ x, relativeTime(range: second; style: narrow) }}', + list: '= {{ x, list }}', + }); + + expect(snapshot.t('plain', { x: 5 })).toBe('= 5'); + expect(snapshot.t('number', { x: 5 })).toBe('= 5'); + expect(snapshot.t('number', { x: 5, minimumFractionDigits: 1 })).toBe( + '= 5.0', + ); + expect(snapshot.t('numberFixed', { x: 5 })).toBe('= 5.00'); + expect( + snapshot.t('numberFixed', { x: 5, minimumFractionDigits: 3 }), + ).toBe('= 5.000'); + expect(snapshot.t('relativeTime', { x: 3 })).toBe('= in 3 days'); + expect(snapshot.t('relativeTime', { x: -3 })).toBe('= 3 days ago'); + expect(snapshot.t('relativeTime', { x: 15, range: 'weeks' })).toBe( + '= in 15 weeks', + ); + expect( + snapshot.t('relativeTime', { x: 15, range: 'weeks', style: 'short' }), + ).toBe('= in 15 wk.'); + expect(snapshot.t('relativeSeconds', { x: 1 })).toBe('= in 1 second'); + expect(snapshot.t('relativeSeconds', { x: 2 })).toBe('= in 2 seconds'); + expect(snapshot.t('relativeSeconds', { x: -3 })).toBe('= 3 seconds ago'); + expect(snapshot.t('relativeSeconds', { x: 0 })).toBe('= in 0 seconds'); + expect(snapshot.t('relativeSecondsShort', { x: 1 })).toBe('= in 1s'); + expect(snapshot.t('relativeSecondsShort', { x: 2 })).toBe('= in 2s'); + expect(snapshot.t('relativeSecondsShort', { x: -3 })).toBe('= 3s ago'); + expect(snapshot.t('relativeSecondsShort', { x: 0 })).toBe('= in 0s'); + expect(snapshot.t('list', { x: ['a'] })).toBe('= a'); + expect(snapshot.t('list', { x: ['a', 'b'] })).toBe('= a and b'); + expect(snapshot.t('list', { x: ['a', 'b', 'c'] })).toBe('= a, b, and c'); + }); + + it('should support plurals', () => { + const snapshot = snapshotWithMessages({ + derp_one: 'derp', + derp_other: 'derps', + derpWithCount_one: '{{ count }} derp', + derpWithCount_other: '{{ count }} derps', + }); + + // TODO(Rugvip): Support plural keys + expect(snapshot.t('derp' as any, { count: 1 })).toBe('derp'); + expect(snapshot.t('derp' as any, { count: 2 })).toBe('derps'); + expect(snapshot.t('derp' as any, { count: 0 })).toBe('derps'); + expect(snapshot.t('derpWithCount' as any, { count: 1 })).toBe('1 derp'); + expect(snapshot.t('derpWithCount' as any, { count: 2 })).toBe('2 derps'); + expect(snapshot.t('derpWithCount' as any, { count: 0 })).toBe('0 derps'); + }); + }); });