Skip to content

Commit 756b4d5

Browse files
authored
feat: add translations (#103)
1 parent b891785 commit 756b4d5

27 files changed

+331
-264
lines changed

.eslintignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ build
22
public
33
coverage
44
src/registerServiceWorker.js
5-
cypress/integration/examples
5+
cypress/integration/examples

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
"@testing-library/jest-dom": "5.16.5",
2727
"@testing-library/react": "14.1.2",
2828
"cypress-vite": "^1.5.0",
29-
"i18next": "22.5.0",
29+
"i18next": "^23.8.2",
3030
"lodash.isequal": "^4.5.0",
3131
"lodash.isobject": "3.0.2",
3232
"lodash.isstring": "4.0.1",
3333
"qs": "6.11.2",
3434
"randomcolor": "0.6.2",
3535
"react": "18.2.0",
3636
"react-dom": "18.2.0",
37-
"react-i18next": "12.3.1",
37+
"react-i18next": "14.0.5",
3838
"react-markdown": "^8.0.7",
3939
"react-toastify": "9.1.3",
4040
"remark-breaks": "^3.0.3",
@@ -75,7 +75,7 @@
7575
"@commitlint/config-conventional": "17.6.7",
7676
"@cypress/code-coverage": "3.12.17",
7777
"@trivago/prettier-plugin-sort-imports": "4.3.0",
78-
"@types/i18n": "0.13.10",
78+
"@types/i18n": "^0.13.10",
7979
"@types/jest": "29.5.11",
8080
"@types/lodash.isequal": "^4.5.6",
8181
"@types/lodash.isobject": "3.0.7",

src/@types/i18next.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { DEFAULT_NAMESPACE, resources } from '@/config/i18n';
2+
3+
declare module 'i18next' {
4+
interface CustomTypeOptions {
5+
defaultNS: typeof DEFAULT_NAMESPACE;
6+
resources: (typeof resources)['en'];
7+
}
8+
}

src/components/Root.tsx

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FC } from 'react';
2-
import { I18nextProvider } from 'react-i18next';
2+
import { I18nextProvider, useTranslation } from 'react-i18next';
33
import { ToastContainer } from 'react-toastify';
44
import 'react-toastify/dist/ReactToastify.min.css';
55

@@ -14,12 +14,10 @@ import { CssBaseline, ThemeProvider, createTheme, styled } from '@mui/material';
1414
import { grey, orange, pink } from '@mui/material/colors';
1515
import { StyledEngineProvider } from '@mui/material/styles';
1616

