From 46f8779cd0697e20387a8d0fb568dca84f9fcabb Mon Sep 17 00:00:00 2001 From: Nikos Vasileiou Date: Mon, 11 Dec 2023 13:00:03 +0200 Subject: [PATCH] Make plural handling more loose --- packages/native/package.json | 32 +++++++++---------- .../src/renderers/MessageFormatRenderer.js | 8 +++-- packages/native/tests/plurals.test.js | 11 +++++++ packages/native/tests/renderer.test.js | 17 ++++++++++ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/packages/native/package.json b/packages/native/package.json index 1c7f1ec2..306fd63c 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -34,27 +34,27 @@ "node": ">=14.0.0" }, "devDependencies": { - "@babel/core": "^7.17.10", - "@babel/plugin-transform-runtime": "^7.17.10", - "@babel/preset-env": "^7.17.10", - "@babel/runtime": "^7.17.9", + "@babel/core": "^7.23.5", + "@babel/plugin-transform-runtime": "^7.23.4", + "@babel/preset-env": "^7.23.5", + "@babel/runtime": "^7.23.5", "babel-eslint": "^10.1.0", - "babel-loader": "^8.2.5", - "chai": "^4.3.4", + "babel-loader": "^8.3.0", + "chai": "^4.3.10", "eslint": "^7.28.0", - "eslint-config-airbnb-base": "^14.2.0", - "eslint-plugin-import": "^2.26.0", - "glob": "^8.0.1", - "mocha": "^10.0.0", - "nock": "^13.1.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.29.0", + "glob": "^8.1.0", + "mocha": "^10.2.0", + "nock": "^13.4.0", "nyc": "^15.1.0", - "source-map-support": "^0.5.19", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2" + "source-map-support": "^0.5.21", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4" }, "dependencies": { - "@messageformat/core": "^3.0.0", - "axios": "^0.27.2", + "@messageformat/core": "^3.3.0", + "axios": "^1.6.2", "md5": "^2.3.0" } } diff --git a/packages/native/src/renderers/MessageFormatRenderer.js b/packages/native/src/renderers/MessageFormatRenderer.js index 20924c7a..88d70ad5 100644 --- a/packages/native/src/renderers/MessageFormatRenderer.js +++ b/packages/native/src/renderers/MessageFormatRenderer.js @@ -18,9 +18,13 @@ export default class MessageFormatRenderer { const locale = ((localeCode || '').split('_'))[0]; if (!MF[locale]) { try { - MF[locale] = new MessageFormat(locale); + MF[locale] = new MessageFormat(locale, { + strictPluralKeys: false, + }); } catch (err) { - MF[locale] = new MessageFormat(); + MF[locale] = new MessageFormat('*', { + strictPluralKeys: false, + }); } } const msg = MF[locale].compile(sourceString); diff --git a/packages/native/tests/plurals.test.js b/packages/native/tests/plurals.test.js index cfce8555..93a54e83 100644 --- a/packages/native/tests/plurals.test.js +++ b/packages/native/tests/plurals.test.js @@ -32,6 +32,7 @@ describe('explodePlurals', () => { testExplode('{cnt, plural, =1 {hello world} other {hello worlds}}', ['cnt', { one: 'hello world', other: 'hello worlds' }]); }); + it('should not explode with leading/tailing spaces', () => { testExplode( ' {cnt, plural, one {hello world} other {hello worlds}}', @@ -42,6 +43,7 @@ describe('explodePlurals', () => { [null, { other: '{cnt, plural, one {hello world} other {hello worlds}} ' }], ); }); + it('should fail when missing opening/closing brackets', () => { testExplode( 'cnt, plural, one {hello world} other {hello worlds}}', @@ -52,6 +54,7 @@ describe('explodePlurals', () => { [null, { other: '{cnt, plural, one {hello world} other {hello worlds}' }], ); }); + it('should fail if message is incomplete', () => { testExplode('{cnt}', [null, { other: '{cnt}' }]); testExplode('{cnt, }', [null, { other: '{cnt, }' }]); @@ -63,6 +66,7 @@ describe('explodePlurals', () => { testExplode('{cnt, plural, one hello world}', [null, { other: '{cnt, plural, one hello world}' }]); }); + it('should fail if variable, plural keyword or rules are wrong', () => { testExplode( '{cn t, plural, one {hello world} other {hello worlds}}', @@ -97,14 +101,17 @@ describe('explodePlurals', () => { [null, { other: '{cnt, plural, one {hello world} o ther {hello worlds}}' }], ); }); + it('should fail on missing plural strings', () => { testExplode('{cnt, plural, one', [null, { other: '{cnt, plural, one' }]); testExplode('{cnt, plural, one}', [null, { other: '{cnt, plural, one}' }]); }); + it('should handle nested brackets', () => { testExplode('{cnt, plural, one {hello {world}} other {hello worlds}}', ['cnt', { one: 'hello {world}', other: 'hello worlds' }]); }); + it("should fail when minimum plural rules don't exist", () => { testExplode('{cnt, plural, one {hello world}}', [null, { other: '{cnt, plural, one {hello world}}' }]); @@ -113,6 +120,7 @@ describe('explodePlurals', () => { [null, { other: '{cnt, plural, few {hello world} other {hello worlds}}' }], ); }); + it('should propertly escape stuff with apostrophes', () => { testExplode("{cnt, plural, one {hello '{'world'}'} other {hello worlds}}", ['cnt', { one: "hello '{'world'}'", other: 'hello worlds' }]); @@ -139,16 +147,19 @@ describe('implodePlurals', () => { expect(implodePlurals({ one: 'hello world', two: 'hello worlds' })).to .equal('{???, plural, one {hello world} two {hello worlds}}'); }); + it('should respect custom count variable', () => { expect(implodePlurals({ zero: 'hello world' }, 'count')).to .equal('{count, plural, zero {hello world}}'); }); + it('should respect order', () => { expect(implodePlurals({ few: 'hello world', many: 'hello worlds' })).to .equal('{???, plural, few {hello world} many {hello worlds}}'); expect(implodePlurals({ many: 'hello worlds', few: 'hello world' })).to .equal('{???, plural, few {hello world} many {hello worlds}}'); }); + it('should ignore unknown rules', () => { expect(implodePlurals({ other: 'hello world', six: 'hello worlds' })).to .equal('{???, plural, other {hello world}}'); diff --git a/packages/native/tests/renderer.test.js b/packages/native/tests/renderer.test.js index ca62945f..39165d0d 100644 --- a/packages/native/tests/renderer.test.js +++ b/packages/native/tests/renderer.test.js @@ -14,6 +14,23 @@ describe('String renderer', () => { t = tx.translate.bind(tx); }); + it('renders plurals', () => { + expect(tx.stringRenderer.render('{cnt, plural, one {# hello} other {# hellos}}', '', { cnt: 1 })) + .to.equal('1 hello'); + expect(tx.stringRenderer.render('{cnt, plural, one {# hello} other {# hellos}}', '', { cnt: 0 })) + .to.equal('0 hellos'); + expect(tx.stringRenderer.render('{cnt, plural, one {# one hello} two {# two hellos} many {# many hellos} other {# other hellos}}', '', { cnt: 2 })) + .to.equal('2 other hellos'); + expect(tx.stringRenderer.render('{cnt, plural, one {# one hello} two {# two hellos} many {# many hellos} other {# other hellos}}', 'he', { cnt: 2 })) + .to.equal('2 two hellos'); + expect(tx.stringRenderer.render('{cnt, plural, one {תחנה אחת} two {# תחנות} many {# תחנות} other {# תחנות}}', 'he', { cnt: 0 })) + .to.equal('0 תחנות'); + expect(tx.stringRenderer.render('{cnt, plural, one {תחנה אחת} two {# תחנות} many {# תחנות} other {# תחנות}}', 'he', { cnt: 1 })) + .to.equal('תחנה אחת'); + expect(tx.stringRenderer.render('{cnt, plural, one {תחנה אחת} two {# תחנות} many {# תחנות} other {# תחנות}}', 'he', { cnt: 2 })) + .to.equal('2 תחנות'); + }); + it('renders with localized dates', async () => { const d = Date.parse('2020-02-01');