Skip to content

Commit 5f3b60b

Browse files
chore: upgrade i18Next to v21 (#1390)
Upgrade dependency i18next to v21 and refactor impacted code.
1 parent 4f73f14 commit 5f3b60b

File tree

22 files changed

+722
-165
lines changed

22 files changed

+722
-165
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ jobs:
5454
E2E_APP_SECRET: ${{ secrets.E2E_APP_SECRET }}
5555
E2E_TEST_USER_1_TOKEN: ${{ secrets.E2E_TEST_USER_1_TOKEN }}
5656
E2E_TEST_USER_2_TOKEN: ${{ secrets.E2E_TEST_USER_2_TOKEN }}
57-
57+
5858
test:
5959
runs-on: ubuntu-latest
6060
strategy:
6161
matrix:
62-
node: [10, 12, 14]
62+
node: [14, 16]
6363
name: Test with Node ${{ matrix.node }}
6464
steps:
6565
- uses: actions/checkout@v2
@@ -82,4 +82,4 @@ jobs:
8282
run: |
8383
yarn lint
8484
yarn coverage
85-
yarn validate-translations
85+
yarn validate-translations

babel.i18next-extract.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

i18next-parser.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// https://github.com/i18next/i18next-parser#options
2+
module.exports = {
3+
createOldCatalogs: false,
4+
input: ['./src/**/*.{tsx,ts}'],
5+
keySeparator: false,
6+
locales: ['de', 'en', 'es', 'fr', 'hi', 'it', 'ja', 'ko', 'nl', 'pt', 'ru', 'tr'],
7+
namespaceSeparator: false,
8+
output: 'src/i18n/$LOCALE.json',
9+
sort(a, b) {
10+
return a < b ? -1 : 1; // alfabetical order
11+
},
12+
useKeysAsDefaultValue: true,
13+
verbose: true,
14+
};

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"dayjs": "^1.10.4",
3333
"emoji-mart": "3.0.1",
3434
"emoji-regex": "^9.2.0",
35-
"i18next": "^19.8.4",
35+
"i18next": "^21.6.14",
3636
"isomorphic-ws": "^4.0.1",
3737
"linkifyjs": "^2.1.9",
3838
"lodash.debounce": "^4.0.8",
@@ -110,7 +110,6 @@
110110
"babel-eslint": "^10.1.0",
111111
"babel-jest": "^26.3.0",
112112
"babel-loader": "8.2.2",
113-
"babel-plugin-i18next-extract": "^0.8.2",
114113
"babel-plugin-module-resolver": "^4.1.0",
115114
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
116115
"codecov": "^3.8.1",
@@ -140,6 +139,7 @@
140139
"file-loader": "^6.2.0",
141140
"htmlparser2": "^5.0.1",
142141
"husky": "^4.3.0",
142+
"i18next-parser": "^6.0.0",
143143
"jest": "^26.6.3",
144144
"jest-axe": "^5.0.1",
145145
"moment": "^2.29.1",
@@ -180,7 +180,7 @@
180180
"build": "rm -rf dist && mkdir -p dist/assets assets && yarn --silent copy-version && yarn build-translations && yarn types && yarn bundle",
181181
"bundle": "rollup -c",
182182
"bundle-size": "BUNDLE_SIZE=true yarn bundle",
183-
"build-translations": "rm -rf .tmpi18ncache || true && mkdir .tmpi18ncache && yarn run babel --config-file ./babel.i18next-extract.js --extensions '.ts','.tsx','.js','.jsx' 'src/**/*.{js,jsx,ts,tsx}' --out-dir '.tmpi18ncache/' && rm -rf .tmpi18ncache && prettier --write 'src/i18n/*.{js,jsx,ts,tsx,json}'",
183+
"build-translations": "i18next",
184184
"copy-version": "echo '\u001b[34mℹ\u001b[0m Copying Version to \u001b[34msrc/version.ts\u001b[0m' && PACKAGE_VERSION=$(node -pe 'require(`./package.json`).version') && PACKAGE_STRING=\"'$PACKAGE_VERSION'\" && echo 'export const version = '$PACKAGE_STRING';' > src/version.ts && echo '\u001b[32m✓\u001b[0m Done Copying Version'",
185185
"coverage": "jest --collectCoverage && codecov",
186186
"eslint": "eslint '**/*.{js,md,ts,jsx,tsx}' --max-warnings 0",

src/components/ChannelHeader/icons.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const MenuIcon = ({ title }: { title?: string }) => {
77

88
return (
99
<svg data-testid='menu-icon' viewBox='0 0 448 512' xmlns='http://www.w3.org/2000/svg'>
10-
<title>{title || t('Menu')}</title>
10+
<title>{title ?? t('Menu')}</title>
1111
<path
1212
d='M0 88C0 74.75 10.75 64 24 64H424C437.3 64 448 74.75 448 88C448 101.3 437.3 112 424 112H24C10.75 112 0 101.3 0 88zM0 248C0 234.7 10.75 224 24 224H424C437.3 224 448 234.7 448 248C448 261.3 437.3 272 424 272H24C10.75 272 0 261.3 0 248zM424 432H24C10.75 432 0 421.3 0 408C0 394.7 10.75 384 24 384H424C437.3 384 448 394.7 448 408C448 421.3 437.3 432 424 432z'
1313
fill='currentColor'

src/components/Message/MessageRepliesCountButton.tsx

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,41 +18,26 @@ const UnMemoizedMessageRepliesCountButton: React.FC<MessageRepliesCountButtonPro
1818

1919
const { t } = useTranslationContext('MessageRepliesCountButton');
2020

21-
let singleReplyText;
22-
let pluralReplyText;
23-
24-
if (reply_count === 1) {
25-
if (labelSingle) {
26-
singleReplyText = `1 ${labelSingle}`;
27-
} else {
28-
singleReplyText = t('1 reply');
29-
}
30-
}
21+
if (!reply_count) return null;
3122

32-
if (reply_count && reply_count > 1) {
33-
if (labelPlural) {
34-
pluralReplyText = `${reply_count} ${labelPlural}`;
35-
} else {
36-
pluralReplyText = t('{{ replyCount }} replies', {
37-
replyCount: reply_count,
38-
});
39-
}
40-
}
23+
let replyCountText = t('replyCount', { count: reply_count });
4124

42-
if (reply_count && reply_count !== 0) {
43-
return (
44-
<button
45-
className='str-chat__message-replies-count-button'
46-
data-testid='replies-count-button'
47-
onClick={onClick}
48-
>
49-
<ReplyIcon />
50-
{reply_count === 1 ? singleReplyText : pluralReplyText}
51-
</button>
52-
);
25+
if (labelPlural && reply_count > 1) {
26+
replyCountText = `${reply_count} ${labelPlural}`;
27+
} else if (labelSingle) {
28+
replyCountText = `1 ${labelSingle}`;
5329
}
5430

55-
return null;
31+
return (
32+
<button
33+
className='str-chat__message-replies-count-button'
34+
data-testid='replies-count-button'
35+
onClick={onClick}
36+
>
37+
<ReplyIcon />
38+
{replyCountText}
39+
</button>
40+
);
5641
};
5742

5843
export const MessageRepliesCountButton = React.memo(

src/components/Message/__tests__/MessageRepliesCountButton.test.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ import React from 'react';
22
import { cleanup, fireEvent, render } from '@testing-library/react';
33
import '@testing-library/jest-dom';
44
import { MessageRepliesCountButton } from '../MessageRepliesCountButton';
5-
import { TranslationContext } from '../../../context';
5+
import { TranslationProvider } from '../../../context';
66

77
const onClickMock = jest.fn();
8+
const defaultSingularText = '1 reply';
9+
const defaultPluralText = '2 replies';
810

9-
const i18nMock = (key) => key;
11+
const i18nMock = (key, { count }) => (count > 1 ? defaultPluralText : defaultSingularText);
1012

1113
const renderComponent = (props) =>
1214
render(
13-
<TranslationContext.Provider value={{ t: i18nMock }}>
15+
<TranslationProvider value={{ t: i18nMock }}>
1416
<MessageRepliesCountButton {...props} onClick={onClickMock} />
15-
</TranslationContext.Provider>,
17+
</TranslationProvider>,
1618
);
1719

1820
describe('MessageRepliesCountButton', () => {
@@ -24,27 +26,27 @@ describe('MessageRepliesCountButton', () => {
2426
it('should render the right text when there is one reply, and labelSingle is not defined', () => {
2527
const { getByText } = renderComponent({ reply_count: 1 });
2628

27-
expect(getByText('1 reply')).toBeInTheDocument();
29+
expect(getByText(defaultSingularText)).toBeInTheDocument();
2830
});
2931

3032
it('should render the right text when there is one reply, and labelSingle is defined', () => {
31-
const labelSingle = 'text';
32-
const { getByText } = renderComponent({ labelSingle, reply_count: 1 });
33+
const customSingularLabel = 'text';
34+
const { getByText } = renderComponent({ labelSingle: customSingularLabel, reply_count: 1 });
3335

34-
expect(getByText(`1 ${labelSingle}`)).toBeInTheDocument();
36+
expect(getByText(`1 ${customSingularLabel}`)).toBeInTheDocument();
3537
});
3638

3739
it('should render the right text when there is more than one reply, and labelPlural is not defined', () => {
3840
const { getByText } = renderComponent({ reply_count: 2 });
3941

40-
expect(getByText('{{ replyCount }} replies')).toBeInTheDocument();
42+
expect(getByText(defaultPluralText)).toBeInTheDocument();
4143
});
4244

4345
it('should render the right text when there is more than one reply, and labelPlural is defined', () => {
44-
const labelPlural = 'text';
45-
const { getByText } = renderComponent({ labelPlural, reply_count: 2 });
46+
const customPluralLabel = 'text';
47+
const { getByText } = renderComponent({ labelPlural: customPluralLabel, reply_count: 2 });
4648

47-
expect(getByText(`2 ${labelPlural}`)).toBeInTheDocument();
49+
expect(getByText(`2 ${customPluralLabel}`)).toBeInTheDocument();
4850
});
4951

5052
it('should call the onClick prop if the button is clicked', () => {

src/components/Thread/Thread.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,7 @@ const DefaultThreadHeader = <
8181

8282
const getReplyCount = () => {
8383
if (!thread.reply_count) return '';
84-
if (thread.reply_count === 1) return t('1 reply');
85-
return t('{{ replyCount }} replies', {
86-
replyCount: thread.reply_count,
87-
});
84+
return t('replyCount', { count: thread.reply_count });
8885
};
8986

9087
return (

src/components/Thread/__tests__/Thread.test.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ const channelActionContextMock = {
7171
loadMoreThread: jest.fn(() => Promise.resolve()),
7272
};
7373

74-
const i18nMock = jest.fn((key) => key);
74+
const i18nMock = jest.fn((key, props) => {
75+
if (key === 'replyCount' && props.count === 1) return '1 reply';
76+
else if (key === 'replyCount' && props.count > 1) return '2 replies';
77+
return key;
78+
});
7579

7680
const renderComponent = ({
7781
chatClient,
@@ -112,10 +116,10 @@ describe('Thread', () => {
112116
it('should render the reply count', () => {
113117
const { getByText } = renderComponent({ chatClient });
114118

115-
expect(i18nMock).toHaveBeenCalledWith('{{ replyCount }} replies', {
116-
replyCount: threadStart.reply_count,
119+
expect(i18nMock).toHaveBeenCalledWith('replyCount', {
120+
count: threadStart.reply_count,
117121
});
118-
expect(getByText('{{ replyCount }} replies')).toBeInTheDocument();
122+
expect(getByText('2 replies')).toBeInTheDocument();
119123
});
120124

121125
it('should render the message that starts the thread', () => {

src/i18n/de.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
2-
"1 reply": "1 Antwort",
32
"Attach files": "Dateien anhängen",
43
"Cancel": "Stornieren",
54
"Channel Missing": "Kanal fehlt",
@@ -61,13 +60,14 @@
6160
"You have no channels currently": "Du hast momentan noch keinen Channels",
6261
"You've reached the maximum number of files": "Die maximale Dateianzahl ist erreicht",
6362
"live": "live",
63+
"replyCount_one": "1 Antwort",
64+
"replyCount_other": "{{ count }} Antworten",
6465
"this content could not be displayed": "Dieser Inhalt konnte nicht angezeigt werden",
6566
"{{ commaSeparatedUsers }} and {{ moreCount }} more": "{{ commaSeparatedUsers }} und {{moreCount}} Mehr",
6667
"{{ commaSeparatedUsers }}, and {{ lastUser }}": "{{ commaSeparatedUsers }} und {{ lastUser }}",
6768
"{{ firstUser }} and {{ secondUser }}": "{{ firstUser }} und {{ secondUser }}",
6869
"{{ imageCount }} more": "{{ imageCount }} mehr",
6970
"{{ memberCount }} members": "{{ memberCount }} Mitglieder",
70-
"{{ replyCount }} replies": "{{ replyCount }} Antworten",
7171
"{{ user }} has been muted": "{{ user }} wurde stummgeschaltet",
7272
"{{ user }} has been unmuted": "{{ user }} wurde nicht stummgeschaltet",
7373
"{{ watcherCount }} online": "{{ watcherCount }} online",

0 commit comments

Comments
 (0)