17+
import { TEXT_ANALYSIS } from '@/langs/constants';
18+
1719
import { ENABLE_MOCK_API } from '../config/env';
1820
import i18nConfig from '../config/i18n';
19-
import {
20-
CONTEXT_FETCHING_ERROR_MESSAGE,
21-
TOKEN_REQUEST_ERROR_MESSAGE,
22-
} from '../config/messages';
2321
import {
2422
QueryClientProvider,
2523
ReactQueryDevtools,
@@ -75,53 +73,57 @@ const RootDiv = styled('div')({
7573
height: '100%',
7674
});
7775

78-
const Root: FC = () => {
76+
// This function is necessary to allow to use translations.
77+
const AppWithContext = (): JSX.Element => {
7978
const [mockContext, setMockContext] = useObjectState(defaultMockContext);
79+
const { t } = useTranslation();
8080

8181
return (
82-
<RootDiv>
83-
{/* Used to define the order of injected properties between JSS and emotion */}
84-
<StyledEngineProvider injectFirst>
85-
<ThemeProvider theme={theme}>
86-
<CssBaseline enableColorScheme />
87-
<I18nextProvider i18n={i18nConfig}>
88-
<QueryClientProvider client={queryClient}>
89-
<WithLocalContext
90-
defaultValue={window.Cypress ? window.appContext : mockContext}
91-
LoadingComponent={<Loader />}
92-
useGetLocalContext={hooks.useGetLocalContext}
93-
useAutoResize={hooks.useAutoResize}
94-
onError={() => {
95-
showErrorToast(CONTEXT_FETCHING_ERROR_MESSAGE);
96-
}}
97-
>
98-
<WithTokenContext
99-
LoadingComponent={<Loader />}
100-
useAuthToken={hooks.useAuthToken}
101-
onError={() => {
102-
showErrorToast(TOKEN_REQUEST_ERROR_MESSAGE);
103-
}}
104-
>
105-
<ToastContainer position="bottom-right" />
106-
<App />
107-
{import.meta.env.DEV && ENABLE_MOCK_API && (
108-
<GraaspContextDevTool
109-
members={mockMembers}
110-
context={mockContext}
111-
setContext={setMockContext}
112-
/>
113-
)}
114-
</WithTokenContext>
115-
</WithLocalContext>
116-
{import.meta.env.DEV && (
117-
<ReactQueryDevtools position="top-right" />
118-
)}
119-
</QueryClientProvider>
120-
</I18nextProvider>
121-
</ThemeProvider>
122-
</StyledEngineProvider>
123-
</RootDiv>
82+
<WithLocalContext
83+
defaultValue={window.Cypress ? window.appContext : mockContext}
84+
LoadingComponent={<Loader />}
85+
useGetLocalContext={hooks.useGetLocalContext}
86+
useAutoResize={hooks.useAutoResize}
87+
onError={() => {
88+
showErrorToast(t(TEXT_ANALYSIS.CONTEXT_FETCHING_ERROR_MESSAGE));
89+
}}
90+
>
91+
<WithTokenContext
92+
LoadingComponent={<Loader />}
93+
useAuthToken={hooks.useAuthToken}
94+
onError={() => {
95+
showErrorToast(t(TEXT_ANALYSIS.TOKEN_REQUEST_ERROR_MESSAGE));
96+
}}
97+
>
98+
<ToastContainer position="bottom-right" />
99+
<App />
100+
{import.meta.env.DEV && ENABLE_MOCK_API && (
101+
<GraaspContextDevTool
102+
members={mockMembers}
103+
context={mockContext}
104+
setContext={setMockContext}
105+
/>
106+
)}
107+
</WithTokenContext>
108+
</WithLocalContext>
124109
);
125110
};
126111

112+
const Root: FC = () => (
113+
<RootDiv>
114+
{/* Used to define the order of injected properties between JSS and emotion */}
115+
<StyledEngineProvider injectFirst>
116+
<ThemeProvider theme={theme}>
117+
<CssBaseline enableColorScheme />
118+
<I18nextProvider i18n={i18nConfig}>
119+
<QueryClientProvider client={queryClient}>
120+
<AppWithContext />
121+
{import.meta.env.DEV && <ReactQueryDevtools position="top-right" />}
122+
</QueryClientProvider>
123+
</I18nextProvider>
124+
</ThemeProvider>
125+
</StyledEngineProvider>
126+
</RootDiv>
127+
);
128+
127129
export default Root;

src/components/common/PublicAlert.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { useLocalContext } from '@graasp/apps-query-client';
44

55
import Alert from '@mui/material/Alert';
66

7+
import { TEXT_ANALYSIS } from '@/langs/constants';
8+
79
const PublicAlert = (): JSX.Element | null => {
810
const { t } = useTranslation();
911

@@ -16,7 +18,7 @@ const PublicAlert = (): JSX.Element | null => {
1618

1719
return (
1820
<Alert severity="error">
19-
{t('You are not authenticated. You cannot save any data')}
21+
{t(TEXT_ANALYSIS.PUBLIC_ALERT_NOT_AUTHENTICATED)}
2022
</Alert>
2123
);
2224
};

src/components/common/chat/ChatBox.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import randomColor from 'randomcolor';
22

33
import { FC, useEffect, useRef, useState } from 'react';
4+
import { useTranslation } from 'react-i18next';
45
import ReactMarkdown from 'react-markdown';
56

67
import {
@@ -9,7 +10,9 @@ import {
910
useLocalContext,
1011
} from '@graasp/apps-query-client';
1112

12-
import { Alert, AlertTitle, Box, Stack, styled } from '@mui/material';
13+
import { Alert, Box, Stack, styled } from '@mui/material';
14+
15+
import { TEXT_ANALYSIS } from '@/langs/constants';
1316

1417
import { APP_DATA_TYPES, ChatAppData } from '../../../config/appDataTypes';
1518
import {
@@ -20,10 +23,8 @@ import { DEFAULT_INITIAL_PROMPT } from '../../../config/appSettings';
2023
import {
2124
ANONYMOUS_USER,
2225
MAX_CONVERSATION_LENGTH,
23-
MAX_CONVERSATION_LENGTH_ALERT,
2426
SCROLL_SAFETY_MARGIN,
2527
} from '../../../config/constants';
26-
import { CHAT_BOT_ERROR_MESSAGE } from '../../../config/messages';
2728
import { mutations } from '../../../config/queryClient';
2829
import { CHATBOT_MODE_CY, messagesDataCy } from '../../../config/selectors';
2930
import {
@@ -73,6 +74,7 @@ const StyledReactMarkdown = styled(ReactMarkdown)(({ theme }) => ({
7374
type Prop = { focusWord: string; isOpen: boolean };
7475

7576
const ChatBox: FC<Prop> = ({ focusWord, isOpen }) => {
77+
const { t } = useTranslation();
7678
const { postAppDataAsync, postAppData, appDataArray } = useAppDataContext();
7779
const { appSettingArray } = useAppSettingContext();
7880
const context = useLocalContext();
@@ -113,7 +115,7 @@ const ChatBox: FC<Prop> = ({ focusWord, isOpen }) => {
113115
setLoading(true);
114116

115117
const appData = {
116-
message: CHAT_BOT_ERROR_MESSAGE,
118+
message: t(TEXT_ANALYSIS.CHAT_BOT_ERROR_MESSAGE),
117119
keyword: focusWord,
118120
};
119121

@@ -135,8 +137,7 @@ const ChatBox: FC<Prop> = ({ focusWord, isOpen }) => {
135137
const renderBar =
136138
chatAppData.length > MAX_CONVERSATION_LENGTH ? (
137139
<Alert severity="info">
138-
<AlertTitle>Info</AlertTitle>
139-
{MAX_CONVERSATION_LENGTH_ALERT}
140+
{t(TEXT_ANALYSIS.MAX_CONVERSATION_LENGTH_ALERT)}
140141
</Alert>
141142
) : (
142143
<InputBar onSend={(input) => onSend(input)} />

src/components/common/chat/InputBar.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { FC, KeyboardEventHandler, useState } from 'react';
2+
import { useTranslation } from 'react-i18next';
23

34
import SendIcon from '@mui/icons-material/Send';
45
import {
@@ -9,13 +10,16 @@ import {
910
OutlinedInput,
1011
} from '@mui/material';
1112

13+
import { TEXT_ANALYSIS } from '@/langs/constants';
14+
1215
import { ENTER_KEY } from '../../../config/constants';
1316

1417
type Prop = {
1518
onSend: (input: string) => void;
1619
};
1720

1821
const InputBar: FC<Prop> = ({ onSend }) => {
22+
const { t } = useTranslation();
1923
const [comment, setComment] = useState('');
2024

2125
const onChange = ({ target }: { target: { value: string } }): void => {
@@ -37,23 +41,23 @@ const InputBar: FC<Prop> = ({ onSend }) => {
3741

3842
return (
3943
<FormControl sx={{ m: 1 }} variant="outlined">
40-
<InputLabel>reply here ...</InputLabel>
44+
<InputLabel>{t(TEXT_ANALYSIS.INPUT_BAR_REPLY_HERE)}</InputLabel>
4145
<OutlinedInput
4246
value={comment}
4347
onChange={onChange}
4448
onKeyDown={onEnterPress}
4549
endAdornment={
4650
<InputAdornment position="end">
4751
<IconButton
48-
aria-label="send"
52+
aria-label={t(TEXT_ANALYSIS.INPUT_BAR_SEND_BTN)}
4953
onClick={() => handleClickSend(comment)}
5054
edge="end"
5155
>
5256
<SendIcon />
5357
</IconButton>
5458
</InputAdornment>
5559
}
56-
label="reply here ..."
60+
label={t(TEXT_ANALYSIS.INPUT_BAR_REPLY_HERE)}
5761
/>
5862
</FormControl>
5963
);

src/components/common/display/Banner.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { FC, useState } from 'react';
2+
import { useTranslation } from 'react-i18next';
23

34
import { Box, Button, Typography } from '@mui/material';
45

6+
import { TEXT_ANALYSIS } from '@/langs/constants';
7+
58
import { BANNER_CY, SHOW_KEYWORDS_BUTTON_CY } from '../../../config/selectors';
69
import {
710
DEFAULT_MARGIN,
@@ -15,11 +18,11 @@ type Prop = {
1518
};
1619

1720
const Banner: FC<Prop> = ({ title, disabled, onClick }) => {
21+
const { t } = useTranslation();
1822
const [showKeywords, setShowKeywords] = useState(false);
1923

20-
// TODO: translate me
21-
const SHOW_KEYWORDS_LABEL = 'show keywords';
22-
const HIDE_KEYWORDS_LABEL = 'hide keywords';
24+
const SHOW_KEYWORDS_LABEL = t(TEXT_ANALYSIS.BANNER_SHOW_KEYWORDS_BTN);
25+
const HIDE_KEYWORDS_LABEL = t(TEXT_ANALYSIS.BANNER_HIDE_KEYWORDS_BTN);
2326

2427
const toggleKeywords = (): void => {
2528
onClick(!showKeywords);

src/components/common/settings/GraaspButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FC } from 'react';
22

33
import { Button, SxProps } from '@mui/material';
44

5-
import { FULL_WIDTH, GRAASP_VIOLET } from '../../../config/stylingConstants';
5+
import { GRAASP_VIOLET } from '../../../config/stylingConstants';
66

77
type Prop = {
88
buttonDataCy: string;
@@ -28,11 +28,11 @@ const GraaspButton: FC<Prop> = ({
2828
<Button
2929
data-cy={buttonDataCy}
3030
variant="contained"
31+
fullWidth={fullWidth}
3132
sx={{
3233
backgroundColor: GRAASP_VIOLET,
3334
minHeight,
3435
marginRight,
35-
...(fullWidth && { width: FULL_WIDTH }),
3636
...sx,
3737
}}
3838
onClick={handleOnClick}

0 commit comments

Comments
 (0)