matrix-art

An image gallery for Matrix
git clone git://archive.git.mtrnord.blog/MTRNord/matrix-art.git
Log | Files | Refs | README | LICENSE

commit af3e36c5dfb002d13543738a31f58443946bf68d
parent 5401431c5385a1b340bce3d534077ee15e809371
Author: MTRNord <mtrnord1@gmail.com>
Date:   Wed,  4 Jan 2023 20:58:10 +0100

readd translations

Diffstat:
Mpackage-lock.json | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackage.json | 4++++
Apublic/locales/en/translation.json | 14++++++++++++++
Msrc/app.tsx | 49+++++++++++++++++++------------------------------
Msrc/components/header.tsx | 9++++++---
Asrc/i18n.ts | 33+++++++++++++++++++++++++++++++++
Msrc/main.tsx | 2++
Msrc/pages/Home.tsx | 5++++-
Msrc/pages/Join.tsx | 18++++++++++--------
Msrc/utils/asyncImages.tsx | 2--
10 files changed, 298 insertions(+), 44 deletions(-)

diff --git a/package-lock.json b/package-lock.json @@ -11,11 +11,15 @@ "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz", "buffer": "^6.0.3", "events": "^3.3.0", + "i18next": "^22.4.8", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.1.1", "inquirer": "^9.1.4", "matrix-events-sdk": "^0.0.1", "matrix-js-sdk": "^23.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.1.1", "react-plock": "github:MTRNord/react-plock#MTRNord/patch-react", "react-router-dom": "^6.6.1", "react-spinners": "^0.13.7" @@ -1883,6 +1887,14 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2742,6 +2754,52 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "22.4.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.8.tgz", + "integrity": "sha512-XSOy17ZWqflOiJRYE/dzv6vDle2Se32dnHREHb93UnZzZ1+UnvQ8yKtt1fpNL3zvXz5AwCqqixrtTVZmRetaiQ==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.20.6" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "dependencies": { + "@babel/runtime": "^7.19.4" + } + }, + "node_modules/i18next-http-backend": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz", + "integrity": "sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==", + "dependencies": { + "cross-fetch": "3.1.5" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3323,6 +3381,25 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", @@ -3862,6 +3939,27 @@ "react": "^18.2.0" } }, + "node_modules/react-i18next": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", + "integrity": "sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==", + "dependencies": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "18.1.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", @@ -4647,6 +4745,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/tslib": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", @@ -4831,6 +4934,14 @@ "ejs": "^3.1.8" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -4839,6 +4950,20 @@ "defaults": "^1.0.3" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6095,6 +6220,14 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz", "integrity": "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==" }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6749,6 +6882,38 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "requires": { + "void-elements": "3.1.0" + } + }, + "i18next": { + "version": "22.4.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.8.tgz", + "integrity": "sha512-XSOy17ZWqflOiJRYE/dzv6vDle2Se32dnHREHb93UnZzZ1+UnvQ8yKtt1fpNL3zvXz5AwCqqixrtTVZmRetaiQ==", + "requires": { + "@babel/runtime": "^7.20.6" + } + }, + "i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "requires": { + "@babel/runtime": "^7.19.4" + } + }, + "i18next-http-backend": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz", + "integrity": "sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==", + "requires": { + "cross-fetch": "3.1.5" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -7162,6 +7327,14 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-releases": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", @@ -7504,6 +7677,15 @@ "scheduler": "^0.23.0" } }, + "react-i18next": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", + "integrity": "sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==", + "requires": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + } + }, "react-is": { "version": "18.1.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", @@ -8063,6 +8245,11 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "tslib": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", @@ -8172,6 +8359,11 @@ "ejs": "^3.1.8" } }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -8180,6 +8372,20 @@ "defaults": "^1.0.3" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json @@ -13,11 +13,15 @@ "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz", "buffer": "^6.0.3", "events": "^3.3.0", + "i18next": "^22.4.8", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.1.1", "inquirer": "^9.1.4", "matrix-events-sdk": "^0.0.1", "matrix-js-sdk": "^23.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.1.1", "react-plock": "github:MTRNord/react-plock#MTRNord/patch-react", "react-router-dom": "^6.6.1", "react-spinners": "^0.13.7" diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json @@ -0,0 +1,13 @@ +{ + "Loading...": "Loading...", + "Login": "Login", + "Register": "Register", + "Search": "Search", + "Join": "Join", + "Post": "Post", + "Homeserver": "Homeserver", + "Username": "Username", + "Password": "Password", + "Create account": "Create account", + "Explore": "Explore" +} +\ No newline at end of file diff --git a/src/app.tsx b/src/app.tsx @@ -10,6 +10,7 @@ import olmWasmPath from "@matrix-org/olm/olm.wasm?url"; import OlmLegacy from '@matrix-org/olm/olm_legacy.js?url'; import Olm from '@matrix-org/olm'; import { Route, Routes } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; const Join = lazy(() => import("./pages/Join")); const Post = lazy(() => import("./pages/Post")); @@ -76,48 +77,36 @@ export function App() { <Routes> <Route path="/" element={<Home />} /> <Route path="join" element={ - <Suspense fallback={ - <div className="flex flex-col"> - <header> - <Header /> - </header> - <main className="m-12 mt-6 flex items-center justify-center"> - <p className="text-lg text-data font-bold">Loading...</p> - </main> - </div> - }> + <Suspense fallback={<LoadingPage />}> <Join /> </Suspense> } /> <Route path="/post/:postId" element={ - <Suspense fallback={ - <div className="flex flex-col"> - <header> - <Header /> - </header> - <main className="m-12 mt-6 flex items-center justify-center"> - <p className="text-lg text-data font-bold">Loading...</p> - </main> - </div> - }> + <Suspense fallback={<LoadingPage />}> <Post /> </Suspense> } /> <Route path="/profile/:userId" element={ - <Suspense fallback={ - <div className="flex flex-col"> - <header> - <Header /> - </header> - <main className="m-12 mt-6 flex items-center justify-center"> - <p className="text-lg text-data font-bold">Loading...</p> - </main> - </div> - }> + <Suspense fallback={<LoadingPage />}> <Profile /> </Suspense> } /> </Routes> </Client.Provider > ); +} + +function LoadingPage() { + const { t } = useTranslation(); + + return ( + <div className="flex flex-col"> + <header> + <Header /> + </header> + <main className="m-12 mt-6 flex items-center justify-center"> + <p className="text-lg text-data font-bold">{t('Loading...')}</p> + </main> + </div> + ); } \ No newline at end of file diff --git a/src/components/header.tsx b/src/components/header.tsx @@ -2,9 +2,12 @@ import { Link } from "react-router-dom"; import { useContext } from "react"; import { Client } from "../context"; import logo_url from "./Logo_colored.svg"; +import { useTranslation } from "react-i18next"; export function Header() { const client = useContext(Client); + const { t } = useTranslation(); + return ( <div className="m-12 flex flex-col lg:flex-row items-center justify-between"> <Link to="/"><img alt="Matrix Art" src={logo_url} /></Link> @@ -16,12 +19,12 @@ export function Header() { <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" /> </svg> </div> - <input className="search-bg shadow rounded-2xl border-0 py-3 px-4 pl-12 text-data" placeholder="Search"></input> + <input className="search-bg shadow rounded-2xl border-0 py-3 px-4 pl-12 text-data" placeholder={t("Search") as string}></input> </div> { - !client?.isLoggedIn() ? <Link to="/join" className="text-white font-bold text-1xl logo-bg rounded-2xl py-3 px-12 shadow mt-4 sm:mt-0 transform transition-transform ease-in-out duration-300 hover:scale-105">Join</Link> : - <Link to="/" className="text-white font-bold text-1xl logo-bg rounded-2xl py-3 px-12 shadow mt-4 sm:mt-0 transform transition-transform ease-in-out duration-300 hover:scale-105">Post</Link> + !client?.isLoggedIn() ? <Link to="/join" className="text-white font-bold text-1xl logo-bg rounded-2xl py-3 px-12 shadow mt-4 sm:mt-0 transform transition-transform ease-in-out duration-300 hover:scale-105">{t('Join')}</Link> : + <Link to="/" className="text-white font-bold text-1xl logo-bg rounded-2xl py-3 px-12 shadow mt-4 sm:mt-0 transform transition-transform ease-in-out duration-300 hover:scale-105">{t('Post')}</Link> } </div> diff --git a/src/i18n.ts b/src/i18n.ts @@ -0,0 +1,32 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +import Backend from 'i18next-http-backend'; +import LanguageDetector from 'i18next-browser-languagedetector'; +// don't want to use this? +// have a look at the Quick start guide +// for passing in lng and translations on init + +i18n + // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales) + // learn more: https://github.com/i18next/i18next-http-backend + // want your translations to be loaded from a professional CDN? => https://github.com/locize/react-tutorial#step-2---use-the-locize-cdn + .use(Backend) + // detect user language + // learn more: https://github.com/i18next/i18next-browser-languageDetector + .use(LanguageDetector) + // pass the i18n instance to react-i18next. + .use(initReactI18next) + // init i18next + // for all options read: https://www.i18next.com/overview/configuration-options + .init({ + fallbackLng: 'en', + debug: true, + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + } + }); + + +export default i18n; +\ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx @@ -3,6 +3,8 @@ import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import { App } from './app'; import './index.css'; +import './i18n'; + ReactDOM.createRoot(document.querySelector('#app') as HTMLElement).render( <React.StrictMode> diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx @@ -3,6 +3,7 @@ import { Post } from "../components/post"; import { Plock } from "react-plock"; import { UserData } from "../data/user"; import { PostData } from "../data/post"; +import { useTranslation } from "react-i18next"; const BREAKPOINTS = [ @@ -13,13 +14,15 @@ const BREAKPOINTS = [ ]; export function Home() { + const { t } = useTranslation(); + return ( <div className="flex flex-col"> <header> <Header /> </header> <main className="m-12 mt-6"> - <h1 className="text-3xl font-bold mb-4 text-white">Explore</h1> + <h1 className="text-3xl font-bold mb-4 text-white">{t('Explore')}</h1> <div className="flex justify-center" id="gallery"> <Plock gap={"24px"} breakpoints={BREAKPOINTS}> {/* diff --git a/src/pages/Join.tsx b/src/pages/Join.tsx @@ -2,6 +2,7 @@ import React, { useState, useContext } from "react"; import { Client } from "../context"; import { Header } from "../components/header"; import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; const ACTIVE_TAB_CSS = "inline-block p-4 w-full text-gray-900 bg-gray-100 hover:ring-blue-300 hover:ring-1 focus:ring-transparent focus:outline-none dark:bg-gray-700 dark:text-white"; const TAB_CSS = "inline-block p-4 w-full bg-white hover:text-gray-700 hover:bg-gray-50 hover:ring-blue-300 hover:ring-1 focus:ring-transparent focus:outline-none dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700"; @@ -14,6 +15,7 @@ export default function Join() { const [password, setPassword] = useState(""); const [createProfile, setCreateProfile] = useState(true); const [currentTab, setCurrentTab] = useState("login"); + const { t } = useTranslation(); const onSubmit = async (e: React.FormEvent) => { if (!client) { @@ -60,26 +62,26 @@ export default function Join() { </header> <main className="m-12 mt-6"> - <h1 className="text-3xl font-bold mb-4 text-white">Join</h1> + <h1 className="text-3xl font-bold mb-4 text-white">{t('Join')}</h1> <div className="flex items-center flex-col"> <ul className="w-1/4 mb-8 hidden text-sm font-medium text-center text-gray-500 rounded-lg divide-x divide-gray-200 shadow sm:flex dark:divide-gray-700 dark:text-gray-400"> <li className="w-full"> - <a href="#" className={currentTab === "login" ? `${ACTIVE_TAB_CSS} rounded-l-lg` : `${TAB_CSS} rounded-l-lg`} onClick={(e) => { setCurrentTab("login"); }}>Login</a> + <a href="#" className={currentTab === "login" ? `${ACTIVE_TAB_CSS} rounded-l-lg` : `${TAB_CSS} rounded-l-lg`} onClick={(e) => { setCurrentTab("login"); }}>{t('Login')}</a> </li> <li className="w-full"> - <a href="#" className={currentTab === "register" ? `${ACTIVE_TAB_CSS} rounded-r-lg` : `${TAB_CSS} rounded-r-lg`} onClick={(e) => { setCurrentTab("register"); }}>Register</a> + <a href="#" className={currentTab === "register" ? `${ACTIVE_TAB_CSS} rounded-r-lg` : `${TAB_CSS} rounded-r-lg`} onClick={(e) => { setCurrentTab("register"); }}>{t('Register')}</a> </li> </ul> <form onSubmit={onSubmit} className="flex flex-col items-start w-1/4"> - <input className="search-bg shadow rounded-lg border-0 py-3 px-4 placeholder-data text-white mb-4 w-full" type="url" placeholder="Homeserver" name="homeserver" value={homeserver} onInput={onInput} /> - <input className="search-bg shadow rounded-lg border-0 py-3 px-4 placeholder-data text-white mb-4 w-full" autoComplete="username" type="text" placeholder="Username" name="username" value={username} onInput={onInput} /> - <input className="search-bg shadow rounded-lg border-0 py-3 px-4 placeholder-data text-white mb-4 w-full" autoComplete="current-password" type="password" placeholder="Password" name="password" value={password} onInput={onInput} /> + <input className="search-bg shadow rounded-lg border-0 py-3 px-4 placeholder-data text-white mb-4 w-full" type="url" placeholder={t("Homeserver") as string} name="homeserver" value={homeserver} onInput={onInput} /> + <input className="search-bg shadow rounded-lg border-0 py-3 px-4 placeholder-data text-white mb-4 w-full" autoComplete="username" type="text" placeholder={t("Username") as string} name="username" value={username} onInput={onInput} /> + <input className="search-bg shadow rounded-lg border-0 py-3 px-4 placeholder-data text-white mb-4 w-full" autoComplete="current-password" type="password" placeholder={t("Password") as string} name="password" value={password} onInput={onInput} /> <label className="mb-4 flex items-center"> <input className="checked:bg-blue-600 checked:border-transparent checked:bg-no-repeat checked:bg-[url('/tick.svg')] outline-none focus:outline-offset-2 focus:ring-blue-600 focus:ring-offset-2 focus:ring-offset-white p-0 inline-block align-middle box-border select-none shrink-0 shadow h-5 w-5 appearance-none rounded bg-white border-none" type="checkbox" name="createProfile" checked={createProfile} onChange={onInput} /> - <span className="ml-4 text-data text-base font-medium">Create account</span> + <span className="ml-4 text-data text-base font-medium">{t('Create account')}</span> </label> - <button type="submit" className="text-white font-bold text-lg logo-bg rounded-xl py-2 px-10 shadow transform transition-transform ease-in-out duration-300 hover:scale-105 w-1/2 self-center justify-center flex">{currentTab === "login" ? "Login" : "Register"}</button> + <button type="submit" className="text-white font-bold text-lg logo-bg rounded-xl py-2 px-10 shadow transform transition-transform ease-in-out duration-300 hover:scale-105 w-1/2 self-center justify-center flex">{currentTab === "login" ? t("Login") : t("Register")}</button> </form> </div> </main > diff --git a/src/utils/asyncImages.tsx b/src/utils/asyncImages.tsx @@ -1,6 +1,4 @@ // First we need a type of cache to avoid creating resources for images - -import React from "react"; import { ImgHTMLAttributes } from "react"; import { createResource, Resource } from "./resources";