diff --git a/package.json b/package.json index b76f953c..e2415ed5 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,9 @@ }, "dependencies": { "clsx": "1.1.1", - "immutable": "4.0.0-rc.12" + "i18next": "20.4.0", + "immutable": "4.0.0-rc.12", + "react-i18next": "11.11.4" }, "devDependencies": { "@commitlint/cli": "12.1.4", diff --git a/src/components/Chatbox.tsx b/src/components/Chatbox.tsx index 1f6acad7..b35847cf 100644 --- a/src/components/Chatbox.tsx +++ b/src/components/Chatbox.tsx @@ -37,7 +37,7 @@ const Chatbox: FC = ({
- + diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 3b5eb377..d5e6cfbf 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,5 @@ import React, { FC } from 'react'; +import { useTranslation } from 'react-i18next'; import { makeStyles } from '@material-ui/core/styles'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; @@ -26,6 +27,7 @@ type Props = { const Header: FC = ({ title }) => { const classes = useStyles(); + const { t } = useTranslation(); return (
@@ -33,7 +35,7 @@ const Header: FC = ({ title }) => { - {title || 'Chatbox'} + {title || t('Chatbox')} diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 971eb561..e23aedfc 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -2,6 +2,7 @@ import React, { FC, useRef } from 'react'; import IconButton from '@material-ui/core/IconButton'; import TextField from '@material-ui/core/TextField'; import Box from '@material-ui/core/Box'; +import { useTranslation } from 'react-i18next'; import SendIcon from '@material-ui/icons/Send'; import { makeStyles } from '@material-ui/core/styles'; import { PartialChatMessage } from '../types'; @@ -23,13 +24,15 @@ const useStyles = makeStyles((theme) => ({ const Input: FC = ({ chatId, placeholder, sendMessageFunction }) => { const classes = useStyles(); const textRef = useRef(); + const { t } = useTranslation(); const onClick = (): void => { - const text = textRef?.current?.value; - if (!text) { - return; + if (textRef?.current?.value) { + const text = textRef?.current?.value; + sendMessageFunction?.({ chatId, body: text }); + // reset input content + textRef.current.value = ''; } - return sendMessageFunction?.({ chatId, body: text }); }; return ( @@ -44,7 +47,7 @@ const Input: FC = ({ chatId, placeholder, sendMessageFunction }) => { id="outlined-basic" variant="outlined" fullWidth - placeholder={placeholder || 'Type something...'} + placeholder={placeholder || t('Type something…')} /> diff --git a/src/components/Messages.tsx b/src/components/Messages.tsx index a5adfaa9..5b08fde3 100644 --- a/src/components/Messages.tsx +++ b/src/components/Messages.tsx @@ -7,44 +7,44 @@ import type { ChatMessage } from '../types'; type Props = { messages?: List; + height?: number; }; -const useStyles = makeStyles(() => ({ - wrapper: { - overflowY: 'scroll', - height: '90%', - flexDirection: 'column', - alignItems: 'flex-start', - display: 'flex', - justifyContent: 'flex-end', - }, -})); +const Messages: FC = ({ messages, height }) => { + const ref = useRef(null); -const Messages: FC = ({ messages }) => { - const boxRef = useRef(); + const useStyles = makeStyles(() => ({ + container: { + overflowY: 'scroll', + height, + }, + messagesContainer: { + flexDirection: 'column', + alignItems: 'flex-start', + display: 'flex', + justifyContent: 'flex-end', + }, + })); const classes = useStyles(); - // scroll down to last message + // scroll down to last message at start and on new message useEffect(() => { - if (boxRef?.current) { + if (ref?.current) { // really big number to scroll down - boxRef.current.scrollTop = 99999; + ref.current.scrollTop = 99999; } - }, [boxRef]); + }, [ref, messages]); return ( - - {messages?.map((message) => ( - // todo: apply key - - ))} - +
+ + {messages?.map((message) => ( + // todo: apply key + + ))} + +
); }; diff --git a/src/config/i18n.ts b/src/config/i18n.ts new file mode 100644 index 00000000..58c3df44 --- /dev/null +++ b/src/config/i18n.ts @@ -0,0 +1,61 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import en from '../langs/en.json'; +import fr from '../langs/fr.json'; + +i18n.use(initReactI18next).init({ + resources: { + en, + fr, + }, + lng: 'en', + fallbackLng: 'en', + // debug only when not in production + debug: process.env.NODE_ENV !== 'production', + ns: ['translations'], + defaultNS: 'translations', + keySeparator: false, + interpolation: { + escapeValue: false, + formatSeparator: ',', + }, + react: { + wait: true, + }, +}); + +const langs = { + // bg: "български", + // ca: "Català", + // cs: "čeština", + // de: "Deutsch", + // el: "Ελληνικά", + en: 'English', + // es: "Español", + // et: "Eesti", + // fi: "Suomi", + fr: 'Français', + // hu: "Magyar", + // it: "Italiano", + // ja: '日本語', + // ka: "ქართული", + // lt: "lietuvių kalba", + // lv: "Latviešu", + // nl: "Nederlands", + // pt: "Português", + // ro: "Română", + // ru: "Русский", + // sk: "Slovenský", + // sl: "Slovenščina", + // sr: "српски језик", + // sw: 'Kiswahili', + // tr: "Türkçe", + // uk: "Українська", + // vi: "Tiếng Việt", + // zh: "简体中文", + // zh_tw: "繁體中文", +}; + +export { langs }; + +export default i18n; diff --git a/src/langs/en.json b/src/langs/en.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/src/langs/en.json @@ -0,0 +1 @@ +{} diff --git a/src/langs/fr.json b/src/langs/fr.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/src/langs/fr.json @@ -0,0 +1 @@ +{} diff --git a/yarn.lock b/yarn.lock index ad406e0a..787ca854 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1609,6 +1609,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.14.5": + version: 7.15.3 + resolution: "@babel/runtime@npm:7.15.3" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: 2f0b8d2d4e36035ab1d84af0ec26aafa098536870f27c8e07de0a0e398f7a394fdea68a88165535ffb52ded6a68912bdc3450bdf91f229eb132e1c89470789f5 + languageName: node + linkType: hard + "@babel/template@npm:^7.10.4, @babel/template@npm:^7.14.5, @babel/template@npm:^7.3.3": version: 7.14.5 resolution: "@babel/template@npm:7.14.5" @@ -1921,10 +1930,12 @@ __metadata: eslint-plugin-react-app: ^6.0.0 eslint-plugin-standard: ^5.0.0 husky: 7.0.1 + i18next: 20.4.0 immutable: 4.0.0-rc.12 npm-run-all: ^4.1.5 prettier: ^2.0.4 pretty-quick: 3.1.1 + react-i18next: 11.11.4 rimraf: 3.0.2 rollup: 2.55.1 rollup-plugin-peer-deps-external: 2.2.4 @@ -1934,7 +1945,6 @@ __metadata: "@material-ui/core": "*" "@material-ui/icons": "*" "@material-ui/lab": "*" - "@material-ui/styles": "*" react: "*" react-dom: "*" languageName: unknown @@ -8981,6 +8991,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"html-parse-stringify@npm:^3.0.1": + version: 3.0.1 + resolution: "html-parse-stringify@npm:3.0.1" + dependencies: + void-elements: 3.1.0 + checksum: 334fdebd4b5c355dba8e95284cead6f62bf865a2359da2759b039db58c805646350016d2017875718bc3c4b9bf81a0d11be5ee0cf4774a3a5a7b97cde21cfd67 + languageName: node + linkType: hard + "html-webpack-plugin@npm:4.5.0": version: 4.5.0 resolution: "html-webpack-plugin@npm:4.5.0" @@ -9154,6 +9173,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"i18next@npm:20.4.0": + version: 20.4.0 + resolution: "i18next@npm:20.4.0" + dependencies: + "@babel/runtime": ^7.12.0 + checksum: e2e3697132317f241b6290d4d0b549b0e445a9b400666505f549d62d38cd2f8347fb511e79c6f88f59021f0c9976c6f18cfe345fa3f223c110a3b0072a256f66 + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -14100,6 +14128,19 @@ fsevents@^1.2.7: languageName: node linkType: hard +"react-i18next@npm:11.11.4": + version: 11.11.4 + resolution: "react-i18next@npm:11.11.4" + dependencies: + "@babel/runtime": ^7.14.5 + html-parse-stringify: ^3.0.1 + peerDependencies: + i18next: ">= 19.0.0" + react: ">= 16.8.0" + checksum: 5630bfe1b39e6ce893fcccfba62cd42db92d130c0d00f181ec3bc68884ee0db3c698d0b6320d05dc783e5e32c14f7e547bb8aa034788a5f317563ad32f29edf9 + languageName: node + linkType: hard + "react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.6": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -16877,6 +16918,13 @@ typescript@^4.3.5: languageName: node linkType: hard +"void-elements@npm:3.1.0": + version: 3.1.0 + resolution: "void-elements@npm:3.1.0" + checksum: 0390f818107fa8fce55bb0a5c3f661056001c1d5a2a48c28d582d4d847347c2ab5b7f8272314cac58acf62345126b6b09bea623a185935f6b1c3bbce0dfd7f7f + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2"