From 0e98ea0b3246ac79b5a92798b1ca11ae6204bbff Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 1 Apr 2021 15:54:01 +0000 Subject: [PATCH] Merge branch 'i18n' into 'master' i18n support for event, post and job ads See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!51 (cherry picked from commit 9e7fe73c041a5a99f7bdc98c9a5d3d1e4473bbd4) 39104f70 install localisation library 7ce9f384 translate event page contents 9bec3f1e translate calendars feaaa981 translate feed page 1f7d842b fix news button translation ac0d33ef fix e2e tests 4eeb798d add getStaticProps to use static optimization --- next.config.js | 3 + package-lock.json | 389 ++++++++++++++++++ package.json | 1 + public/locales/en/common.json | 51 +++ public/locales/fi/common.json | 3 + src/components/Card.tsx | 42 +- src/components/ChangeLanguageButton.tsx | 26 ++ src/components/Icon.tsx | 12 +- src/components/index.tsx | 1 + src/i18n.ts | 16 + src/pages/404.tsx | 4 +- src/pages/_app.tsx | 3 +- src/pages/admin/events/index.tsx | 4 +- src/pages/admin/feed/index.tsx | 4 +- src/pages/admin/index.tsx | 4 +- src/pages/admin/jobads/index.tsx | 4 +- src/pages/admin/login.tsx | 4 +- src/pages/admin/logout.tsx | 4 +- src/pages/admin/signups/index.tsx | 4 +- src/pages/kilta/fuksi.tsx | 4 +- src/pages/kilta/index.tsx | 4 +- src/pages/kilta/kunnia.tsx | 4 +- src/pages/opinnot_ja_ura.tsx | 4 +- src/pages/yhteystiedot.tsx | 4 +- src/views/ActualPage/ActualPageView.tsx | 10 +- src/views/ActualPage/EventCalendar.tsx | 36 +- src/views/ActualPage/News.tsx | 33 +- src/views/EventPage/EventPageView.tsx | 105 +++-- src/views/FeedPage/FeedPageView.tsx | 19 +- src/views/FrontPage/FrontPageView.tsx | 14 +- src/views/InEnglishPage/InEnglishPageView.tsx | 14 +- tests/testcafe/signupToEvent.test.ts | 2 +- 32 files changed, 721 insertions(+), 111 deletions(-) create mode 100644 public/locales/en/common.json create mode 100644 public/locales/fi/common.json create mode 100644 src/components/ChangeLanguageButton.tsx create mode 100644 src/i18n.ts diff --git a/next.config.js b/next.config.js index 67cb2c3..9501ea6 100644 --- a/next.config.js +++ b/next.config.js @@ -4,6 +4,9 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({ module.exports = withBundleAnalyzer({ target: "server", + publicRuntimeConfig: { + localeSubpaths: {}, + }, images: { domains: [ "sahkoinsinoorikilta.fi", diff --git a/package-lock.json b/package-lock.json index 980895c..2677e11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "js-cookie": "2.2.1", "lodash": "4.17.21", "next": "10.1.2", + "next-i18next": "7.0.1", "normalize.css": "8.0.1", "react": "17.0.1", "react-beautiful-dnd": "13.1.0", @@ -1887,6 +1888,23 @@ "postcss-syntax": ">=0.36.2" } }, + "node_modules/@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/error-stack-parser": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", @@ -1899,6 +1917,27 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, + "node_modules/@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -1950,6 +1989,11 @@ "@types/unist": "*" } }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, "node_modules/@types/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", @@ -1984,6 +2028,16 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, + "node_modules/@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, "node_modules/@types/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.2.tgz", @@ -2041,6 +2095,15 @@ "redux": "^4.0.0" } }, + "node_modules/@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/@types/shortid": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz", @@ -6343,6 +6406,14 @@ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, + "node_modules/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=", + "dependencies": { + "void-elements": "^2.0.1" + } + }, "node_modules/html-tags": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", @@ -6443,6 +6514,40 @@ "node": ">= 10" } }, + "node_modules/i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-5.0.1.tgz", + "integrity": "sha512-7K4A6DJ2rNz3Yd835Y493UgkzUxgpGsCeIMKLGkt6Ps0cbgSaJ+LdATFNFA+ujp2brmsUM9BeDThXKhabXUbUw==", + "dependencies": { + "@babel/runtime": "^7.5.5" + } + }, + "node_modules/i18next-fs-backend": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz", + "integrity": "sha512-RFkfy10hNxJqc7MVAp5iAZq0Tum6msBCNebEe3OelOBvrROvzHUPaR8Qe10RQrOGokTm0W4vJGEJzruFkEt+hQ==" + }, + "node_modules/i18next-http-backend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.2.1.tgz", + "integrity": "sha512-9L2sa+wybqi57/+VsrJPo5DCI7WLoudaK12xz0okYSmsi3Izyj7sCgrYL52WHy/O3BFY9HGORBwSwlD1WYuH6Q==", + "dependencies": { + "node-fetch": "2.6.1" + } + }, + "node_modules/i18next-http-middleware": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.0.tgz", + "integrity": "sha512-65rP8bi5b7znBzfgIUy0KE00SWg1X6mL5XEkassgTrjAeLSfSb4vQ2bs9cN3qwHCynKIpmHjmNDu5c8NylTVmw==" + }, "node_modules/iconv-lite": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", @@ -8162,6 +8267,46 @@ } } }, + "node_modules/next-i18next": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/next-i18next/-/next-i18next-7.0.1.tgz", + "integrity": "sha512-x97lSfB11q/5Flf1Ocz02WoXeIRuVi7oJQ+8p9kNSfoIJExz1nTLPpJmWr2SF8wDxaljzL/kjlETUOyptGhSjQ==", + "dependencies": { + "@types/express": "^4.16.1", + "core-js": "^3", + "hoist-non-react-statics": "^3.2.0", + "i18next": "^19.6.3", + "i18next-browser-languagedetector": "^5.0.0", + "i18next-fs-backend": "^1.0.7", + "i18next-http-backend": "^1.0.17", + "i18next-http-middleware": "^3.0.2", + "path-match": "^1.2.4", + "prop-types": "^15.6.2", + "react-i18next": "^11.7.3", + "url": "^0.11.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/isaachinman" + }, + "peerDependencies": { + "next": ">= 9.5.0", + "react": ">= 16.8.0" + } + }, + "node_modules/next-i18next/node_modules/core-js": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.10.0.tgz", + "integrity": "sha512-MQx/7TLgmmDVamSyfE+O+5BHvG1aUGj/gHhLn1wVtm2B5u1eVIPvh7vkfjwWKNCjrTJB8+He99IntSQ1qP+vYQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/next-sitemap": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-1.6.18.tgz", @@ -9173,12 +9318,51 @@ "node": ">=8" } }, + "node_modules/path-match": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/path-match/-/path-match-1.2.4.tgz", + "integrity": "sha1-pidH88fgwlFHYml/JEQ1hbCRAOo=", + "dependencies": { + "http-errors": "~1.4.0", + "path-to-regexp": "^1.0.0" + } + }, + "node_modules/path-match/node_modules/http-errors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.4.0.tgz", + "integrity": "sha1-bAJC3qaz33r9oVPHEImzHG6Cqr8=", + "dependencies": { + "inherits": "2.0.1", + "statuses": ">= 1.2.1 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/path-match/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, "node_modules/path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -10268,6 +10452,19 @@ "react": "17.0.1" } }, + "node_modules/react-i18next": { + "version": "11.8.12", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.8.12.tgz", + "integrity": "sha512-M2PSVP9MzT/7yofXfCOF5gAVotinrM4BXWiguk8uFSznJsfFzTjrp3K9CBWcXitpoCBVZGZJ2AnbaWGSNkJqfw==", + "dependencies": { + "@babel/runtime": "^7.13.6", + "html-parse-stringify2": "^2.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -14074,6 +14271,14 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/watchpack": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", @@ -15944,6 +16149,23 @@ "unist-util-find-all-after": "^3.0.2" } }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "requires": { + "@types/node": "*" + } + }, "@types/error-stack-parser": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", @@ -15956,6 +16178,27 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, + "@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -16007,6 +16250,11 @@ "@types/unist": "*" } }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, "@types/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", @@ -16041,6 +16289,16 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, "@types/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.2.tgz", @@ -16098,6 +16356,15 @@ "redux": "^4.0.0" } }, + "@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "@types/shortid": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz", @@ -19446,6 +19713,14 @@ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, + "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-tags": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", @@ -19515,6 +19790,40 @@ "integrity": "sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg==", "dev": true }, + "i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", + "requires": { + "@babel/runtime": "^7.12.0" + } + }, + "i18next-browser-languagedetector": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-5.0.1.tgz", + "integrity": "sha512-7K4A6DJ2rNz3Yd835Y493UgkzUxgpGsCeIMKLGkt6Ps0cbgSaJ+LdATFNFA+ujp2brmsUM9BeDThXKhabXUbUw==", + "requires": { + "@babel/runtime": "^7.5.5" + } + }, + "i18next-fs-backend": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz", + "integrity": "sha512-RFkfy10hNxJqc7MVAp5iAZq0Tum6msBCNebEe3OelOBvrROvzHUPaR8Qe10RQrOGokTm0W4vJGEJzruFkEt+hQ==" + }, + "i18next-http-backend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.2.1.tgz", + "integrity": "sha512-9L2sa+wybqi57/+VsrJPo5DCI7WLoudaK12xz0okYSmsi3Izyj7sCgrYL52WHy/O3BFY9HGORBwSwlD1WYuH6Q==", + "requires": { + "node-fetch": "2.6.1" + } + }, + "i18next-http-middleware": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.0.tgz", + "integrity": "sha512-65rP8bi5b7znBzfgIUy0KE00SWg1X6mL5XEkassgTrjAeLSfSb4vQ2bs9cN3qwHCynKIpmHjmNDu5c8NylTVmw==" + }, "iconv-lite": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", @@ -20831,6 +21140,32 @@ } } }, + "next-i18next": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/next-i18next/-/next-i18next-7.0.1.tgz", + "integrity": "sha512-x97lSfB11q/5Flf1Ocz02WoXeIRuVi7oJQ+8p9kNSfoIJExz1nTLPpJmWr2SF8wDxaljzL/kjlETUOyptGhSjQ==", + "requires": { + "@types/express": "^4.16.1", + "core-js": "^3", + "hoist-non-react-statics": "^3.2.0", + "i18next": "^19.6.3", + "i18next-browser-languagedetector": "^5.0.0", + "i18next-fs-backend": "^1.0.7", + "i18next-http-backend": "^1.0.17", + "i18next-http-middleware": "^3.0.2", + "path-match": "^1.2.4", + "prop-types": "^15.6.2", + "react-i18next": "^11.7.3", + "url": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.10.0.tgz", + "integrity": "sha512-MQx/7TLgmmDVamSyfE+O+5BHvG1aUGj/gHhLn1wVtm2B5u1eVIPvh7vkfjwWKNCjrTJB8+He99IntSQ1qP+vYQ==" + } + } + }, "next-sitemap": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-1.6.18.tgz", @@ -21581,12 +21916,52 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-match": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/path-match/-/path-match-1.2.4.tgz", + "integrity": "sha1-pidH88fgwlFHYml/JEQ1hbCRAOo=", + "requires": { + "http-errors": "~1.4.0", + "path-to-regexp": "^1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.4.0.tgz", + "integrity": "sha1-bAJC3qaz33r9oVPHEImzHG6Cqr8=", + "requires": { + "inherits": "2.0.1", + "statuses": ">= 1.2.1 < 2" + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -22443,6 +22818,15 @@ "scheduler": "^0.20.1" } }, + "react-i18next": { + "version": "11.8.12", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.8.12.tgz", + "integrity": "sha512-M2PSVP9MzT/7yofXfCOF5gAVotinrM4BXWiguk8uFSznJsfFzTjrp3K9CBWcXitpoCBVZGZJ2AnbaWGSNkJqfw==", + "requires": { + "@babel/runtime": "^7.13.6", + "html-parse-stringify2": "^2.0.1" + } + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -25515,6 +25899,11 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, "watchpack": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", diff --git a/package.json b/package.json index da266de..cf34741 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "js-cookie": "2.2.1", "lodash": "4.17.21", "next": "10.1.2", + "next-i18next": "7.0.1", "normalize.css": "8.0.1", "react": "17.0.1", "react-beautiful-dnd": "13.1.0", diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 0000000..911b94a --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1,51 @@ +{ + "Lue lisää": "Read more", + "lngButton": "Suomeksi", + "Paikka": "Location", + "Alkaa": "Starts at", + "Päättyy": "Ends at", + "Lataa lisää": "Load more", + "Tapahtumat": "Events", + "Uutiset": "News", + + "Hakemaasi sivua": + "Page", + + "ei löydy": + "does not exist", + + "Hups, tapahtui virhe": "Oops, an error occured", + + "Lue lisää täältä": "More information here", + "Tärkeä tiedote!": "Important notice!", + + "Katso kaikki tapahtumat": + "All events", + + "Ilmoittaudu": + "Sign-up", + + "Ilmoittautuminen": + "Sign-up", + + "Peruuta": + "Cancel", + + "Kokonaishinta": + "Total price", + + "Ilmoittautuminen ei ole vielä auki!": + "Signup is not open yet!", + + "Se aukeaa": + "Signup opens at", + + "Ilmoittautumalla hyväksyn": + "By signing up I accept the", + + "tietosuojaselosteen": + "privacy policy", + + "ja tietojeni tallentamisen.": + "and storing of my data." +} \ No newline at end of file diff --git a/public/locales/fi/common.json b/public/locales/fi/common.json new file mode 100644 index 0000000..16b7f32 --- /dev/null +++ b/public/locales/fi/common.json @@ -0,0 +1,3 @@ +{ + "lngButton": "In English" +} \ No newline at end of file diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 4d90970..3b2c6ad 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -7,7 +7,7 @@ import breakpoints from "@theme/breakpoints"; interface WrappedCardProps { title: string; - start_time: string; + startTime: string; text: string; link: string; image?: { @@ -15,6 +15,7 @@ interface WrappedCardProps { alt: string; }; buttonOnClick?: () => void; + buttonText?: string; } const StyledCard = styled.article` @@ -69,36 +70,21 @@ const StyledCard = styled.article` `; const WrappedCard: React.FC = ({ - title, text, link, image, start_time, buttonOnClick, ...props -}) => { - const options: Intl.DateTimeFormatOptions = { - day: "numeric", - month: "numeric", - year: "numeric", - hour: "numeric", - minute: "2-digit", - }; - const datetime = new Date(start_time).toLocaleString("fi-FI", options); - - const button = ( + title, text, link, image, startTime, buttonOnClick, buttonText, ...props +}) => ( + + {image && ( + {image.alt} + )} +

{startTime}

+

{title}

+

{text}

- ); - - return ( - - {image && ( - {image.alt} - )} -

{datetime}

-

{title}

-

{text}

- {button} -
- ); -}; +
+); export default WrappedCard; diff --git a/src/components/ChangeLanguageButton.tsx b/src/components/ChangeLanguageButton.tsx new file mode 100644 index 0000000..55345b4 --- /dev/null +++ b/src/components/ChangeLanguageButton.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import styled from "styled-components"; +import i18nNext from "../i18n"; +import Icon, { IconType } from "./Icon"; + +const ChangeLanguageButton: React.FC = (props) => { + const { language, changeLanguage } = i18nNext.i18n; + return ( + + ); +}; + +export default styled(ChangeLanguageButton)` + font-size: 4rem; + background: none; + border: none; + width: fit-content; +`; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 1efc431..8290aea 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -69,10 +69,18 @@ const nameToIcon = (name: IconType): JSX.Element | string => { ); } if (name === IconType.FinlandFlag) { - return "🇫🇮"; + return ( + + 🇫🇮 + + ); } if (name === IconType.GBFlag) { - return "🇬🇧"; + return ( + + 🇬🇧 + + ); } return null; }; diff --git a/src/components/index.tsx b/src/components/index.tsx index c9c36fb..3ca5862 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -10,3 +10,4 @@ export { default as InfoBox } from "./InfoBox"; export { default as Accordion } from "./Accordion/Accordion"; export { default as Link } from "./Link"; export { default as CrossFadeImages } from "./CrossFadeImages"; +export { default as ChangeLanguageButton } from "./ChangeLanguageButton"; diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..f98fac4 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,16 @@ +import NextI18Next from "next-i18next"; + +import Config from "next/config"; +import path from "path"; + +const NextI18NextInstance = new NextI18Next({ + defaultLanguage: "fi", + defaultNS: "common", + localeSubpaths: Config().publicRuntimeConfig.localeSubpaths, + localePath: path.resolve("./public/locales"), + otherLanguages: ["en"], +}); + +export const { appWithTranslation, useTranslation } = NextI18NextInstance; + +export default NextI18NextInstance; diff --git a/src/pages/404.tsx b/src/pages/404.tsx index c58c5ed..38f59af 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import styled from "styled-components"; @@ -25,4 +25,6 @@ const NotFoundPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default NotFoundPage; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index f182d7a..924cb10 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,6 +5,7 @@ import Head from "next/head"; import styled, { createGlobalStyle } from "styled-components"; import { ToastContainer } from "react-toastify"; import { colors } from "@theme/colors"; +import { appWithTranslation } from "../i18n"; import "react-mde/lib/styles/css/react-mde-all.css"; import "react-toastify/dist/ReactToastify.css"; @@ -158,4 +159,4 @@ const Web20App = ({ Component, pageProps }: AppProps) => ( // return { ...appProps } // } -export default Web20App; +export default appWithTranslation(Web20App); diff --git a/src/pages/admin/events/index.tsx b/src/pages/admin/events/index.tsx index edfc3f4..c261008 100644 --- a/src/pages/admin/events/index.tsx +++ b/src/pages/admin/events/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import { formatRelative } from "date-fns"; import { toast } from "react-toastify"; import styled from "styled-components"; @@ -75,4 +75,6 @@ const AdminEventPage: NextPage = () => { ); }; +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminEventPage; diff --git a/src/pages/admin/feed/index.tsx b/src/pages/admin/feed/index.tsx index 793f27b..7137c94 100644 --- a/src/pages/admin/feed/index.tsx +++ b/src/pages/admin/feed/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import { formatRelative } from "date-fns"; import { toast } from "react-toastify"; import styled from "styled-components"; @@ -75,4 +75,6 @@ const AdminFeedPage: NextPage = () => { ); }; +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminFeedPage; diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx index cab83e2..331e6e2 100644 --- a/src/pages/admin/index.tsx +++ b/src/pages/admin/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import AdminPageWrapper from "@views/common/AdminPageWrapper"; @@ -16,4 +16,6 @@ const AdminFrontPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminFrontPage; diff --git a/src/pages/admin/jobads/index.tsx b/src/pages/admin/jobads/index.tsx index cc6e7fa..7705cc2 100644 --- a/src/pages/admin/jobads/index.tsx +++ b/src/pages/admin/jobads/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import { formatRelative } from "date-fns"; import { toast } from "react-toastify"; import styled from "styled-components"; @@ -79,4 +79,6 @@ const AdminJobAdPage: NextPage = () => { ); }; +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminJobAdPage; diff --git a/src/pages/admin/login.tsx b/src/pages/admin/login.tsx index a7f052b..cb81da3 100644 --- a/src/pages/admin/login.tsx +++ b/src/pages/admin/login.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import { useRouter } from "next/router"; import styled from "styled-components"; import { generateToken, setTokenCookie, isAuthenticated } from "@utils/auth"; @@ -80,4 +80,6 @@ const AdminLoginPage: NextPage = () => { ); }; +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminLoginPage; diff --git a/src/pages/admin/logout.tsx b/src/pages/admin/logout.tsx index 9c506ef..f2593e7 100644 --- a/src/pages/admin/logout.tsx +++ b/src/pages/admin/logout.tsx @@ -1,4 +1,4 @@ -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import { useRouter } from "next/router"; import { deleteTokenCookie } from "@utils/auth"; @@ -12,4 +12,6 @@ const AdminLogoutPage: NextPage = () => { return null; }; +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminLogoutPage; diff --git a/src/pages/admin/signups/index.tsx b/src/pages/admin/signups/index.tsx index 6c4b762..36123c0 100644 --- a/src/pages/admin/signups/index.tsx +++ b/src/pages/admin/signups/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import { formatRelative } from "date-fns"; import { toast } from "react-toastify"; import styled from "styled-components"; @@ -84,4 +84,6 @@ const AdminSignupPage: NextPage = () => { ); }; +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default AdminSignupPage; diff --git a/src/pages/kilta/fuksi.tsx b/src/pages/kilta/fuksi.tsx index f1b6b7a..ee5feda 100644 --- a/src/pages/kilta/fuksi.tsx +++ b/src/pages/kilta/fuksi.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import FreshmenPageView from "@views/FreshmenPage/FreshmenPageView"; import PageWrapper from "@views/common/PageWrapper"; @@ -15,4 +15,6 @@ const FreshmenPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default FreshmenPage; diff --git a/src/pages/kilta/index.tsx b/src/pages/kilta/index.tsx index 4d24616..401b9e3 100644 --- a/src/pages/kilta/index.tsx +++ b/src/pages/kilta/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import GuildPageView from "@views/GuildPage/GuildPageView"; import PageWrapper from "@views/common/PageWrapper"; @@ -15,4 +15,6 @@ const GuildPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default GuildPage; diff --git a/src/pages/kilta/kunnia.tsx b/src/pages/kilta/kunnia.tsx index ca416f8..be080ac 100644 --- a/src/pages/kilta/kunnia.tsx +++ b/src/pages/kilta/kunnia.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import HonoraryPageView from "@views/HonoraryPage/HonoraryPageView"; import PageWrapper from "@views/common/PageWrapper"; @@ -15,4 +15,6 @@ const HonoraryPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default HonoraryPage; diff --git a/src/pages/opinnot_ja_ura.tsx b/src/pages/opinnot_ja_ura.tsx index 0f448f9..cd08098 100644 --- a/src/pages/opinnot_ja_ura.tsx +++ b/src/pages/opinnot_ja_ura.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import StudiesPageView from "@views/StudiesPage/StudiesPageView"; import PageWrapper from "@views/common/PageWrapper"; @@ -15,4 +15,6 @@ const StudiesPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default StudiesPage; diff --git a/src/pages/yhteystiedot.tsx b/src/pages/yhteystiedot.tsx index 1492246..3d7bdd9 100644 --- a/src/pages/yhteystiedot.tsx +++ b/src/pages/yhteystiedot.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { NextPage } from "next"; +import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import ContactsPageView from "@views/ContactsPage/ContactsPageView"; import PageWrapper from "@views/common/PageWrapper"; @@ -15,4 +15,6 @@ const ContactsPage: NextPage = () => ( ); +export const getStaticProps: GetStaticProps = async () => ({ props: {} }); + export default ContactsPage; diff --git a/src/views/ActualPage/ActualPageView.tsx b/src/views/ActualPage/ActualPageView.tsx index b689c61..ca99f99 100644 --- a/src/views/ActualPage/ActualPageView.tsx +++ b/src/views/ActualPage/ActualPageView.tsx @@ -5,7 +5,7 @@ import Event from "@models/Event"; import Post from "@models/Feed"; import { - Divider, CTASection, TextSection, Link, CrossFadeImages, + Divider, CTASection, TextSection, Link, CrossFadeImages, ChangeLanguageButton, } from "@components/index"; import ActualPageHero from "./ActualPageHero"; import EventCalendar from "./EventCalendar"; @@ -32,14 +32,18 @@ const Gallery = styled.div` } `; +const LngButton = styled(ChangeLanguageButton)` + align-self: flex-end; + margin-right: 1rem; +`; + const ActualPageView: React.FC = ({ events, feed }) => ( <> + - - = ({ events }) => { // const [filterSelected, setFilter] = useState(0); const [numberShown, setNumberShown] = useState(8); - const filteredEvents = events.slice(0, numberShown); + + const { t } = useTranslation(); + const { language } = i18nNext.i18n; + const isFi = language === "fi"; + + const options: Intl.DateTimeFormatOptions = { + day: "numeric", + month: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", + }; + + const filteredEvents = events.slice(0, numberShown).map((e) => ({ + ...e, + title: isFi ? e.title_fi : e.title_en, + description: isFi ? e.description_fi : e.description_en, + content: isFi ? e.content_fi : e.content_en, + location: isFi ? e.location_fi : e.location_en, + startDate: new Date(e.start_time).toLocaleString(isFi ? "fi-FI" : "en-GB", options), + endDate: new Date(e.end_time).toLocaleString(isFi ? "fi-FI" : "en-GB", options), + })); + return (

- Tapahtumat + {t("Tapahtumat")} {/* )} diff --git a/src/views/ActualPage/News.tsx b/src/views/ActualPage/News.tsx index ea7f6f6..142790d 100644 --- a/src/views/ActualPage/News.tsx +++ b/src/views/ActualPage/News.tsx @@ -5,6 +5,7 @@ import Button from "@components/Button"; import { CardSection, Card, FullWidthSection } from "@components/index"; import noop from "@utils/noop"; import FilterContainer from "./FilterContainer"; +import i18nNext, { useTranslation } from "../../i18n"; interface NewsProps { feed: Post[]; @@ -13,11 +14,30 @@ interface NewsProps { const News: React.FC = ({ feed }) => { // const [filterSelected, setFilter] = useState(0); const [numberShown, setNumberShown] = useState(8); - const filteredFeed = feed.slice(0, numberShown); + + const { t } = useTranslation(); + const { language } = i18nNext.i18n; + const isFi = language === "fi"; + + const options: Intl.DateTimeFormatOptions = { + day: "numeric", + month: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", + }; + + const filteredFeed = feed.slice(0, numberShown).map((p) => ({ + ...p, + title: isFi ? p.title_fi : p.title_en, + description: isFi ? p.description_fi : p.description_en, + content: isFi ? p.content_fi : p.content_en, + publishTime: new Date(p.publish_time).toLocaleString(isFi ? "fi-FI" : "en-GB", options), + })); return (

- Uutiset + {t("Uutiset")} {/* )} diff --git a/src/views/EventPage/EventPageView.tsx b/src/views/EventPage/EventPageView.tsx index 29ebba9..853513b 100644 --- a/src/views/EventPage/EventPageView.tsx +++ b/src/views/EventPage/EventPageView.tsx @@ -4,11 +4,11 @@ import styled from "styled-components"; import colors from "@theme/colors"; import Event from "@models/Event"; import Button from "@components/Button"; -import { Link, TextSection } from "@components/index"; +import { Link, TextSection, ChangeLanguageButton } from "@components/index"; import noop from "@utils/noop"; - import MarkdownStyles from "@views/common/MarkdownStyles"; import LoadingView from "@views/common/LoadingView"; +import i18nNext, { useTranslation } from "../../i18n"; interface EventPageViewProps { event?: Event; @@ -33,7 +33,6 @@ const StyledTextSection = styled(TextSection)` line-height: 0.4rem; } } - `; const SignupButtons = styled.div` @@ -46,49 +45,69 @@ const Content = styled(MarkdownStyles)` margin-top: 1.5rem; `; +const LngButton = styled(ChangeLanguageButton)` + align-self: flex-end; + margin-right: 1rem; +`; + const EventPageView: React.FC = ({ event }) => { + const { t } = useTranslation(); if (!event) return ; - const date_start = new Date(event.start_time).toLocaleString("fi-FI"); - const date_end = new Date(event.end_time).toLocaleString("fi-FI"); + const { language } = i18nNext.i18n; + const isFi = language === "fi"; + const { + title, description, content, location, startDate, endDate, + } = { + title: isFi ? event.title_fi : event.title_en, + description: isFi ? event.description_fi : event.description_en, + content: isFi ? event.content_fi : event.content_en, + location: isFi ? event.location_fi : event.location_en, + startDate: new Date(event.start_time).toLocaleString(isFi ? "fi-FI" : "en-GB"), + endDate: new Date(event.end_time).toLocaleString(isFi ? "fi-FI" : "en-GB"), + }; + return ( - -

- {event.title_fi} -

- {event.description_fi} -

- {event.title_fi} -

-
- -

- Paikka: {event.location_fi} -

-

- -

-

- -

- {/* We may have multiple signup forms. Generate own Button for each one */} - - {event.signupForm.map((sf) => ( - - - - ))} - -
-
+ <> + + +

+ {title} +

+ {description} +

+ {title} +

+
+ +

+ {`${t("Paikka")}: ${location}`} +

+

+ +

+

+ +

+ {/* We may have multiple signup forms. Generate own Button for each one */} + + {event.signupForm.map((sf) => ( + + + + ))} + +
+
+ ); }; export default EventPageView; diff --git a/src/views/FeedPage/FeedPageView.tsx b/src/views/FeedPage/FeedPageView.tsx index b6805e6..b7c5cf7 100644 --- a/src/views/FeedPage/FeedPageView.tsx +++ b/src/views/FeedPage/FeedPageView.tsx @@ -6,6 +6,7 @@ import Post from "@models/Feed"; import { TextSection } from "@components/index"; import MarkdownStyles from "@views/common/MarkdownStyles"; import LoadingView from "@views/common/LoadingView"; +import i18nNext from "../../i18n"; interface FeedPageViewProps { post?: Post; @@ -32,17 +33,27 @@ const Content = styled(MarkdownStyles)` const FeedPageView: React.FC = ({ post }) => { if (!post) return ; + const { language } = i18nNext.i18n; + const isFi = language === "fi"; + const { + title, description, content, + } = { + title: isFi ? post.title_fi : post.title_en, + description: isFi ? post.description_fi : post.description_en, + content: isFi ? post.content_fi : post.content_en, + }; + return (

- {post.title_fi} + {title}

- {post.description_fi} + {description}

{post.image && ( {post.title_fi} = ({ post }) => { )}

- +
); diff --git a/src/views/FrontPage/FrontPageView.tsx b/src/views/FrontPage/FrontPageView.tsx index 400e968..f3a893b 100644 --- a/src/views/FrontPage/FrontPageView.tsx +++ b/src/views/FrontPage/FrontPageView.tsx @@ -32,6 +32,14 @@ interface FrontPageViewProps { feed: Post[]; } +const cardTimeOpts: Intl.DateTimeFormatOptions = { + day: "numeric", + month: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", +}; + const SponsorReel = styled.div` text-align: center; & > div { @@ -72,7 +80,7 @@ const FrontPageView: React.FC = ({ events, feed }) => ( = ({ events, feed }) => ( alt: event.title_fi, }} buttonOnClick={noop} + buttonText="Lue lisää ›" data-e2e="event-card" /> ))} @@ -104,10 +113,11 @@ const FrontPageView: React.FC = ({ events, feed }) => ( ))}