From 07b13c3f7aed682930e80dd3d0ba32d5db783169 Mon Sep 17 00:00:00 2001 From: Sudhanshu Gautam Date: Sat, 29 Jun 2019 02:40:53 +0530 Subject: [PATCH] adds translation support via i18n.js Uses react-i18next library to provide APIs for translational purposes. Translation data is loaded from `src/locales/{{lng}}/translation.json` and is accessed by dot notation. The data can be translated in two ways: 1. In functional components, a method `t` can be instantiated using the `useTranslation` method from rect-18next. 2. In class components, a method `t` can be accessed via props while exporting the component with `withTranslation` method from react-i18next library. The syntax will look like `t('data.data')` or `this.props.t('data.data')` Signed-off-by: Sudhanshu Gautam --- package.json | 1 - public/index.html | 2 +- src/App.js | 25 ++++++---- src/App.scss | 4 ++ src/components/header.js | 82 +++++++++++++++++++++++++-------- src/containers/home/home.js | 28 ++++++++--- src/i18n.js | 21 +++++---- src/locales/de/translation.json | 20 ++++++++ src/locales/en/translation.json | 20 ++++++++ yarn.lock | 7 --- 10 files changed, 156 insertions(+), 54 deletions(-) create mode 100644 src/locales/de/translation.json create mode 100644 src/locales/en/translation.json diff --git a/package.json b/package.json index 84d9054..8c1d6ca 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "gh-pages": "^2.0.1", "i18next": "^17.0.4", "i18next-browser-languagedetector": "^3.0.1", - "i18next-xhr-backend": "^3.0.0", "node-sass": "^4.12.0", "react": "^16.8.6", "react-dom": "^16.8.6", diff --git a/public/index.html b/public/index.html index 053c4bb..b6977ca 100644 --- a/public/index.html +++ b/public/index.html @@ -21,7 +21,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + OpenWrt Firmware Selector Wizard diff --git a/src/App.js b/src/App.js index 5768231..cc67f10 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import './App.scss'; @@ -7,6 +7,7 @@ import { ThemeProvider } from '@material-ui/styles'; import Header from './components/header.js' import Home from './containers/home/home'; import NotFound from './containers/not-found/not-found'; +import LinearProgress from '@material-ui/core/LinearProgress'; const theme = createMuiTheme({ palette: { @@ -22,15 +23,19 @@ const theme = createMuiTheme({ function App() { return ( -
-
- - - - - - -
+ + }> +
+
+ + + + + + +
+
); } diff --git a/src/App.scss b/src/App.scss index b41d297..7f6d16a 100644 --- a/src/App.scss +++ b/src/App.scss @@ -19,6 +19,10 @@ color: white; } +.language-selector-popper { + padding: 15px; +} + .App-link { color: #61dafb; } diff --git a/src/components/header.js b/src/components/header.js index 7ccdb17..00734aa 100644 --- a/src/components/header.js +++ b/src/components/header.js @@ -1,25 +1,67 @@ import React from "react"; -import AppBar from '@material-ui/core/AppBar'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; import LanguageIcon from '@material-ui/icons/Language'; -import { Toolbar } from '@material-ui/core'; +import { Toolbar, Typography, AppBar, Button, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Popper, Fade, Paper } from '@material-ui/core'; +import { useTranslation } from 'react-i18next'; +import i18next from 'i18next'; -class Header extends React.Component { - render() { - return ( - - - OpenWrt Firmware Selector Wizard -
- -
-
- ); +export default function Header() { + + const { t, i18n } = useTranslation(); + + const [value, setValue] = React.useState('en'); + const [anchorEl, setAnchorEl] = React.useState(null); + + const changeLanguage = event => { + var val = event.target.value; + i18n.changeLanguage(val); + setAnchorEl(null); + }; + + const openChangeLanguagePopper = event => { + setValue(i18next.language.substring(0, 2)); + setAnchorEl(anchorEl ? null : event.currentTarget); } -} -export default Header; + const open = Boolean(anchorEl); + const id = open ? 'simple-popper' : undefined; + + return ( + + + OpenWrt Firmware Selector Wizard +
+ + + {({ TransitionProps }) => ( + + + + Change Language +
+ + } label="English" /> + } label="German" /> + +
+
+
+ )} +
+
+
+ ); +} diff --git a/src/containers/home/home.js b/src/containers/home/home.js index b2d28c9..d13ed3a 100644 --- a/src/containers/home/home.js +++ b/src/containers/home/home.js @@ -3,6 +3,7 @@ import { Container, Paper, Typography, Grid, Button } from "@material-ui/core"; import './home.scss'; import Select from 'react-select'; import data from '../../data.json'; +import { withTranslation } from 'react-i18next'; class Home extends React.Component { @@ -145,12 +146,19 @@ class Home extends React.Component { }); } } + + noOptionsMessage = (props) => {this.props.t('components.select.noOptions')}; + render() { return ( - Download OpenWrt firmware for your device! - Please use the input below to download firmware for your device! + + {this.props.t('appIntro.head')} + + + {this.props.t('appIntro.para')} +
@@ -158,6 +166,8 @@ class Home extends React.Component { onChange={this.changeVendor} options={this.devices} value={this.state.vendor} + placeholder={this.props.t('components.select.placeholder')} + noOptionsMessage={this.noOptionsMessage} /> { @@ -167,6 +177,8 @@ class Home extends React.Component { onChange={this.changeModel} options={this.state.vendor.value} value={this.state.model} + placeholder={this.props.t('components.select.placeholder')} + noOptionsMessage={this.noOptionsMessage} /> ) @@ -179,6 +191,8 @@ class Home extends React.Component { onChange={this.changeVariant} options={this.state.model.value} value={this.state.variant} + placeholder={this.props.t('components.select.placeholder')} + noOptionsMessage={this.noOptionsMessage} /> ) @@ -191,24 +205,24 @@ class Home extends React.Component { variant="contained" onClick={this.findDevice.bind(this)} > - Submit + {this.props.t('components.submit')}
{this.state.showDeviceData ? ( - + - + { this.state.device.variant === null || this.state.device.variant === '' ? '' : ( - + ) @@ -230,4 +244,4 @@ class Home extends React.Component { } } -export default Home; +export default withTranslation()(Home); diff --git a/src/i18n.js b/src/i18n.js index 0c563de..d3bbfa7 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -1,16 +1,20 @@ import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; - -import Backend from 'i18next-xhr-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; -// not like to use this? -// have a look at the Quick start guide -// for passing in lng and translations on init +import translationEN from './locales/en/translation.json'; +import translationDE from './locales/de/translation.json'; + + +const resources = { + en: { + translation: translationEN + }, + de: { + translation: translationDE + } +}; i18n - // load translation using xhr -> see /public/locales - // learn more: https://github.com/i18next/i18next-xhr-backend - .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) @@ -19,6 +23,7 @@ i18n // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ + resources, fallbackLng: 'en', debug: true, diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json new file mode 100644 index 0000000..9daad3e --- /dev/null +++ b/src/locales/de/translation.json @@ -0,0 +1,20 @@ +{ + "title": "OpenWrt Firmware Selector Wizard", + "appIntro": { + "head": "Laden Sie die OpenWrt-Firmware für Ihr Gerät herunter!", + "para": "Bitte benutzen Sie den unten stehenden Eingang, um die Firmware für Ihr Gerät herunterzuladen!" + }, + "components": { + "submit": "einreichen", + "changeLanguage": "Sprache ändern", + "select": { + "placeholder": "Wählen...", + "noOptions": "Keine Optionen" + } + }, + "table": { + "model": "Modell", + "vendor": "Verkäufer", + "variant": "Variante" + } +} \ No newline at end of file diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json new file mode 100644 index 0000000..c11c28d --- /dev/null +++ b/src/locales/en/translation.json @@ -0,0 +1,20 @@ +{ + "title": "OpenWrt Firmware Selector Wizard", + "appIntro": { + "head": "Download OpenWrt firmware for your device!", + "para": "Please use the input below to download firmware for your device!" + }, + "components": { + "submit": "Submit", + "changeLanguage": "Change Language", + "select": { + "placeholder": "Select...", + "noOptions": "No options" + } + }, + "table": { + "model": "Model", + "vendor": "Vendor", + "variant": "Variant" + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 3a64f96..6e376e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5055,13 +5055,6 @@ i18next-browser-languagedetector@^3.0.1: resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-3.0.1.tgz#a47c43176e8412c91e808afb7c6eb5367649aa8e" integrity sha512-WFjPLNPWl62uu07AHY2g+KsC9qz0tyMq+OZEB/H7N58YKL/JLiCz9U709gaR20Mule/Ppn+uyfVx5REJJjn1HA== -i18next-xhr-backend@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/i18next-xhr-backend/-/i18next-xhr-backend-3.0.0.tgz#75235870c920291dbfd32043505b7ede0dc87da3" - integrity sha512-Pi/X91Zk2nEqdEHTV+FG6VeMHRcMcPKRsYW/A0wlaCfKsoJc3TI7A75Tqse/d5LVGN2Ymzx0FT+R+gLag9Eb2g== - dependencies: - "@babel/runtime" "^7.4.5" - i18next@^17.0.4: version "17.0.4" resolved "https://registry.yarnpkg.com/i18next/-/i18next-17.0.4.tgz#c690b9de0c950ff8abe626562d03c4144dd75030"
Model{this.props.t('table.model')} {this.state.device.model}
Vendor{this.props.t('table.vendor')} {this.state.device.vendor}
Variant{this.props.t('table.variant')} {this.state.device.variant}