From 2edad0d5ec6c7b2ff78dc30735f6ddfb7c52bdc6 Mon Sep 17 00:00:00 2001 From: Chris <chris@chrissnijder.nl> Date: Thu, 4 Feb 2021 00:43:07 +0100 Subject: [PATCH] First stab at i18n, not full l10n, but it will do. --- jest.setup.js | 3 +- package.json | 7 +- pnpm-lock.yaml | 75 ++++++++++++++++++- src/App.tsx | 17 ++--- .../locale-switcher/LocaleSwitcher.test.tsx | 14 ++++ .../locale-switcher/LocaleSwitcher.tsx | 14 ++++ src/index.tsx | 1 + src/services/i18n.ts | 33 ++++++++ 8 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 src/components/locale-switcher/LocaleSwitcher.test.tsx create mode 100644 src/components/locale-switcher/LocaleSwitcher.tsx create mode 100644 src/services/i18n.ts diff --git a/jest.setup.js b/jest.setup.js index cbe7c3d..dbc2608 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1 +1,2 @@ -import "@testing-library/jest-dom/extend-expect"; \ No newline at end of file +import "@testing-library/jest-dom/extend-expect"; +import "./src/services/i18n"; diff --git a/package.json b/package.json index 8468be0..e3df250 100644 --- a/package.json +++ b/package.json @@ -17,17 +17,22 @@ "author": "Mark Swillus, Tin Geber, Chris Snijder", "license": "Apache-2.0", "dependencies": { + "i18next": "^19.8.7", + "js-yaml": "^4.0.0", "react": "^17.0.0", - "react-dom": "^17.0.0" + "react-dom": "^17.0.0", + "react-i18next": "^11.8.5" }, "devDependencies": { "@snowpack/app-scripts-react": "^2.0.0", + "@snowpack/plugin-build-script": "^2.1.0", "@snowpack/plugin-dotenv": "^2.0.5", "@snowpack/plugin-react-refresh": "^2.4.0", "@snowpack/plugin-sass": "^1.3.0", "@snowpack/plugin-typescript": "^1.2.0", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.0.0", + "@testing-library/user-event": "^12.6.3", "@types/jest": "^26.0.20", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 154297b..38b3c86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,14 +1,19 @@ dependencies: + i18next: 19.8.7 + js-yaml: 4.0.0 react: 17.0.1 react-dom: 17.0.1_react@17.0.1 + react-i18next: 11.8.5_i18next@19.8.7+react@17.0.1 devDependencies: '@snowpack/app-scripts-react': 2.0.0_c39e9db791ea05e3ec4e0e74abb7a6b5 + '@snowpack/plugin-build-script': 2.1.0 '@snowpack/plugin-dotenv': 2.0.5 '@snowpack/plugin-react-refresh': 2.4.0_react-dom@17.0.1+react@17.0.1 '@snowpack/plugin-sass': 1.3.0 '@snowpack/plugin-typescript': 1.2.1_typescript@4.1.3 '@testing-library/jest-dom': 5.11.9 '@testing-library/react': 11.2.3_react-dom@17.0.1+react@17.0.1 + '@testing-library/user-event': 12.6.3 '@types/jest': 26.0.20 '@types/react': 17.0.0 '@types/react-dom': 17.0.0 @@ -1223,6 +1228,11 @@ packages: dev: true resolution: integrity: sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ== + /@babel/runtime/7.12.13: + dependencies: + regenerator-runtime: 0.13.7 + resolution: + integrity: sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw== /@babel/runtime/7.12.5: dependencies: regenerator-runtime: 0.13.7 @@ -1517,6 +1527,13 @@ packages: dev: true resolution: integrity: sha512-UB5KNQDQ0vnPUSGJ1xscILuuT8eyDK46VCAuImDITYbCy5mf812qwhrvNjbZmGIfnkDmTS2RKopP2cbouDzzSw== + /@snowpack/plugin-build-script/2.1.0: + dependencies: + execa: 5.0.0 + npm-run-path: 4.0.1 + dev: true + resolution: + integrity: sha512-rNuNxI5fM4QieNSOlwhZVD+0iA0nhEAS8MgXStCeH3mr0lC8dVLSYRTt83bS4Q6pxF9zFFfY5z+CTq/JVz8aUw== /@snowpack/plugin-dotenv/2.0.5: dependencies: dotenv: 8.2.0 @@ -1601,6 +1618,17 @@ packages: react-dom: '*' resolution: integrity: sha512-BirBUGPkTW28ULuCwIbYo0y2+0aavHczBT6N9r3LrsswEW3pg25l1wgoE7I8QBIy1upXWkwKpYdWY7NYYP0Bxw== + /@testing-library/user-event/12.6.3: + dependencies: + '@babel/runtime': 7.12.13 + dev: true + engines: + node: '>=10' + npm: '>=6' + peerDependencies: + '@testing-library/dom': '>=7.21.4' + resolution: + integrity: sha512-PCmbUKofE4SXA7l8jphZAbvv5H3c4ix34xPZ/GNe99fASX//msJRgiMbHIBP+GwRfgVG9c7zmkODSPu2X2vNRw== /@types/aria-query/4.2.1: dev: true resolution: @@ -1805,6 +1833,10 @@ packages: dev: true resolution: integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + /argparse/2.0.1: + dev: false + resolution: + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== /aria-query/4.2.2: dependencies: '@babel/runtime': 7.12.5 @@ -3013,6 +3045,12 @@ packages: dev: true resolution: integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + /html-parse-stringify2/2.0.1: + dependencies: + void-elements: 2.0.1 + dev: false + resolution: + integrity: sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= /http-signature/1.2.0: dependencies: assert-plus: 1.0.0 @@ -3036,6 +3074,12 @@ packages: node: '>=10.17.0' resolution: integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + /i18next/19.8.7: + dependencies: + '@babel/runtime': 7.12.13 + dev: false + resolution: + integrity: sha512-ezo1gb7QO4OQ5gQCdZMUxopwQSoqpRp6whdEjm1grxMSmkGj1NJ+kYS0UQd4NnpPIVqsgqTQ2L2eqSQYQ+U3Fw== /iconv-lite/0.4.24: dependencies: safer-buffer: 2.1.2 @@ -3770,6 +3814,13 @@ packages: hasBin: true resolution: integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + /js-yaml/4.0.0: + dependencies: + argparse: 2.0.1 + dev: false + hasBin: true + resolution: + integrity: sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== /jsbn/0.1.1: dev: true resolution: @@ -4483,6 +4534,18 @@ packages: react: 17.0.1 resolution: integrity: sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug== + /react-i18next/11.8.5_i18next@19.8.7+react@17.0.1: + dependencies: + '@babel/runtime': 7.12.13 + html-parse-stringify2: 2.0.1 + i18next: 19.8.7 + react: 17.0.1 + dev: false + peerDependencies: + i18next: '>= 19.0.0' + react: '>= 16.8.0' + resolution: + integrity: sha512-2jY/8NkhNv2KWBnZuhHxTn13aMxAbvhiDUNskm+1xVVnrPId78l8fA7fCyVeO3XU1kptM0t4MtvxV1Nu08cjLw== /react-is/17.0.1: dev: true resolution: @@ -4553,7 +4616,6 @@ packages: resolution: integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== /regenerator-runtime/0.13.7: - dev: true resolution: integrity: sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== /regenerator-transform/0.14.5: @@ -5392,6 +5454,12 @@ packages: '0': node >=0.6.0 resolution: integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + /void-elements/2.0.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= /w3c-hr-time/1.0.2: dependencies: browser-process-hrtime: 1.0.0 @@ -5566,21 +5634,26 @@ packages: integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== specifiers: '@snowpack/app-scripts-react': ^2.0.0 + '@snowpack/plugin-build-script': ^2.1.0 '@snowpack/plugin-dotenv': ^2.0.5 '@snowpack/plugin-react-refresh': ^2.4.0 '@snowpack/plugin-sass': ^1.3.0 '@snowpack/plugin-typescript': ^1.2.0 '@testing-library/jest-dom': ^5.11.9 '@testing-library/react': ^11.0.0 + '@testing-library/user-event': ^12.6.3 '@types/jest': ^26.0.20 '@types/react': ^17.0.0 '@types/react-dom': ^17.0.0 '@types/snowpack-env': ^2.3.2 + i18next: ^19.8.7 jest-cli: ^26.6.3 jest-each: ^26.6.2 + js-yaml: ^4.0.0 prettier: ^2.0.5 react: ^17.0.0 react-dom: ^17.0.0 + react-i18next: ^11.8.5 rxjs: ^6.6.3 sass: ^1.32.6 snowpack: ^3.0.11 diff --git a/src/App.tsx b/src/App.tsx index da61c30..9754928 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,22 +1,17 @@ -import React, { useState, useEffect } from 'react'; -import PotentialSolutionList, { PotentialSolutionListProps } from './components/potential-solution-list/PotentialSolutionList'; +import React from 'react'; +import { useTranslation } from "react-i18next"; import './App.css'; -import Spinner from './components/spinner/Spinner'; +import LocaleSwitcher from "./components/locale-switcher/LocaleSwitcher" interface AppProps { } function App({ }: AppProps) { - - const solution = { link: "http://example.com", description: "What is your bankaccountnumer?" }; - const givenSolutions: PotentialSolutionListProps = { - intro: "Here are some of the most common examples of administrative issues:", - solutions: [solution] - }; + const { i18n, t } = useTranslation(); return ( <> - <PotentialSolutionList {...givenSolutions} /> - <Spinner big={false} loading={true}/> + <LocaleSwitcher /> + <header><img src="" /><h1>{t("title")}</h1><p>{t("subtitle")}</p></header> </> ); } diff --git a/src/components/locale-switcher/LocaleSwitcher.test.tsx b/src/components/locale-switcher/LocaleSwitcher.test.tsx new file mode 100644 index 0000000..065db32 --- /dev/null +++ b/src/components/locale-switcher/LocaleSwitcher.test.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { screen, render, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import LocaleSwitcher from './LocaleSwitcher'; + +describe('<LocaleSwitcher>', () => { + it('renders a list of languages', () => { + const { getByText, } = render(<LocaleSwitcher />); + const select = screen.getByRole("combobox"); + expect(select).toHaveValue("en"); + userEvent.selectOptions(select, ["nl"]) + expect(select).toHaveValue("nl"); + }); +}); \ No newline at end of file diff --git a/src/components/locale-switcher/LocaleSwitcher.tsx b/src/components/locale-switcher/LocaleSwitcher.tsx new file mode 100644 index 0000000..b4247b6 --- /dev/null +++ b/src/components/locale-switcher/LocaleSwitcher.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +function LocaleSwitcher() { + const { i18n } = useTranslation(); + return ( + <select value={i18n.language} onChange={e => i18n.changeLanguage(e.target.value)}> + <option value="en">English</option> + <option value="nl">Nederlands</option> + </select> + + ); +} +export default LocaleSwitcher; \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index fd82a6d..25ec269 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; +import "./services/i18n"; ReactDOM.render( <React.StrictMode> diff --git a/src/services/i18n.ts b/src/services/i18n.ts new file mode 100644 index 0000000..e7a2943 --- /dev/null +++ b/src/services/i18n.ts @@ -0,0 +1,33 @@ +import i18next, { Resource, ResourceKey } from "i18next"; +import { initReactI18next } from "react-i18next"; + +const resources: Resource = { + en: { + translation: { + title: "Report a problem", + subtitle: "Let's troubleshoot together. What type of issue are you experiencing?", + selectProblemGroup: "Select an issue type..", + selectProblem: "Select issue.." + }, + }, + nl: { + translation: { + title: "Meld een probleem", + subtitle: "Laten we samen uitzoeken wat er aan de hand is. Wat voor soort probleem ervaar je?", + selectProblemGroup: "Selecteer een type issue..", + selectProblem: "Selecteer issue.." + }, + }, +}; + +i18next + .use(initReactI18next) + .init({ + resources, + lng: "en", + interpolation: { + escapeValue: false, + }, + }); + +export default i18next; \ No newline at end of file -- GitLab