From 985e6de190c62a7efd6ad5a457984ebfb081af59 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 14 Jun 2024 07:18:30 +0200 Subject: [PATCH] feat(parser): Handle a few more use cases of potential count attributes. (#260) --- src/parser.js | 22 +++++ test/react-i18next/i18n.js | 6 +- test/react-i18next/trans-render.test.js | 106 +++++++++++++++++++++++- 3 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/parser.js b/src/parser.js index 0c2a202..d243d19 100644 --- a/src/parser.js +++ b/src/parser.js @@ -614,6 +614,28 @@ class Parser { }, {}); } + // Member Expression (e.g. i18nKey={foo.bar}) + if (expression.type === 'MemberExpression') { + acc[name] = expression.object.name + '.' + expression.property.name; + } + + // Conditional Expression (e.g. i18nKey={true ? 'foo' : 'bar'}) + if (expression.type === 'ConditionalExpression') { + acc[name] = expression.consequent.value; + } + + // Unary Expression (e.g. i18nKey={foo?.bar}) + if (expression.type === 'UnaryExpression') { + acc[name] = expression.alternate.object.name + '.' + expression.alternate.property.name; + } + + // Binary Expression (e.g. i18nKey={foo.bar - 1}) + if (expression.type === 'BinaryExpression') { + if (expression.left.type === 'MemberExpression' && expression.right.type === 'Literal') { + acc[name] = expression.left.object.name + '.' + expression.left.property.name; + } + } + // Template Literal if (expression.type === 'TemplateLiteral') { acc[name] = expression.quasis diff --git a/test/react-i18next/i18n.js b/test/react-i18next/i18n.js index 5cfcda3..543155d 100644 --- a/test/react-i18next/i18n.js +++ b/test/react-i18next/i18n.js @@ -28,6 +28,8 @@ i18n.init({ transTest2InV2: 'Hello <1>{{name}}, you have {{count}} message. Open <5>hear.', transTest2InV2_other: 'Hello <1>{{name}}, you have {{count}} messages. Open <5>here.', + transPluralWithCountAsMemberExpression: '{{count}} item', + transPluralWithCountAsMemberExpression_other: '{{count}} items', testTransKey1: '<0>{{numOfItems}} item matched.', testTransKey1_other: '<0>{{numOfItems}} items matched.', testTransKey2: '<0><0>{{numOfItems}} item matched.', @@ -53,7 +55,9 @@ i18n.init({ escapeValue: false, // not needed for react!! formatSeparator: ',', format(value, format) { - if (format === 'uppercase') return value.toUpperCase(); + if (format === 'uppercase') { + return value.toUpperCase(); + } return value; }, }, diff --git a/test/react-i18next/trans-render.test.js b/test/react-i18next/trans-render.test.js index 494e393..a882a82 100644 --- a/test/react-i18next/trans-render.test.js +++ b/test/react-i18next/trans-render.test.js @@ -1,6 +1,6 @@ /** @jest-environment jsdom */ -import React from 'react'; import { render } from '@testing-library/react'; +import React from 'react'; import { Trans, withTranslation, @@ -309,7 +309,7 @@ describe('trans complex - count only in props', () => { // prettier-ignore return ( - Hello {{ name }}, you have {{n: count}} message. Open here. + Hello {{ name }}, you have {{ n: count }} message. Open here. ); }; @@ -368,10 +368,108 @@ describe('trans complex v2 no extra pseudo elements for interpolation', () => { }); }); +describe('Trans with a count using an object property should be detected as a translation with a plural', () => { + const TestComponent = () => { + const obj = { anyProp: { a: 10 } }; + // prettier-ignore + return ( + + {{ count: obj.anyProp }} item. + + ); + }; + + it('should render correct content with the count', () => { + const { container } = render(); + expect(container.firstChild).toMatchInlineSnapshot(` +
+ 10 items +
+`); + }); +}); + +describe('Trans with a count using an conditionnal statement should be detected as a translation with a plural', () => { + const TestComponent = () => { + const obj = true; + // prettier-ignore + return ( + + {{ count: 6 }} item. + + ); + }; + + it('should render correct content with the count', () => { + const { container } = render(); + expect(container.firstChild).toMatchInlineSnapshot(` +
+ 3 items +
+`); + }); +}); + +describe('Trans with a count using an Unary Expression statement should be detected as a translation with a plural', () => { + const TestComponent = () => { + const obj = { a: 3 }; + // prettier-ignore + return ( + + {{ count: 6 }} item. + + ); + }; + + it('should render correct content with the count', () => { + const { container } = render(); + expect(container.firstChild).toMatchInlineSnapshot(` +
+ 3 items +
+`); + }); +}); + +describe('Trans with a count using an Binary Expression statement should be detected as a translation with a plural', () => { + const TestComponent = () => { + const obj = { a: 3 }; + // prettier-ignore + return ( + + {{ count: 6 }} item. + + ); + }; + + it('should render correct content with the count', () => { + const { container } = render(); + expect(container.firstChild).toMatchInlineSnapshot(` +
+ 2 items +
+`); + }); +}); + describe('trans with t as prop', () => { const TestComponent = ({ t, cb }) => { const customT = (...args) => { - if (cb) cb(); + if (cb) { + cb(); + } return t(...args); }; return ( @@ -415,7 +513,7 @@ describe('trans with empty content', () => { const TestComponent = () => ; it('should render an empty string', () => { const { container } = render(); - expect(container.firstChild).toMatchInlineSnapshot(`
`); + expect(container.firstChild).toMatchInlineSnapshot('
'); }); });