From e5cf8ff1074c4019690d40a228b35965062f3510 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 13 Jul 2020 23:10:05 +0200 Subject: [PATCH] Add translations --- package.json | 6 ++- src/app.tsx | 96 ++++++++++++++++++++++++------------------ src/i18n.ts | 26 ++++++++++++ src/index.tsx | 1 + src/pages/main.tsx | 16 ++++--- src/styles/header.scss | 6 ++- translations/en.yaml | 17 ++++++++ translations/pl.yaml | 17 ++++++++ webpack.config.js | 4 ++ yarn.lock | 51 +++++++++++++++++++++- 10 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 src/i18n.ts create mode 100644 translations/en.yaml create mode 100644 translations/pl.yaml diff --git a/package.json b/package.json index 7188204..8f9be5c 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "date-holidays": "^1.5.3", "file-loader": "4.3.0", "html-webpack-plugin": "4.0.0-beta.11", + "i18next": "^19.6.0", + "i18next-browser-languagedetector": "^5.0.0", "moment": "^2.26.0", "node-sass": "^4.14.1", "optimize-css-assets-webpack-plugin": "5.0.3", @@ -40,6 +42,7 @@ "react-app-polyfill": "^1.0.6", "react-dev-utils": "^10.2.1", "react-dom": "^16.13.1", + "react-i18next": "^11.7.0", "react-redux": "^7.2.0", "react-router-dom": "^5.2.0", "redux": "^4.0.5", @@ -52,7 +55,8 @@ "webpack": "4.42.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "3.10.3", - "workbox-webpack-plugin": "4.3.1" + "workbox-webpack-plugin": "4.3.1", + "yaml-loader": "^0.6.0" }, "scripts": { "serve": "webpack-dev-server --mode development", diff --git a/src/app.tsx b/src/app.tsx index d196c3d..f078d76 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,14 +1,9 @@ -import React, { Dispatch } from 'react'; +import React, { Dispatch, HTMLProps } from 'react'; import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles"; import { studentTheme } from "./ui/theme"; import { MuiPickersUtilsProvider } from "@material-ui/pickers"; import MomentUtils from "@date-io/moment"; import { BrowserRouter, Link, Route, Switch } from "react-router-dom" - -import "moment/locale/pl" -import '@/styles/overrides.scss' -import '@/styles/header.scss' - import moment, { Moment } from "moment"; import { route, routes } from "@/routing"; import { Provider, useDispatch, useSelector } from "react-redux"; @@ -16,6 +11,15 @@ import store from "@/state/store"; import { AppState } from "@/state/reducer"; import { StudentAction, StudentActions } from "@/state/actions/student"; import { sampleStudent } from "@/provider/dummy/student"; +import { Trans, useTranslation } from "react-i18next"; +import { StudentState } from "@/state/reducer/student"; +import { Student } from "@/data"; + +import "moment/locale/pl" +import '@/styles/overrides.scss' +import '@/styles/header.scss' +import classNames from "classnames"; +import { Button } from "@material-ui/core"; moment.locale("pl") @@ -25,10 +29,10 @@ class LocalizedMomentUtils extends MomentUtils { } } -const UserMenu = () => { - const student = useSelector(state => state.student); - +const UserMenu = (props: HTMLProps) => { + const student = useSelector(state => state.student as Student); const dispatch = useDispatch>(); + const { t } = useTranslation(); const handleUserLogin = () => { dispatch({ @@ -41,41 +45,32 @@ const UserMenu = () => { dispatch({ type: StudentActions.Logout }) } - return student ? <> - zalogowany jako: Jan Kowalski - {' '} - (wyloguj się) - : <> - zaloguj się - ; + return
    + { + student ? <> + logged in as {{ name: student.name }} + {' '} + ({ t('logout') }) + : <> + { t('login') } + + } +
; } -const AppHeader = () => { - return
- -
- - -
-
-} +const LanguageSwitcher = ({ className, ...props }: HTMLProps) => { + const { i18n } = useTranslation(); + const handleLanguageChange = (language: string) => () => { + i18n.changeLanguage(language); + } + + return
    + { ['pl', 'en'].map(language =>
  • + { language } +
  • ) } +
+} function App() { return ( @@ -84,7 +79,24 @@ function App() { - +
+ +
+ + +
+
{ routes.map(({ name, content, ...route }) => { content() }) }
diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..a38e4f0 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,26 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; + +const resources = { + en: { + translation: require('../translations/en.yaml'), + }, + pl: { + translation: require('../translations/pl.yaml'), + } +} + +i18n + .use(I18nextBrowserLanguageDetector) + .use(initReactI18next) + .init({ + resources, + fallbackLng: "en", + debug: true, + interpolation: { + escapeValue: false + } + }) + +export default i18n; diff --git a/src/index.tsx b/src/index.tsx index 902d328..4bbd47a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import "./i18n" import App from './app'; ReactDOM.render( diff --git a/src/pages/main.tsx b/src/pages/main.tsx index d343098..9bf4978 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -4,25 +4,29 @@ import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, import { InternshipForm } from "@/forms/Internship"; import { Link as RouterLink } from "react-router-dom"; import { route } from "@/routing"; +import { useTranslation } from "react-i18next"; export const MainPage = () => { + const { t } = useTranslation(); + return - Moja praktyka + { t("sections.my-internship.header") } - Uzupełnienie danych w systemie + { t('steps.personal-data.header') } - do: 05.07.2020 + { t('until', { date: '05.07.2020' }) } - lol - Zgłoszenie praktyki + { t('steps.internship-proposal.header')} - + diff --git a/src/styles/header.scss b/src/styles/header.scss index 4f5c722..ea7b6c1 100644 --- a/src/styles/header.scss +++ b/src/styles/header.scss @@ -57,7 +57,11 @@ text-transform: uppercase; li { - padding: 7px; list-style: none; + + a { + padding: 7px; + display: block; + } } } diff --git a/translations/en.yaml b/translations/en.yaml new file mode 100644 index 0000000..cfec6f8 --- /dev/null +++ b/translations/en.yaml @@ -0,0 +1,17 @@ +--- +login: login +logout: logout +logged-in-as: logged in as <1>{{ name }} + +until: until {{ date }} + +sections: + my-internship: + header: "My internship" + +steps: + personal-data: + header: "Personal data" + internship-proposal: + header: "Internship proposal" + form: "Internship proposal form" diff --git a/translations/pl.yaml b/translations/pl.yaml new file mode 100644 index 0000000..7c1bcbf --- /dev/null +++ b/translations/pl.yaml @@ -0,0 +1,17 @@ +--- +login: zaloguj się +logout: wyloguj się +logged-in-as: zalogowany jako <1>{{ name }} + +until: do {{ date }} + +sections: + my-internship: + header: "Moja praktyka" + +steps: + personal-data: + header: "Uzupełnienie informacji" + internship-proposal: + header: "Zgłoszenie praktyki" + form: "Formularz zgłaszania praktyki" diff --git a/webpack.config.js b/webpack.config.js index 916d8ea..6aeedda 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -34,6 +34,10 @@ const config = { }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: 'file-loader' + }, { + test: /\.ya?ml$/, + type: 'json', + use: 'yaml-loader' }] }, plugins: [ diff --git a/yarn.lock b/yarn.lock index 48105c9..29848d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -926,6 +926,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.1": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.4.tgz#a6724f1a6b8d2f6ea5236dbfe58c7d7ea9c5eb99" + integrity sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.1", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" @@ -4388,6 +4395,13 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +html-parse-stringify2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + html-webpack-plugin@4.0.0-beta.11: version "4.0.0-beta.11" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715" @@ -4492,6 +4506,20 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== +i18next-browser-languagedetector@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-5.0.0.tgz#9e946ed2ea5514a636913fe020a32455e82946e3" + integrity sha512-ekeKbRvTOsSOABSEPHFqyb6Q37JagZXjkISgQKHP84t/VZRW/B3FMVz+tBNQDVdZLsEaOe8fuJpeZsw2TvWeVQ== + dependencies: + "@babel/runtime" "^7.5.5" + +i18next@^19.6.0: + version "19.6.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.6.0.tgz#3881b8e476e494dcdadcc8983e594080417fd82e" + integrity sha512-t+pA7iN2WtwS1UQc4PFKHDIO4HYZIl2Wo8UC8gqt70Q1qY50FflAF5vV4IbQEqy4DuK3I9wv3BL1PMvkk238WA== + dependencies: + "@babel/runtime" "^7.10.1" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7323,6 +7351,14 @@ react-error-overlay@^6.0.7: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== +react-i18next@^11.7.0: + version "11.7.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.7.0.tgz#f27c4c237a274e007a48ac1210db83e33719908b" + integrity sha512-8tvVkpuxQlubcszZON+jmoCgiA9gCZ74OAYli9KChPhETtq8pJsANBTe9KRLRLmX3ubumgvidURWr0VvKz1tww== + dependencies: + "@babel/runtime" "^7.3.1" + html-parse-stringify2 "2.0.1" + react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -8910,6 +8946,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + watchpack-chokidar2@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" @@ -9290,7 +9331,15 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^1.7.2: +yaml-loader@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.6.0.tgz#fe1c48b9f4803dace55a59a1474e790ba6ab1b48" + integrity sha512-1bNiLelumURyj+zvVHOv8Y3dpCri0F2S+DCcmps0pA1zWRLjS+FhZQg4o3aUUDYESh73+pKZNI18bj7stpReow== + dependencies: + loader-utils "^1.4.0" + yaml "^1.8.3" + +yaml@^1.7.2, yaml@^1.8.3: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==