Add i18next
This commit is contained in:
parent
f9ec31fd7a
commit
ee62b85008
30
package-lock.json
generated
30
package-lock.json
generated
@ -5475,6 +5475,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"html-parse-stringify2": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=",
|
||||||
|
"requires": {
|
||||||
|
"void-elements": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"html-webpack-plugin": {
|
"html-webpack-plugin": {
|
||||||
"version": "4.0.0-beta.11",
|
"version": "4.0.0-beta.11",
|
||||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz",
|
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz",
|
||||||
@ -5650,6 +5658,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"i18next": {
|
||||||
|
"version": "19.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-19.1.0.tgz",
|
||||||
|
"integrity": "sha512-ISbmukX4L6Dz0QoH9+EW1AnBw7j+NRLoMu9uLPMaNSSTP9Eie9/oUL0dOyWX15baB3gYOpkHJpGZRHOqcnl0ew==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@ -9497,6 +9513,15 @@
|
|||||||
"integrity": "sha512-ueZzLmHltszTshDMwyfELDq8zOA803wQ1ZuzCccXa1m57k1PxSHfflPD5W9YIiTXLs0JTLzoj6o1LuM5N6zzNA==",
|
"integrity": "sha512-ueZzLmHltszTshDMwyfELDq8zOA803wQ1ZuzCccXa1m57k1PxSHfflPD5W9YIiTXLs0JTLzoj6o1LuM5N6zzNA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"react-i18next": {
|
||||||
|
"version": "11.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.3.1.tgz",
|
||||||
|
"integrity": "sha512-S/CWHcnew1lXo8HeniGhBU5kTmPhZ4w4rtA4m/gDN07soCtKKYSAcLNm7zhwjI2OSR4Skd0vOtzNp/FzEEjxIw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"html-parse-stringify2": "2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-icons": {
|
"react-icons": {
|
||||||
"version": "3.8.0",
|
"version": "3.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.8.0.tgz",
|
||||||
@ -11548,6 +11573,11 @@
|
|||||||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"void-elements": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
|
||||||
|
},
|
||||||
"walk-back": {
|
"walk-back": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/walk-back/-/walk-back-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/walk-back/-/walk-back-4.0.0.tgz",
|
||||||
|
@ -18,8 +18,10 @@
|
|||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"debounce": "^1.2.0",
|
"debounce": "^1.2.0",
|
||||||
|
"i18next": "^19.1.0",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
|
"react-i18next": "^11.3.1",
|
||||||
"react-icons": "^3.8.0",
|
"react-icons": "^3.8.0",
|
||||||
"react-redux": "^7.1.3",
|
"react-redux": "^7.1.3",
|
||||||
"redux": "^4.0.4",
|
"redux": "^4.0.4",
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { RsvpReader } from './components/RsvpReader'
|
import { RsvpReader } from './components/RsvpReader'
|
||||||
import { GutenbergSearch } from './components/GutenbergSearch'
|
import { GutenbergSearch } from './components/GutenbergSearch'
|
||||||
|
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
import { LangSelect } from './components/LangSelect'
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header>
|
<header>
|
||||||
<h1>The fast reader</h1>
|
<h1>{t('title')}</h1>
|
||||||
|
<LangSelect />
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<RsvpReader></RsvpReader>
|
<RsvpReader></RsvpReader>
|
||||||
|
22
src/components/LangSelect.js
Normal file
22
src/components/LangSelect.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
import { languages } from '../i18n'
|
||||||
|
|
||||||
|
export const LangSelect = () => {
|
||||||
|
const { i18n } = useTranslation()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<select
|
||||||
|
value={i18n.language}
|
||||||
|
onChange={evt => i18n.changeLanguage(evt.target.value)}
|
||||||
|
>
|
||||||
|
{languages.map(lng => (
|
||||||
|
<option key={lng} value={lng}>
|
||||||
|
{lng}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -5,10 +5,12 @@ import { debounce } from 'debounce'
|
|||||||
import { setMaxLength, setWpm, setOffset, setLang } from '../store/actions'
|
import { setMaxLength, setWpm, setOffset, setLang } from '../store/actions'
|
||||||
import { Slider } from './Slider'
|
import { Slider } from './Slider'
|
||||||
import { selectOffset, selectLang } from '../store/selectors'
|
import { selectOffset, selectLang } from '../store/selectors'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const availableLanguages = ['en', 'de']
|
const availableLanguages = ['en', 'de']
|
||||||
|
|
||||||
export const Options = () => {
|
export const Options = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const maxLength = useSelector(state => state.maxLength)
|
const maxLength = useSelector(state => state.maxLength)
|
||||||
const wpm = useSelector(state => state.wpm)
|
const wpm = useSelector(state => state.wpm)
|
||||||
@ -17,23 +19,23 @@ export const Options = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Options</h2>
|
<h2>{t('options.title')}</h2>
|
||||||
<Slider
|
<Slider
|
||||||
title={'Maximum segment length'}
|
title={t('options.maxLength')}
|
||||||
min={3}
|
min={3}
|
||||||
max={15}
|
max={15}
|
||||||
value={maxLength}
|
value={maxLength}
|
||||||
onChange={debounce(val => dispatch(setMaxLength(val)), 100)}
|
onChange={debounce(val => dispatch(setMaxLength(val)), 100)}
|
||||||
/>
|
/>
|
||||||
<Slider
|
<Slider
|
||||||
title={'Words per minute'}
|
title={t('options.wpm')}
|
||||||
min={100}
|
min={100}
|
||||||
max={1000}
|
max={1000}
|
||||||
value={wpm}
|
value={wpm}
|
||||||
onChange={debounce(val => dispatch(setWpm(val)), 50)}
|
onChange={debounce(val => dispatch(setWpm(val)), 50)}
|
||||||
/>
|
/>
|
||||||
<Slider
|
<Slider
|
||||||
title={'Offset from center'}
|
title={t('options.offset')}
|
||||||
min={-50}
|
min={-50}
|
||||||
max={50}
|
max={50}
|
||||||
value={offset}
|
value={offset}
|
||||||
|
10
src/i18n/de.json
Normal file
10
src/i18n/de.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"title": "Der schnelle leser",
|
||||||
|
"search": "Suche",
|
||||||
|
"options": {
|
||||||
|
"title": "Optionen",
|
||||||
|
"maxLength": "Maximale Segmentlänge",
|
||||||
|
"wpm": "Wörter pro Minute",
|
||||||
|
"offset": "Versatz der Wortanzeige"
|
||||||
|
}
|
||||||
|
}
|
10
src/i18n/en.json
Normal file
10
src/i18n/en.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"title": "The Fast Reader",
|
||||||
|
"search": "Search",
|
||||||
|
"options": {
|
||||||
|
"title": "Options",
|
||||||
|
"maxLength": "Maximum segment length",
|
||||||
|
"wpm": "Words per minute",
|
||||||
|
"offset": "Offset from center"
|
||||||
|
}
|
||||||
|
}
|
22
src/i18n/index.js
Normal file
22
src/i18n/index.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import i18n from 'i18next'
|
||||||
|
import { initReactI18next } from 'react-i18next'
|
||||||
|
|
||||||
|
import de from './de.json'
|
||||||
|
import en from './en.json'
|
||||||
|
|
||||||
|
const resources = {
|
||||||
|
en: { translation: en },
|
||||||
|
de: { translation: de }
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(de, en)
|
||||||
|
i18n.use(initReactI18next).init({
|
||||||
|
resources,
|
||||||
|
lng: 'en',
|
||||||
|
fallbackLng: 'en',
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const languages = Object.keys(resources)
|
@ -7,6 +7,8 @@ import { Provider } from 'react-redux'
|
|||||||
import { App } from './App'
|
import { App } from './App'
|
||||||
import { store } from './store/index.js'
|
import { store } from './store/index.js'
|
||||||
|
|
||||||
|
import './i18n'
|
||||||
|
|
||||||
function createRootElement() {
|
function createRootElement() {
|
||||||
const body = document.getElementsByTagName('body')[0]
|
const body = document.getElementsByTagName('body')[0]
|
||||||
const root = document.createElement('div')
|
const root = document.createElement('div')
|
||||||
|
Loading…
Reference in New Issue
Block a user