Add translations

This commit is contained in:
Kacper Donat 2020-07-13 23:10:05 +02:00
parent 1a81c7ead5
commit e5cf8ff107
10 changed files with 189 additions and 51 deletions

View File

@ -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",

View File

@ -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<AppState>(state => state.student);
const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
const student = useSelector<AppState, Student>(state => state.student as Student);
const dispatch = useDispatch<Dispatch<StudentAction>>();
const { t } = useTranslation();
const handleUserLogin = () => {
dispatch({
@ -41,41 +45,32 @@ const UserMenu = () => {
dispatch({ type: StudentActions.Logout })
}
return student ? <>
zalogowany jako: <strong>Jan Kowalski</strong>
{' '}
(<Link to={'#'} onClick={ handleUserLogout }>wyloguj się</Link>)
</> : <>
<Link to={'#'} onClick={ handleUserLogin }>zaloguj się</Link>
</>;
return <ul {...props}>
{
student ? <>
<Trans t={ t } i18nKey="logged-in-as">logged in as <strong>{{ name: student.name }}</strong></Trans>
{' '}
(<Link to={'#'} onClick={ handleUserLogout }>{ t('logout') }</Link>)
</> : <>
<Link to={'#'} onClick={ handleUserLogin }>{ t('login') }</Link>
</>
}
</ul>;
}
const AppHeader = () => {
return <header className="header">
<div id="logo" className="header__logo">
<Link to={ route('home') }>
<img src="img/pg-logotyp.svg"/>
</Link>
</div>
<div className="header__nav">
<nav className="header__top">
<ul className="header__menu"></ul>
<div className="header__user">
<UserMenu />
</div>
<div className="header__divider" />
<ul className="header__language-switcher">
<li>pl</li>
<li>en</li>
</ul>
</nav>
<nav className="header__bottom">
<ul className="header__menu header__menu--main"></ul>
</nav>
</div>
</header>
}
const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>) => {
const { i18n } = useTranslation();
const handleLanguageChange = (language: string) => () => {
i18n.changeLanguage(language);
}
return <ul className={ classNames(className, "language-switcher") } { ...props }>
{ ['pl', 'en'].map(language => <li key={ language }>
<Link to="#" onClick={ handleLanguageChange(language) }>{ language }</Link>
</li>) }
</ul>
}
function App() {
return (
@ -84,7 +79,24 @@ function App() {
<MuiPickersUtilsProvider utils={ LocalizedMomentUtils } libInstance={ moment }>
<ThemeProvider theme={ studentTheme }>
<BrowserRouter>
<AppHeader />
<header className="header">
<div id="logo" className="header__logo">
<Link to={ route('home') }>
<img src="img/pg-logotyp.svg"/>
</Link>
</div>
<div className="header__nav">
<nav className="header__top">
<ul className="header__menu"></ul>
<UserMenu className="header__user"/>
<div className="header__divider" />
<LanguageSwitcher className="header__language-switcher"/>
</nav>
<nav className="header__bottom">
<ul className="header__menu header__menu--main"></ul>
</nav>
</div>
</header>
<Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch>
</BrowserRouter>
</ThemeProvider>

26
src/i18n.ts Normal file
View File

@ -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;

View File

@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import "./i18n"
import App from './app';
ReactDOM.render(

View File

@ -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 <Page my={6}>
<Container>
<Typography variant="h2">Moja praktyka</Typography>
<Typography variant="h2">{ t("sections.my-internship.header") }</Typography>
<Stepper orientation="vertical" nonLinear>
<Step completed active={false}>
<StepLabel>
Uzupełnienie danych w systemie
{ t('steps.personal-data.header') }
<Box>
<Typography variant="subtitle2" color="textSecondary">do: 05.07.2020</Typography>
<Typography variant="subtitle2" color="textSecondary">{ t('until', { date: '05.07.2020' }) }</Typography>
</Box>
</StepLabel>
<StepContent>lol</StepContent>
</Step>
<Step active>
<StepLabel>Zgłoszenie praktyki</StepLabel>
<StepLabel>{ t('steps.internship-proposal.header')}</StepLabel>
<StepContent>
<Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }>Formularz zgłoszenia praktyki</Button>
<Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }>
{ t('steps.internship-proposal.form') }
</Button>
</StepContent>
</Step>
</Stepper>

View File

@ -57,7 +57,11 @@
text-transform: uppercase;
li {
padding: 7px;
list-style: none;
a {
padding: 7px;
display: block;
}
}
}

17
translations/en.yaml Normal file
View File

@ -0,0 +1,17 @@
---
login: login
logout: logout
logged-in-as: logged in as <1>{{ name }}</1>
until: until {{ date }}
sections:
my-internship:
header: "My internship"
steps:
personal-data:
header: "Personal data"
internship-proposal:
header: "Internship proposal"
form: "Internship proposal form"

17
translations/pl.yaml Normal file
View File

@ -0,0 +1,17 @@
---
login: zaloguj się
logout: wyloguj się
logged-in-as: zalogowany jako <1>{{ name }}</1>
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"

View File

@ -34,6 +34,10 @@ const config = {
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: 'file-loader'
}, {
test: /\.ya?ml$/,
type: 'json',
use: 'yaml-loader'
}]
},
plugins: [

View File

@ -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==