From 9f921bd8a6970347e88ca6d2d7c1d471c59f914e Mon Sep 17 00:00:00 2001 From: Dylan Van Nielen Date: Tue, 14 Jun 2022 18:15:48 +0930 Subject: [PATCH 01/21] Add translation example Only implemented to a few pages as an example, looking to get feedback. I do not know japanese, I just used google translate to translate a few words as an example - is most likely incorrect --- .gitignore | 1 + package-lock.json | 229 ++++++++++++++++-- package.json | 4 + public/locales/en/translation.json | 34 +++ public/locales/jp/translation.json | 9 + src/app/i18n.jsx | 23 ++ .../space-settings/SpaceSettings.jsx | 30 ++- src/app/organisms/view-source/ViewSource.jsx | 12 +- src/app/organisms/welcome/Welcome.jsx | 10 +- 9 files changed, 315 insertions(+), 37 deletions(-) create mode 100644 public/locales/en/translation.json create mode 100644 public/locales/jp/translation.json create mode 100644 src/app/i18n.jsx diff --git a/.gitignore b/.gitignore index 397d2434d..46a6a7f7f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules devAssets .DS_Store +.vscode \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7eedd84c7..a695ad723 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,9 @@ "flux": "^4.0.3", "formik": "^2.2.9", "html-react-parser": "^1.4.14", + "i18next": "^21.8.9", + "i18next-browser-languagedetector": "^6.1.4", + "i18next-http-backend": "^1.4.1", "katex": "^0.15.6", "linkifyjs": "^2.1.9", "matrix-js-sdk": "^18.0.0", @@ -37,6 +40,7 @@ "react-dnd-html5-backend": "^15.1.3", "react-dom": "^17.0.2", "react-google-recaptcha": "^2.1.0", + "react-i18next": "^11.17.1", "react-modal": "^3.15.1", "sanitize-html": "^2.7.0", "tippy.js": "^6.3.7", @@ -1694,9 +1698,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -4573,11 +4577,11 @@ } }, "node_modules/cross-fetch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", - "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", + "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.1" + "node-fetch": "2.6.7" } }, "node_modules/cross-spawn": { @@ -7290,6 +7294,11 @@ "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", "dev": true }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, "node_modules/html-loader": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.1.0.tgz", @@ -7331,6 +7340,14 @@ "node": ">=12" } }, + "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/html-react-parser": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-1.4.14.tgz", @@ -7475,6 +7492,44 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "21.8.9", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.8.9.tgz", + "integrity": "sha512-PY9a/8ADVmnju1tETeglbbVQi+nM5pcJQWm9kvKMTE3GPgHHtpDsHy5HQ/hccz2/xtW7j3vuso23JdQSH0EttA==", + "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.17.2" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.4.tgz", + "integrity": "sha512-wukWnFeU7rKIWT66VU5i8I+3Zc4wReGcuDK2+kuFhtoxBRGWGdvYI9UQmqNL/yQH1KogWwh+xGEaIPH8V/i2Zg==", + "dependencies": { + "@babel/runtime": "^7.14.6" + } + }, + "node_modules/i18next-http-backend": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.4.1.tgz", + "integrity": "sha512-s4Q9hK2jS29iyhniMP82z+yYY8riGTrWbnyvsSzi5TaF7Le4E7b5deTmtuaRuab9fdDcYXtcwdBgawZG+JCEjA==", + "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", @@ -9833,11 +9888,22 @@ "dev": true }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "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-forge": { @@ -11391,6 +11457,28 @@ "react": ">=16.4.1" } }, + "node_modules/react-i18next": { + "version": "11.17.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.17.1.tgz", + "integrity": "sha512-4H4fK9vWsQtPP0iAdqzGfdPKLaSXpCjuh1xaGsejX/CO8tx8zCnrOnlQhMgrJf+OlUfzth5YaDPXYGp3RHxV1g==", + "dependencies": { + "@babel/runtime": "^7.14.5", + "html-escaper": "^2.0.2", + "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": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -13079,6 +13167,11 @@ "node": ">=0.8" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -13446,6 +13539,14 @@ "node": ">= 0.10" } }, + "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/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -13476,6 +13577,11 @@ "minimalistic-assert": "^1.0.0" } }, + "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/webpack": { "version": "5.73.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz", @@ -13836,6 +13942,15 @@ "node": ">=0.8.0" } }, + "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", @@ -15131,9 +15246,9 @@ } }, "@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", "requires": { "regenerator-runtime": "^0.13.4" }, @@ -17511,11 +17626,11 @@ } }, "cross-fetch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", - "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", + "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.1" + "node-fetch": "2.6.7" } }, "cross-spawn": { @@ -19616,6 +19731,11 @@ "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", "dev": true }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, "html-loader": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.1.0.tgz", @@ -19641,6 +19761,14 @@ "terser": "^5.10.0" } }, + "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" + } + }, "html-react-parser": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-1.4.14.tgz", @@ -19741,6 +19869,30 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "i18next": { + "version": "21.8.9", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.8.9.tgz", + "integrity": "sha512-PY9a/8ADVmnju1tETeglbbVQi+nM5pcJQWm9kvKMTE3GPgHHtpDsHy5HQ/hccz2/xtW7j3vuso23JdQSH0EttA==", + "requires": { + "@babel/runtime": "^7.17.2" + } + }, + "i18next-browser-languagedetector": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.4.tgz", + "integrity": "sha512-wukWnFeU7rKIWT66VU5i8I+3Zc4wReGcuDK2+kuFhtoxBRGWGdvYI9UQmqNL/yQH1KogWwh+xGEaIPH8V/i2Zg==", + "requires": { + "@babel/runtime": "^7.14.6" + } + }, + "i18next-http-backend": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.4.1.tgz", + "integrity": "sha512-s4Q9hK2jS29iyhniMP82z+yYY8riGTrWbnyvsSzi5TaF7Le4E7b5deTmtuaRuab9fdDcYXtcwdBgawZG+JCEjA==", + "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", @@ -21445,9 +21597,12 @@ "dev": true }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "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-forge": { "version": "1.3.1", @@ -22568,6 +22723,16 @@ "react-async-script": "^1.1.1" } }, + "react-i18next": { + "version": "11.17.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.17.1.tgz", + "integrity": "sha512-4H4fK9vWsQtPP0iAdqzGfdPKLaSXpCjuh1xaGsejX/CO8tx8zCnrOnlQhMgrJf+OlUfzth5YaDPXYGp3RHxV1g==", + "requires": { + "@babel/runtime": "^7.14.5", + "html-escaper": "^2.0.2", + "html-parse-stringify": "^3.0.1" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -23859,6 +24024,11 @@ "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -24152,6 +24322,11 @@ "replace-ext": "^1.0.0" } }, + "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==" + }, "warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -24179,6 +24354,11 @@ "minimalistic-assert": "^1.0.0" } }, + "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==" + }, "webpack": { "version": "5.73.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz", @@ -24430,6 +24610,15 @@ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, + "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 index 89da1f6cf..cfca05bbd 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,9 @@ "flux": "^4.0.3", "formik": "^2.2.9", "html-react-parser": "^1.4.14", + "i18next": "^21.8.9", + "i18next-browser-languagedetector": "^6.1.4", + "i18next-http-backend": "^1.4.1", "katex": "^0.15.6", "linkifyjs": "^2.1.9", "matrix-js-sdk": "^18.0.0", @@ -43,6 +46,7 @@ "react-dnd-html5-backend": "^15.1.3", "react-dom": "^17.0.2", "react-google-recaptcha": "^2.1.0", + "react-i18next": "^11.17.1", "react-modal": "^3.15.1", "sanitize-html": "^2.7.0", "tippy.js": "^6.3.7", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json new file mode 100644 index 000000000..11cdca01c --- /dev/null +++ b/public/locales/en/translation.json @@ -0,0 +1,34 @@ +{ + "common" : { + "close": "Close", + "leave": "Leave", + "options": "Options" + }, + "welcome": { + "heading": "Welcome to Cinny!", + "subheading": "Yet another Matrix client" + }, + "view_source":{ + "title": "View Source", + "original_source": "Original source", + "decrypted_source": "Decrypted source" + }, + "space_settings":{ + "subtitle": "space settings", + "leave":{ + "leave_space": "Leave Space", + "leave_dialog_title": "Leave Space", + "leave_dialog_message": "Are you sure you want to leave {{space}}?" + }, + "visibility":{ + "header": "Space visibility (who can join)" + }, + "addresses": { + "header": "Space addresses" + }, + "categorize_subspaces": "Categorize subspaces", + "uncategorize_subspaces": "Uncategorize subspaces", + "pin_sidebar": "Pin to sidebar", + "unpin_sidebar": "Unpin from sidebar" + } +} \ No newline at end of file diff --git a/public/locales/jp/translation.json b/public/locales/jp/translation.json new file mode 100644 index 000000000..6191885d2 --- /dev/null +++ b/public/locales/jp/translation.json @@ -0,0 +1,9 @@ +{ + "common":{ + "close": "閉める", + "leave": "残す" + }, + "welcome":{ + "heading": "いらっしゃいませ" + } +} \ No newline at end of file diff --git a/src/app/i18n.jsx b/src/app/i18n.jsx new file mode 100644 index 000000000..d951279bd --- /dev/null +++ b/src/app/i18n.jsx @@ -0,0 +1,23 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import Backend from 'i18next-http-backend' + +i18n + .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({ + debug: true, + fallbackLng: 'en', + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + } + }); + +export default i18n; diff --git a/src/app/organisms/space-settings/SpaceSettings.jsx b/src/app/organisms/space-settings/SpaceSettings.jsx index 437359930..8ac561ee8 100644 --- a/src/app/organisms/space-settings/SpaceSettings.jsx +++ b/src/app/organisms/space-settings/SpaceSettings.jsx @@ -39,6 +39,10 @@ import CategoryFilledIC from '../../../../public/res/ic/filled/category.svg'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; import { useForceUpdate } from '../../hooks/useForceUpdate'; + +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + const tabText = { GENERAL: 'General', MEMBERS: 'Members', @@ -65,10 +69,12 @@ function GeneralSettings({ roomId }) { const roomName = initMatrix.matrixClient.getRoom(roomId)?.name; const [, forceUpdate] = useForceUpdate(); + const { t } = useTranslation(); + return ( <>
- Options + {t("common.options")} { if (isCategorized) unCategorizeSpace(roomId); @@ -77,7 +83,7 @@ function GeneralSettings({ roomId }) { }} iconSrc={isCategorized ? CategoryFilledIC : CategoryIC} > - {isCategorized ? 'Uncategorize subspaces' : 'Categorize subspaces'} + {isCategorized ? t("space_settings.uncategorize_subspaces") : t("space_settings.categorize_subspaces")} { @@ -87,30 +93,30 @@ function GeneralSettings({ roomId }) { }} iconSrc={isPinned ? PinFilledIC : PinIC} > - {isPinned ? 'Unpin from sidebar' : 'Pin to sidebar'} + {isPinned ? t("space_settings.unpin_sidebar") : t("space_settings.pin_sidebar")} { const isConfirmed = await confirmDialog( - 'Leave space', - `Are you sure that you want to leave "${roomName}" space?`, - 'Leave', + t("space_settings.leave.leave_dialog_title"), + t("space_settings.leave.leave_dialog_message", {space: roomName}), + t("space_settings.leave.leave_space"), 'danger', ); if (isConfirmed) leave(roomId); }} iconSrc={LeaveArrowIC} > - Leave + {t("space_settings.leave.leave_space")}
- Space visibility (who can join) + {t("space_settings.visibility.header")}
- Space addresses + {t("space_settings.addresses.header")}
@@ -154,6 +160,8 @@ function SpaceSettings() { setSelectedTab(tabItem); }; + const { t } = useTranslation(); + return ( {isOpen && twemojify(room.name)} - — space settings + — {t("space_settings.subtitle")} )} - contentOptions={} + contentOptions={} onRequestClose={requestClose} > {isOpen && ( diff --git a/src/app/organisms/view-source/ViewSource.jsx b/src/app/organisms/view-source/ViewSource.jsx index 9bd3334f5..d117d1673 100644 --- a/src/app/organisms/view-source/ViewSource.jsx +++ b/src/app/organisms/view-source/ViewSource.jsx @@ -12,6 +12,9 @@ import PopupWindow from '../../molecules/popup-window/PopupWindow'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function ViewSourceBlock({ title, json }) { return (
@@ -34,6 +37,7 @@ ViewSourceBlock.propTypes = { function ViewSource() { const [isOpen, setIsOpen] = useState(false); const [event, setEvent] = useState(null); + const { t } = useTranslation(); useEffect(() => { const loadViewSource = (e) => { @@ -52,18 +56,18 @@ function ViewSource() { const renderViewSource = () => (
- {event.isEncrypted() && } - + {event.isEncrypted() && } +
); return ( setIsOpen(false)} - contentOptions={ setIsOpen(false)} tooltip="Close" />} + contentOptions={ setIsOpen(false)} tooltip={t("common.close")} />} > {event ? renderViewSource() :
} diff --git a/src/app/organisms/welcome/Welcome.jsx b/src/app/organisms/welcome/Welcome.jsx index 6d135bee2..008345b77 100644 --- a/src/app/organisms/welcome/Welcome.jsx +++ b/src/app/organisms/welcome/Welcome.jsx @@ -5,13 +5,19 @@ import Text from '../../atoms/text/Text'; import CinnySvg from '../../../../public/res/svg/cinny.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function Welcome() { + + const { t, i18n } = useTranslation(); + return (
Cinny logo - Welcome to Cinny - Yet another matrix client + {t('welcome.heading')} + {t('welcome.subheading')}
); From 1ed9d6de782a5c5a96f73d3d27748ecf9efa12d6 Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:31:39 +0930 Subject: [PATCH 02/21] Added translation to Tabs Implemented the change to SpaceSettings as an example. --- public/locales/en/translation.json | 7 +++++++ public/locales/jp/translation.json | 7 +++++++ src/app/atoms/tabs/Tabs.jsx | 7 ++++++- src/app/organisms/space-settings/SpaceSettings.jsx | 9 ++++++--- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 11cdca01c..d2ee9a503 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -30,5 +30,12 @@ "uncategorize_subspaces": "Uncategorize subspaces", "pin_sidebar": "Pin to sidebar", "unpin_sidebar": "Unpin from sidebar" + }, + "settings":{ + "tabs": { + "general": "General", + "members": "Members", + "permission": "Permissions" + } } } \ No newline at end of file diff --git a/public/locales/jp/translation.json b/public/locales/jp/translation.json index 6191885d2..cbbfac1be 100644 --- a/public/locales/jp/translation.json +++ b/public/locales/jp/translation.json @@ -5,5 +5,12 @@ }, "welcome":{ "heading": "いらっしゃいませ" + }, + "settings":{ + "tabs": { + "general": "一般", + "members": "ユーザー", + "permission": "許可" + } } } \ No newline at end of file diff --git a/src/app/atoms/tabs/Tabs.jsx b/src/app/atoms/tabs/Tabs.jsx index 39800ce35..2c87cebe4 100644 --- a/src/app/atoms/tabs/Tabs.jsx +++ b/src/app/atoms/tabs/Tabs.jsx @@ -5,6 +5,9 @@ import './Tabs.scss'; import Button from '../button/Button'; import ScrollView from '../scroll/ScrollView'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function TabItem({ selected, iconSrc, onClick, children, disabled, @@ -41,6 +44,8 @@ TabItem.propTypes = { function Tabs({ items, defaultSelected, onSelect }) { const [selectedItem, setSelectedItem] = useState(items[defaultSelected]); + const { t } = useTranslation(); + const handleTabSelection = (item, index) => { if (selectedItem === item) return; setSelectedItem(item); @@ -59,7 +64,7 @@ function Tabs({ items, defaultSelected, onSelect }) { disabled={item.disabled} onClick={() => handleTabSelection(item, index)} > - {item.text} + {(item.translate != undefined && item.translate) ? t(item.text) : item.text} ))}
diff --git a/src/app/organisms/space-settings/SpaceSettings.jsx b/src/app/organisms/space-settings/SpaceSettings.jsx index 8ac561ee8..bcc01ee15 100644 --- a/src/app/organisms/space-settings/SpaceSettings.jsx +++ b/src/app/organisms/space-settings/SpaceSettings.jsx @@ -44,23 +44,26 @@ import '../../i18n.jsx' import { useTranslation } from 'react-i18next'; const tabText = { - GENERAL: 'General', - MEMBERS: 'Members', - PERMISSIONS: 'Permissions', + GENERAL: 'settings.tabs.general', + MEMBERS: 'settings.tabs.members', + PERMISSIONS: 'settings.tabs.permission', }; const tabItems = [{ iconSrc: SettingsIC, text: tabText.GENERAL, + translate: true, disabled: false, }, { iconSrc: UserIC, text: tabText.MEMBERS, + translate: true, disabled: false, }, { iconSrc: ShieldUserIC, text: tabText.PERMISSIONS, disabled: false, + translate: true }]; function GeneralSettings({ roomId }) { From 8c7749532b026620d10a16689fbdc027350ceb40 Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:02:39 +0930 Subject: [PATCH 03/21] Revert "Added translation to Tabs" This reverts commit 1ed9d6de782a5c5a96f73d3d27748ecf9efa12d6. --- public/locales/en/translation.json | 7 ------- public/locales/jp/translation.json | 7 ------- src/app/atoms/tabs/Tabs.jsx | 7 +------ src/app/organisms/space-settings/SpaceSettings.jsx | 9 +++------ 4 files changed, 4 insertions(+), 26 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index d2ee9a503..11cdca01c 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -30,12 +30,5 @@ "uncategorize_subspaces": "Uncategorize subspaces", "pin_sidebar": "Pin to sidebar", "unpin_sidebar": "Unpin from sidebar" - }, - "settings":{ - "tabs": { - "general": "General", - "members": "Members", - "permission": "Permissions" - } } } \ No newline at end of file diff --git a/public/locales/jp/translation.json b/public/locales/jp/translation.json index cbbfac1be..6191885d2 100644 --- a/public/locales/jp/translation.json +++ b/public/locales/jp/translation.json @@ -5,12 +5,5 @@ }, "welcome":{ "heading": "いらっしゃいませ" - }, - "settings":{ - "tabs": { - "general": "一般", - "members": "ユーザー", - "permission": "許可" - } } } \ No newline at end of file diff --git a/src/app/atoms/tabs/Tabs.jsx b/src/app/atoms/tabs/Tabs.jsx index 2c87cebe4..39800ce35 100644 --- a/src/app/atoms/tabs/Tabs.jsx +++ b/src/app/atoms/tabs/Tabs.jsx @@ -5,9 +5,6 @@ import './Tabs.scss'; import Button from '../button/Button'; import ScrollView from '../scroll/ScrollView'; -import '../../i18n.jsx' -import { useTranslation } from 'react-i18next'; - function TabItem({ selected, iconSrc, onClick, children, disabled, @@ -44,8 +41,6 @@ TabItem.propTypes = { function Tabs({ items, defaultSelected, onSelect }) { const [selectedItem, setSelectedItem] = useState(items[defaultSelected]); - const { t } = useTranslation(); - const handleTabSelection = (item, index) => { if (selectedItem === item) return; setSelectedItem(item); @@ -64,7 +59,7 @@ function Tabs({ items, defaultSelected, onSelect }) { disabled={item.disabled} onClick={() => handleTabSelection(item, index)} > - {(item.translate != undefined && item.translate) ? t(item.text) : item.text} + {item.text} ))}
diff --git a/src/app/organisms/space-settings/SpaceSettings.jsx b/src/app/organisms/space-settings/SpaceSettings.jsx index bcc01ee15..8ac561ee8 100644 --- a/src/app/organisms/space-settings/SpaceSettings.jsx +++ b/src/app/organisms/space-settings/SpaceSettings.jsx @@ -44,26 +44,23 @@ import '../../i18n.jsx' import { useTranslation } from 'react-i18next'; const tabText = { - GENERAL: 'settings.tabs.general', - MEMBERS: 'settings.tabs.members', - PERMISSIONS: 'settings.tabs.permission', + GENERAL: 'General', + MEMBERS: 'Members', + PERMISSIONS: 'Permissions', }; const tabItems = [{ iconSrc: SettingsIC, text: tabText.GENERAL, - translate: true, disabled: false, }, { iconSrc: UserIC, text: tabText.MEMBERS, - translate: true, disabled: false, }, { iconSrc: ShieldUserIC, text: tabText.PERMISSIONS, disabled: false, - translate: true }]; function GeneralSettings({ roomId }) { From 7bb187bce1b1c5445573dc428948e12ad0648761 Mon Sep 17 00:00:00 2001 From: Dylan Van Nielen Date: Thu, 14 Jul 2022 10:48:23 +0930 Subject: [PATCH 04/21] Implemented translation to all organisms --- public/locales/en/translation.json | 416 +++++++++++++++++- public/locales/jp/translation.json | 5 +- src/app/i18n.jsx | 1 - src/app/organisms/create-room/CreateRoom.jsx | 43 +- src/app/organisms/drag-drop/DragDrop.jsx | 8 +- .../emoji-verification/EmojiVerification.jsx | 24 +- src/app/organisms/invite-list/InviteList.jsx | 26 +- src/app/organisms/invite-user/InviteUser.jsx | 40 +- src/app/organisms/join-alias/JoinAlias.jsx | 25 +- .../organisms/navigation/DrawerBreadcrumb.jsx | 9 +- src/app/organisms/navigation/DrawerHeader.jsx | 27 +- src/app/organisms/navigation/SideBar.jsx | 22 +- .../profile-editor/ProfileEditor.jsx | 21 +- .../profile-viewer/ProfileViewer.jsx | 43 +- .../organisms/public-rooms/PublicRooms.jsx | 45 +- src/app/organisms/room/RoomSettings.jsx | 24 +- src/app/organisms/room/RoomViewContent.jsx | 36 +- src/app/organisms/room/RoomViewFloating.jsx | 39 +- src/app/organisms/room/RoomViewHeader.jsx | 14 +- src/app/organisms/room/RoomViewInput.jsx | 23 +- src/app/organisms/room/common.jsx | 120 +++-- src/app/organisms/search/Search.jsx | 9 +- src/app/organisms/settings/AuthRequest.jsx | 12 +- src/app/organisms/settings/CrossSigning.jsx | 136 +++--- src/app/organisms/settings/DeviceManage.jsx | 86 +++- src/app/organisms/settings/KeyBackup.jsx | 59 ++- .../settings/SecretStorageAccess.jsx | 16 +- src/app/organisms/settings/Settings.jsx | 85 ++-- .../shortcut-spaces/ShortcutSpaces.jsx | 25 +- .../organisms/space-manage/SpaceManage.jsx | 47 +- .../space-settings/SpaceSettings.jsx | 18 +- src/app/organisms/view-source/ViewSource.jsx | 6 +- src/app/organisms/welcome/Welcome.jsx | 4 +- 33 files changed, 1110 insertions(+), 404 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 11cdca01c..60aaae8ef 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -1,19 +1,80 @@ { "common" : { "close": "Close", + "open": "Open", "leave": "Leave", - "options": "Options" + "options": "Options", + "cinny": "Cinny", + "slogan": "Yet another matrix client", + "source_code": "Source code", + "sponsor": "Support", + "retry": "Retry", + "delete": "Delete", + "continue": "Continue", + "cancel": "Cancel", + "save": "Save", + "view_more": "View more", + "view_less": "View less", + "copy": "Copy", + "upload": "Upload", + "download": "Download", + "or": "Or", + "reset": "Reset", + "setup": "Setup", + "search": "Search", + "loading": "Loading...", + "joining": "Joining...", + "join": "Join", + "remove": "Remove", + "send": "Send", + "homeserver": "Homeserver", + "invite": "Invite", + "uninvite": "Uninvite", + "invited": "Invited", + "inviting": "Inviting...", + "uninviting": "Uninviting...", + "change": "Change", + "edit": "Edit", + "message_prompt": "Message" }, - "welcome": { + "errors": { + "browser_not_supported": "Not supported in this browser", + "generic": "Something went wrong!" + }, + "Welcome": { "heading": "Welcome to Cinny!", "subheading": "Yet another Matrix client" }, - "view_source":{ + "ViewSource":{ "title": "View Source", "original_source": "Original source", "decrypted_source": "Decrypted source" }, - "space_settings":{ + "SpaceManage": { + "subtitle": "manage rooms", + "load_more": "Load more", + "rooms_and_spaces": "Rooms and spaces", + "private_rooms_message": "Either the space contains private rooms or you need to join space to view it's rooms.", + "items_selected_zero": "No selected items", + "items_selected_one": "{{count}} selected item", + "items_selected_other": "{{count}} selected items", + "room_members_zero": "No room members", + "room_members_one": "{{count}} room member", + "room_members_other": "{{count}} room members", + "mark_suggested_zero": "Marking no rooms as suggested", + "mark_suggested_one": "Marking {{count}} room as suggested", + "mark_suggested_other": "Marking {{count}} rooms as suggested", + "mark_not_suggested_zero": "Marking no rooms as suggested", + "mark_not_suggested_one": "Marking {{count}} room as suggested", + "mark_not_suggested_other": "Marking {{count}} rooms as suggested", + "remove_zero": "Removing no items", + "remove_one": "Removing {{count}} item", + "remove_other": "Removing {{count}} items.", + "suggested": "Suggested", + "mark_as_suggested":"Mark as suggested", + "mark_as_not_suggested": "Mark as not suggested" + }, + "SpaceSettings":{ "subtitle": "space settings", "leave":{ "leave_space": "Leave Space", @@ -30,5 +91,352 @@ "uncategorize_subspaces": "Uncategorize subspaces", "pin_sidebar": "Pin to sidebar", "unpin_sidebar": "Unpin from sidebar" + }, + "Settings": { + "title": "Settings", + "theme": { + "follow_system": { + "title": "Follow system theme", + "description": "Use light or dark mode based on the system settings." + }, + "title": "Theme", + "theme_light": "Light", + "theme_silver": "Silver", + "theme_dark": "Dark", + "theme_butter": "Butter" + }, + "markdown": { + "title": "Markdown formatting", + "description": "Format messages with markdown before sending" + }, + "hide_membership_events": { + "title": "Hide membership events", + "description": "Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)" + }, + "hide_nickname_avatar_events": { + "title": "Hide nick/avatar events", + "description": "Hide nickname and avatar change messages from the room timeline." + }, + "notifications_and_sound": { + "title": "Notifications & Sound", + "desktop": { + "title": "Desktop notifications", + "description": "Show desktop notifications when new messages arrive." + }, + "sound": { + "title": "Notification sound", + "description": "Play a sound when new messages arrive." + } + }, + "security": { + "cross_signing": { + "title": "Cross signing and backup" + }, + "export_import_encryption_keys": { + "title": "Export / Import encryption keys" + }, + "export_encryption_keys": { + "title": "Export E2E room keys", + "description": "Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing." + }, + "import_encryption_keys": { + "title": "Import E2E room keys", + "description": "To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\\'ll have to enter the password you set in order to decrypt it." + } + }, + "logout": { + "title": "Logout", + "dialog": { + "title": "Logout", + "description": "Are you sure that you want to logout your session?", + "confirm": "Logout" + } + }, + "about":{ + "application": "Application", + "credits": "Credits" + } + }, + "ShortcutSpaces": { + "header": "Pin Spaces", + "pinned_spaces": "Pinned spaces", + "no_pinned_spaces": "No pinned spaces", + "unpinned_spaces": "Unpinned spaces", + "no_unpinned_spaces": "No unpinned spaces", + "spaces_selected_zero": "No selected spaces", + "spaces_selected_one": "{{count}} selected space", + "spaces_selected_other": "{{count}} selected spaces", + "pin_button": "Pin" + }, + "SecretStorageAccess": { + "incorrect_security_key": "Incorrect security key", + "incorrect_security_phrase": "Incorrect security phrase", + "security_phrase": "Security Phrase", + "security_key": "Security Key", + "use_security_key": "Use Security Key", + "use_security_phrase": "Use Security Phrase" + }, + "KeyBackup": { + "create_backup_title": "Create key backup", + "create_backup_tooltip": "Create backup", + "creating_backup": "Creating Backup...", + "backup_created": "Successfully created backup", + "backup_failed": "Failed to create backup", + "restoring": "Restoring backup keys...", + "restoring_progress": "Restoring backup keys... ({{progress}}/{{total}}", + "restore_backup_title": "Restore Key Backup", + "restore_backup_tooltip": "Restore Key Backup", + "restore_complete": "Successfully restored backup keys ({{progress}}/{{total}})", + "restore_failed_bad_key": "Failed to restore backup. Key is invalid!", + "restore_failed_unknown": "Failed to restore backup.", + "delete_key_backup_title": "Delete key backup", + "delete_key_backup_tooltip": "Delete backup", + "delete_key_backup_subtitle": "Deleting key backup is permanent.", + "delete_key_backup_message": "All encrypted message keys stored on the server will be permanently deleted.", + "encrypted_messages_backup_description": "Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.", + "encrypted_messages_backup_title": "Encrypted messages backup", + "encrypted_messages_backup_cross_signing_disabled": "Setup cross signing to backup your encrypted messages." + }, + "DeviceManage": { + "edit_session_name_title": "Edit session name", + "edit_session_name_subtitle": "Session name", + "edit_session_name_tooltip": "Edit session name", + "current_device_label": "Current", + "verify_session_button": "Verify", + "unverified_sessions_title": "Unverified sessions", + "unverified_sessions_none": "No unverified sessions", + "unencrypted_sessions_title": "Sessions without encryption support", + "verified_sessions_title": "Verified sessions", + "verified_sessions_none": "No verified sessions", + "setup_cross_signing_message": "Setup cross signing in case you lose all your sessions", + "loading_devices": "Loading devices...", + "logout_device_title": "Logout {{device}}", + "logout_device_message": "You are about to log out the session for {{device}}", + "logout_device_confirm": "Logout", + "logout_device_tooltip": "Remove session", + "session_verification_title": "Session Verification", + "session_name_privacy_message": "Session names are visible to everyone, so do not put any private info here." + }, + "CrossSigning":{ + "title": "Cross Signing", + "setup_failed": "Failed to setup cross signing. Please try again", + "setup": "Setup cross signing", + "save_security_key_message": "Please save this security key somewhere safe", + "security_key_dialog_title": "Security Key", + "security_key_generation_message": "We will generate a Security Key, which you can use to manage message backups and session verification.", + "security_key_generation_button": "Generate Key", + "security_phrase_message": "Alternatively you can set a 'Security Phrase' so you don't have to remember the long Security Key, and optionally save the key as a backup", + "security_phrase_label": "Security Phrase", + "security_phrase_confirm_label": "Confirm Security Phrase", + "security_phrase_set_button": "Set Phrase & Generate Key", + "setup_dialog_title": "Setup cross signing", + "setup_message": "Setup to verify and keep track of all your sessions. Also required to backup encrypted message.", + "reset_keys_subtitle": "Resetting cross-signing keys is permanent.", + "reset_keys_message": "Anyone you have verified with will see security alerts and your message backup will lost. You almost certainly do not want to do this, unless you have lost Security Key or Phrase and every session you can cross-sign from." + + }, + "AuthRequest" : { + "wrong_password": "Wrong password. Please enter the correct password", + "request_failed": "Request failed!", + "password_label": "Account password" + }, + "Search": { + "description": "Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k" + }, + "RoomViewInput":{ + "upload_progress": "Uploading: {{progress}}/{{total}} ({{percent}}%)", + "tombstone_replaced": "This room has been replaced, and is no longer active.", + "tombstone_permission_denied": "You do not have permission to post to this room", + "send_message_placeholder": "Send a message...", + "emoji_tooltip": "Emoji", + "file_size": "Size: {{size}}", + "cancel_reply_tooltip": "Cancel reply" + }, + "RoomViewHeader": { + "search_tooltip": "Search", + "people_tooltip": "People", + "members_tooltip": "Members" + }, + "RoomViewFloating": { + "jump_unread": "Jump to unread messages", + "mark_read": "Mark as read", + "jump_latest": "Jump to latest", + "user_typing_one": "{{user_one}} is typing...", + "user_typing_two": "{{user_one}} and {{user_two}} are typing...", + "user_typing_three": "{{user_one}}, {{user_two}} and {{user_three}} are typing...", + "user_typing_four": "{{user_one}}, {{user_two}}, {{user_three}} and {{user_four}} are typing...", + "user_typing_other": "Several people are typing..." + }, + "RoomViewContent": { + "welcome_to_room": "Welcome to {{room_name}}!", + "beginning_room": "This is the beginning of the {{room_name}} room.", + "beginning_dm": "This is the beginning of your direct message history with @{{user_name}}.", + "created_on": "Created on {{date, datetime}}", + "new_messages": "New messages" + }, + "RoomSettings" : { + "leave_room": "Leave room", + "leave_room_confirm_message": "Are you sure you want to leave {{room_name}}?", + "leave_room_confirm_button": "Leave", + "notification_header": "Notifications (Changing this will only affect you)", + "visibility_header": "Room visibility (Who can join)", + "address_header": "Room addresses", + "encryption_header": "Encryption", + "message_history_header": "Message history visibility", + "room_settings_subtitle": "room settings" + }, + "RoomCommon": { + "user_joined": "{{user_name}} joined the room", + "user_left": "{{user_name}} left the room", + "user_invited": "{{inviter_name}} invited {{user_name}}", + "invite_cancelled": "{{inviter_name}} cancelled {{user_name}}'s invite", + "invite_rejected": "{{user_name}} rejected the invitation", + "user_kicked": "{{actor}} kicked {{user_name}}: {{reason}}", + "user_banned": "{{actor}} banned {{user_name}}: {{reason}}", + "user_unbanned": "{{actor}} unbanned {{user_name}}", + "avatar_set": "{{user_name}} set an avatar", + "avatar_changed": "{{user_name}} changed their avatar", + "avatar_removed": "{{user_name}} removed their avatar", + "name_set": "{{user_name}} set their display name to {{new_name}}", + "name_changed": "{{user_name}} changed their display name to {{new_name}}", + "name_removed": "{{user_name}} removed their display name {{new_name}}" + }, + "PublicRooms": { + "could_not_join_alias": "Unable to join {{alias}}. Either the room is private or doesn't exist", + "try_joining_alias": "Try joining {{alias}}", + "joining_alias": "Joining {{alias}}...", + "no_public_rooms": "No public rooms on {{homeserver}}", + "no_result_found": "No result found for '{{input}}' on {{homeserver}}", + "title": "Public Rooms", + "search_room_name_alias": "Room name or alias", + "search_button": "Search", + "loading": "Loading public rooms from {{homeserver}}...", + "searching": "Searching for '{{query}}' on {{homeserver}}...", + "result_title": "Public rooms on {{homeserver}}", + "search_result_title": "Search result for '{{query}}' on {{homeserver}}" + }, + "ProfileViewer": { + "kick_button": "Kick", + "kick_reason_label": "Kick Reason", + "ban_button": "Ban", + "ban_reason_label": "Ban reason", + "loading_sessions" : "Loading sessions...", + "no_sessions_found": "No sessions found.", + "view_sessions_one": "View session", + "view_sessions_other": "View {{count}} sessions", + "send_direct_message_button": "Message", + "creating_dm_room": "Creating room...", + "ignore": "Ignore", + "ignoring": "Ignoring...", + "unignore": "Unignore", + "unignoring": "Unignoring...", + "change_power_level": "Change power level", + "shared_power_message": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?", + "demoting_self_message": "You will not be able to undo this change as you are demoting yourself. Are you sure?" + }, + "ProfileEditor": { + "remove_avatar": "Remove avatar", + "remove_avatar_confirmation": "Are you sure that you want to remove your avatar?", + "display_name_message": "Display name of {{user_name}}" + }, + "DrawerBreadcrumb": { + "home": "Home" + }, + "DrawerHeader" : { + "add_rooms_or_spaces": "Add rooms or spaces", + "create_new_space": "Create new space", + "create_new_room": "Create new room", + "join_public_room": "Join public room", + "join_with_address": "Join with address", + "add_existing": "Add existing", + "manage_rooms": "Manage rooms", + "home": "Home", + "direct_messages": "Direct messages", + "start_dm_tooltip": "Start DM", + "add_rooms_spaces_tooltip": "Add rooms/spaces" + }, + "SideBar": { + "settings_tooltip": "Settings", + "unverified_sessions_one": "{{count}} unverified session", + "unverified_sessions_other": "{{count}} unverified sessions", + "home_tooltip": "Home", + "direct_messages_tooltip": "People", + "pin_spaces_tooltip": "Pin spaces", + "search_tooltip": "Search", + "invites_tooltip": "Invites" + }, + "JoinAlias": { + "invalid_address": "Invalid address.", + "looking_for_address": "Looking for address...", + "joining_alias": "Joining {{alias_name}}...", + "couldnt_find_room_or_space_alias": "Unable to find room/space with {{alias_name}}. Either the room/space is private or doesn't exist.", + "couldnt_find_room_or_space": "Unable to join {{alias_name}}. Either the room/space is private or doesn't exist.", + "address_label": "Address", + "title": "Join with address" + }, + "InviteUser": { + "user_not_found": "{{user_name}} not found!", + "no_matches_found": "No matches found for {{user_name}}", + "invite_result": { + "invited": "Invited", + "already_joined": "Already joined", + "already_invited": "Already invited", + "banned": "Banned" + }, + "search_label": "Name or User ID", + "search_result_title": "Search result for user {{user_name}}", + "searching_for_user": "Searching for user {{user_name}}...", + "invite_to_room": "Invite to {{room}}", + "invite_to_dm": "Direct Message" + }, + "InviteList": { + "accept_invite": "Accept", + "reject_invite": "Reject", + "direct_messages_title": "Direct Messages", + "rooms_title": "Rooms", + "spaces_title": "Spaces", + "title": "Invites" + }, + "EmojiVerification": { + "waiting_for_response": "Waiting for response from other device...", + "confirmation_prompt": "Confirm the emoji below are displayed on both devices, in the same order:", + "emojis_match_button": "They match", + "emojis_dont_match_button": "They don't match", + "accept_request_from_other_device_message": "Please accept the request from other device.", + "begin_verification_process_message": "Click accept to start the verification process.", + "begin_verification_button_text": "Accept", + "title": "Emoji Verification" + }, + "DragDrop": { + "drop_file_to_upload_prompt": "Drop file to upload" + }, + "CreateRoom": { + "private_room_short": "Private", + "restricted_room_short": "Restricted", + "public_room_short": "Public", + "private_room_long": "Private (invite only)", + "restricted_room_long": "Restricted (space member can join)", + "public_room_long": "Public (anyone can join)", + "visibility_title": "Visibility", + "visibility_message": "Visibility (who can join)", + "select_who_can_join_space": "Select who can join this space", + "select_who_can_join_room": "Select who can join this room", + "space_address": "Space address", + "room_address": "Room address", + "room_address_already_in_use": "{{room_address}} is already in use", + "e2e_title": "Enable end-to-end encryption", + "e2e_message": "You can’t disable this later. Bridges & most bots won’t work yet.", + "role_title": "Select your role", + "role_message": "Selecting 'Admin' sets your power level to 100 whereas 'Founder' sets it to 101.", + "creating_room": "Creating room...", + "creating_space": "Creating space...", + "topic_label": "Topic (optional)", + "space_name": "Space name", + "room_name": "Room name", + "role_admin": "Admin", + "role_founder": "Founder", + "create_room": "Create room", + "create_space": "Create space", + "home": "Home" } } \ No newline at end of file diff --git a/public/locales/jp/translation.json b/public/locales/jp/translation.json index 6191885d2..c926fe9cd 100644 --- a/public/locales/jp/translation.json +++ b/public/locales/jp/translation.json @@ -3,7 +3,8 @@ "close": "閉める", "leave": "残す" }, - "welcome":{ - "heading": "いらっしゃいませ" + "Welcome": { + "heading": "おはようございます", + "subheading": "Yet another Matrix client" } } \ No newline at end of file diff --git a/src/app/i18n.jsx b/src/app/i18n.jsx index d951279bd..c62de110d 100644 --- a/src/app/i18n.jsx +++ b/src/app/i18n.jsx @@ -14,7 +14,6 @@ i18n // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, - fallbackLng: 'en', interpolation: { escapeValue: false, // not needed for react as it escapes by default } diff --git a/src/app/organisms/create-room/CreateRoom.jsx b/src/app/organisms/create-room/CreateRoom.jsx index 15be02d29..d9d7d509a 100644 --- a/src/app/organisms/create-room/CreateRoom.jsx +++ b/src/app/organisms/create-room/CreateRoom.jsx @@ -33,7 +33,14 @@ import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg'; import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + function CreateRoomContent({ isSpace, parentId, onRequestClose }) { + + const { t } = useTranslation(); + const [joinRule, setJoinRule] = useState(parentId ? 'restricted' : 'invite'); const [isEncrypted, setIsEncrypted] = useState(true); const [isCreatingRoom, setIsCreatingRoom] = useState(false); @@ -130,8 +137,8 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) { }; const joinRules = ['invite', 'restricted', 'public']; - const joinRuleShortText = ['Private', 'Restricted', 'Public']; - const joinRuleText = ['Private (invite only)', 'Restricted (space member can join)', 'Public (anyone can join)']; + const joinRuleShortText = [ t("CreateRoom.private_room_short"), t("CreateRoom.restricted_room_short"), t("CreateRoom.public_room_short")]; + const joinRuleText = [ t("CreateRoom.private_room_long"), t("CreateRoom.restricted_room_long"), t("CreateRoom.public_room_long")]; const jrRoomIC = [HashLockIC, HashIC, HashGlobeIC]; const jrSpaceIC = [SpaceLockIC, SpaceIC, SpaceGlobeIC]; const handleJoinRule = (evt) => { @@ -140,7 +147,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) { getEventCords(evt, '.btn-surface'), (closeMenu) => ( <> - Visibility (who can join) + {t("CreateRoom.visibility_message")} { joinRules.map((rule) => (
{joinRuleShortText[joinRules.indexOf(joinRule)]} )} - content={{`Select who can join this ${isSpace ? 'space' : 'room'}.`}} + content={{isSpace ? t("CreateRoom.select_who_can_join_space") : t("CreateRoom.select_who_can_join_room")}} /> {joinRule === 'public' && (
- {isSpace ? 'Space address' : 'Room address'} + {isSpace ? t("CreateRoom.space_address") : t("CreateRoom.room_address")}
# {`:${userHs}`}
- {isValidAddress === false && {`#${addressValue}:${userHs} is already in use`}} + {isValidAddress === false && { t("CreateRoom.room_address_already_in_use", {room_address: `#${addressValue}:${userHs}`})}}
)} {!isSpace && joinRule !== 'public' && ( } - content={You can’t disable this later. Bridges & most bots won’t work yet.} + content={ {t("CreateRoom.e2e_message")}} /> )} )} content={( - Selecting Admin sets 100 power level whereas Founder sets 101. + {t("CreateRoom.role_message")} )} /> - +
- +
)} {typeof creatingError === 'string' && {creatingError}} @@ -277,13 +284,13 @@ function CreateRoom() { isOpen={create !== null} title={( - {parentId ? twemojify(room.name) : 'Home'} + {parentId ? twemojify(room.name) : t("CreateRoom.home")} - {` — create ${isSpace ? 'space' : 'room'}`} + {` — ${isSpace ? t("CreateRoom.create_space") : t("CreateRoom.create_room")}`} )} - contentOptions={} + contentOptions={} onRequestClose={onRequestClose} > { diff --git a/src/app/organisms/drag-drop/DragDrop.jsx b/src/app/organisms/drag-drop/DragDrop.jsx index e92f8a703..8576325fc 100644 --- a/src/app/organisms/drag-drop/DragDrop.jsx +++ b/src/app/organisms/drag-drop/DragDrop.jsx @@ -5,14 +5,20 @@ import './DragDrop.scss'; import RawModal from '../../atoms/modal/RawModal'; import Text from '../../atoms/text/Text'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function DragDrop({ isOpen }) { + + const { t } = useTranslation(); + return ( - Drop file to upload + {t("DragDrop.drop_file_to_upload_prompt")} ); } diff --git a/src/app/organisms/emoji-verification/EmojiVerification.jsx b/src/app/organisms/emoji-verification/EmojiVerification.jsx index 6fe81cddf..9d54ba24c 100644 --- a/src/app/organisms/emoji-verification/EmojiVerification.jsx +++ b/src/app/organisms/emoji-verification/EmojiVerification.jsx @@ -20,6 +20,10 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import { useStore } from '../../hooks/useStore'; import { accessSecretStorage } from '../settings/SecretStorageAccess'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + function EmojiVerificationContent({ data, requestClose }) { const [sas, setSas] = useState(null); const [process, setProcess] = useState(false); @@ -28,6 +32,8 @@ function EmojiVerificationContent({ data, requestClose }) { const mountStore = useStore(); const beginStore = useStore(); + const { t } = useTranslation(); + const beginVerification = async () => { if ( isCrossVerified(mx.deviceId) @@ -94,14 +100,14 @@ function EmojiVerificationContent({ data, requestClose }) { const renderWait = () => ( <> - Waiting for response from other device... + {t("EmojiVerification.waiting_for_response")} ); if (sas !== null) { return (
- Confirm the emoji below are displayed on both devices, in the same order: + {t("EmojiVerification.confirmation_prompt")}
{sas.sas.emoji.map((emoji, i) => ( // eslint-disable-next-line react/no-array-index-key @@ -114,8 +120,8 @@ function EmojiVerificationContent({ data, requestClose }) {
{process ? renderWait() : ( <> - - + + )}
@@ -126,7 +132,7 @@ function EmojiVerificationContent({ data, requestClose }) { if (targetDevice) { return (
- Please accept the request from other device. + {t("EmojiVerification.accept_request_from_other_device_message")}
{renderWait()}
@@ -136,12 +142,12 @@ function EmojiVerificationContent({ data, requestClose }) { return (
- Click accept to start the verification process. + {t("EmojiVerification.begin_verification_process_message")}
{ process ? renderWait() - : + : }
@@ -182,10 +188,10 @@ function EmojiVerification() { className="emoji-verification" title={( - Emoji verification + {t("EmojiVerification.title")} )} - contentOptions={} + contentOptions={} onRequestClose={requestClose} > { diff --git a/src/app/organisms/invite-list/InviteList.jsx b/src/app/organisms/invite-list/InviteList.jsx index 231928fee..c94950a5a 100644 --- a/src/app/organisms/invite-list/InviteList.jsx +++ b/src/app/organisms/invite-list/InviteList.jsx @@ -16,9 +16,17 @@ import RoomTile from '../../molecules/room-tile/RoomTile'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + + + function InviteList({ isOpen, onRequestClose }) { const [procInvite, changeProcInvite] = useState(new Set()); + const { t } = useTranslation(); + function acceptInvite(roomId, isDM) { procInvite.add(roomId); changeProcInvite(new Set(Array.from(procInvite))); @@ -73,8 +81,8 @@ function InviteList({ isOpen, onRequestClose }) { ? () : (
- - + +
) } @@ -85,14 +93,14 @@ function InviteList({ isOpen, onRequestClose }) { return ( } + title={t("InviteList.title")} + contentOptions={} onRequestClose={onRequestClose} >
{ initMatrix.roomList.inviteDirects.size !== 0 && (
- Direct Messages + {t("InviteList.direct_messages_title")}
)} { @@ -110,8 +118,8 @@ function InviteList({ isOpen, onRequestClose }) { ? () : (
- - + +
) } @@ -121,14 +129,14 @@ function InviteList({ isOpen, onRequestClose }) { } { initMatrix.roomList.inviteSpaces.size !== 0 && (
- Spaces + {t("InviteList.spaces_title")}
)} { Array.from(initMatrix.roomList.inviteSpaces).map(renderRoomTile) } { initMatrix.roomList.inviteRooms.size !== 0 && (
- Rooms + {t("InviteList.rooms_title")}
)} { Array.from(initMatrix.roomList.inviteRooms).map(renderRoomTile) } diff --git a/src/app/organisms/invite-user/InviteUser.jsx b/src/app/organisms/invite-user/InviteUser.jsx index d0a8d9e13..56ad16fda 100644 --- a/src/app/organisms/invite-user/InviteUser.jsx +++ b/src/app/organisms/invite-user/InviteUser.jsx @@ -19,6 +19,10 @@ import RoomTile from '../../molecules/room-tile/RoomTile'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import UserIC from '../../../../public/res/ic/outlined/user.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + function InviteUser({ isOpen, roomId, searchTerm, onRequestClose, }) { @@ -36,6 +40,8 @@ function InviteUser({ const usernameRef = useRef(null); + const { t } = useTranslation(); + const mx = initMatrix.matrixClient; function getMapCopy(myMap) { @@ -82,7 +88,7 @@ function InviteUser({ avatar_url: result.avatar_url, }]); } catch (e) { - updateSearchQuery({ error: `${inputUsername} not found!` }); + updateSearchQuery({error: t("InviteUser.user_not_found", {user_name: inputUsername})}); } } else { try { @@ -91,13 +97,13 @@ function InviteUser({ limit: 20, }); if (result.results.length === 0) { - updateSearchQuery({ error: `No matches found for "${inputUsername}"!` }); + updateSearchQuery({ error: t("InviteUser.no_matches_found", {user_name: inputUsername})}); updateIsSearching(false); return; } updateUsers(result.results); } catch (e) { - updateSearchQuery({ error: 'Something went wrong!' }); + updateSearchQuery({ error: t("errors.generic")}); } } updateIsSearching(false); @@ -135,7 +141,7 @@ function InviteUser({ } catch (e) { deleteUserFromProc(userId); if (typeof e.message === 'string') procUserError.set(userId, e.message); - else procUserError.set(userId, 'Something went wrong!'); + else procUserError.set(userId, t("errors.generic")); updateUserProcError(getMapCopy(procUserError)); } } @@ -155,7 +161,7 @@ function InviteUser({ } catch (e) { deleteUserFromProc(userId); if (typeof e.message === 'string') procUserError.set(userId, e.message); - else procUserError.set(userId, 'Something went wrong!'); + else procUserError.set(userId, t("errors.generic")); updateUserProcError(getMapCopy(procUserError)); } } @@ -173,7 +179,7 @@ function InviteUser({ return ; } if (invitedUserIds.has(userId)) { - return messageJSX('Invited', true); + return messageJSX(t("InviteUser.invite_result.invited"), true); } if (typeof roomId === 'string') { const member = mx.getRoom(roomId).getMember(userId); @@ -181,18 +187,18 @@ function InviteUser({ const userMembership = member.membership; switch (userMembership) { case 'join': - return messageJSX('Already joined', true); + return messageJSX(t("InviteUser.invite_result.already_joined"), true); case 'invite': - return messageJSX('Already Invited', true); + return messageJSX(t("InviteUser.invite_result.already_invited"), true); case 'ban': - return messageJSX('Banned', false); + return messageJSX(t("InviteUser.invite_result.banned"), false); default: } } } return (typeof roomId === 'string') - ? - : ; + ? + : ; }; const renderError = (userId) => { if (!procUserError.has(userId)) return null; @@ -239,27 +245,27 @@ function InviteUser({ return ( } + title={(typeof roomId === 'string' ? t("InviteUser.invite_to_room", {room: mx.getRoom(roomId).name}) : t("InviteUser.invite_to_dm"))} + contentOptions={} onRequestClose={onRequestClose} >
{ e.preventDefault(); searchUser(usernameRef.current.value); }}> - - + +
{ typeof searchQuery.username !== 'undefined' && isSearching && (
- {`Searching for user "${searchQuery.username}"...`} + {t("InviteUser.searching_for_user", {user_name: searchQuery.username})}
) } { typeof searchQuery.username !== 'undefined' && !isSearching && ( - {`Search result for user "${searchQuery.username}"`} + {t("InviteUser.search_result_title", {user_name: searchQuery.username})} ) } { diff --git a/src/app/organisms/join-alias/JoinAlias.jsx b/src/app/organisms/join-alias/JoinAlias.jsx index 9e7f6df1a..05e033a33 100644 --- a/src/app/organisms/join-alias/JoinAlias.jsx +++ b/src/app/organisms/join-alias/JoinAlias.jsx @@ -19,6 +19,11 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import { useStore } from '../../hooks/useStore'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + + const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/; function JoinAliasContent({ term, requestClose }) { @@ -29,6 +34,8 @@ function JoinAliasContent({ term, requestClose }) { const mx = initMatrix.matrixClient; const mountStore = useStore(); + const { t } = useTranslation(); + const openRoom = (roomId) => { const room = mx.getRoom(roomId); if (!room) return; @@ -54,10 +61,10 @@ function JoinAliasContent({ term, requestClose }) { const alias = e.target.alias.value; if (alias?.trim() === '') return; if (alias.match(ALIAS_OR_ID_REG) === null) { - setError('Invalid address.'); + setError(t("JoinAlias.invalid_address")); return; } - setProcess('Looking for address...'); + setProcess(t("JoinAlias.looking_for_address")); setError(undefined); let via; if (alias.startsWith('#')) { @@ -65,12 +72,12 @@ function JoinAliasContent({ term, requestClose }) { const aliasData = await mx.resolveRoomAlias(alias); via = aliasData?.servers.slice(0, 3) || []; if (mountStore.getItem()) { - setProcess(`Joining ${alias}...`); + setProcess(t("JoinAlias.joining_alias", {alias_name: alias})); } } catch (err) { if (!mountStore.getItem()) return; setProcess(false); - setError(`Unable to find room/space with ${alias}. Either room/space is private or doesn't exist.`); + setError(t("JoinAlias.couldnt_find_room_or_space_alias", {alias_name: alias})); } } try { @@ -81,14 +88,14 @@ function JoinAliasContent({ term, requestClose }) { } catch { if (!mountStore.getItem()) return; setProcess(false); - setError(`Unable to join ${alias}. Either room/space is private or doesn't exist.`); + setError(t("JoinAlias.couldnt_find_room_or_space", {alias_name: alias})); } }; return (
{process} ) - : + : }
@@ -142,9 +149,9 @@ function JoinAlias() { Join with address + {t("JoinAlias.title")} )} - contentOptions={} + contentOptions={} onRequestClose={requestClose} > { data ? :
} diff --git a/src/app/organisms/navigation/DrawerBreadcrumb.jsx b/src/app/organisms/navigation/DrawerBreadcrumb.jsx index ca9ab6a09..f67693bfd 100644 --- a/src/app/organisms/navigation/DrawerBreadcrumb.jsx +++ b/src/app/organisms/navigation/DrawerBreadcrumb.jsx @@ -18,6 +18,11 @@ import NotificationBadge from '../../atoms/badge/NotificationBadge'; import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + + function DrawerBreadcrumb({ spaceId }) { const [, forceUpdate] = useState({}); const scrollRef = useRef(null); @@ -25,6 +30,8 @@ function DrawerBreadcrumb({ spaceId }) { const mx = initMatrix.matrixClient; const spacePath = navigation.selectedSpacePath; + const { t } = useTranslation(); + function onNotiChanged(roomId, total, prevTotal) { if (total === prevTotal) return; if (navigation.selectedSpacePath.includes(roomId)) { @@ -109,7 +116,7 @@ function DrawerBreadcrumb({ spaceId }) { className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''} onClick={() => selectSpace(id)} > - {id === cons.tabs.HOME ? 'Home' : twemojify(mx.getRoom(id).name)} + {id === cons.tabs.HOME ? t("DrawerBreadcrumb.home") : twemojify(mx.getRoom(id).name)} { noti !== null && ( - Add rooms or spaces + {t("DrawerHeader.add_rooms_or_spaces")} { afterOptionSelect(); openCreateRoom(true, spaceId); }} disabled={!canManage} > - Create new space + {t("DrawerHeader.create_new_space")} { afterOptionSelect(); openCreateRoom(false, spaceId); }} disabled={!canManage} > - Create new room + {t("DrawerHeader.create_new_room")} { !spaceId && ( { afterOptionSelect(); openPublicRooms(); }} > - Join public room + {t("DrawerHeader.join_public_room")} )} { !spaceId && ( @@ -65,7 +72,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { iconSrc={PlusIC} onClick={() => { afterOptionSelect(); openJoinAlias(); }} > - Join with address + {t("DrawerHeader.join_with_address")} )} { spaceId && ( @@ -74,7 +81,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { onClick={() => { afterOptionSelect(); openSpaceAddExisting(spaceId); }} disabled={!canManage} > - Add existing + {t("DrawerHeader.add_existing")} )} { spaceId && ( @@ -82,7 +89,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { onClick={() => { afterOptionSelect(); openSpaceManage(spaceId); }} iconSrc={HashSearchIC} > - Manage rooms + {t("DrawerHeader.manage_rooms")} )} @@ -98,7 +105,7 @@ HomeSpaceOptions.propTypes = { function DrawerHeader({ selectedTab, spaceId }) { const mx = initMatrix.matrixClient; - const tabName = selectedTab !== cons.tabs.DIRECTS ? 'Home' : 'Direct messages'; + const tabName = selectedTab !== cons.tabs.DIRECTS ? t("DrawerHeader.home") : t("DrawerHeader.direct_messages"); const isDMTab = selectedTab === cons.tabs.DIRECTS; const room = mx.getRoom(spaceId); @@ -142,8 +149,8 @@ function DrawerHeader({ selectedTab, spaceId }) { )} - { isDMTab && openInviteUser()} tooltip="Start DM" src={PlusIC} size="small" /> } - { !isDMTab && } + { isDMTab && openInviteUser()} tooltip={t("DrawerHeader.start_dm_tooltip")} src={PlusIC} size="small" /> } + { !isDMTab && } ); } diff --git a/src/app/organisms/navigation/SideBar.jsx b/src/app/organisms/navigation/SideBar.jsx index 531869656..8f587b126 100644 --- a/src/app/organisms/navigation/SideBar.jsx +++ b/src/app/organisms/navigation/SideBar.jsx @@ -34,6 +34,11 @@ import { useDeviceList } from '../../hooks/useDeviceList'; import { tabText as settingTabText } from '../settings/Settings'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + + function useNotificationUpdate() { const { notifications } = initMatrix; const [, forceUpdate] = useState({}); @@ -50,6 +55,9 @@ function useNotificationUpdate() { } function ProfileAvatarMenu() { + + const { t } = useTranslation(); + const mx = initMatrix.matrixClient; const [profile, setProfile] = useState({ avatarUrl: null, @@ -77,7 +85,7 @@ function ProfileAvatarMenu() { return ( openSettings(settingTabText.SECURITY)} avatar={} /> @@ -147,7 +155,7 @@ function FeaturedTab() { return ( <> selectTab(cons.tabs.HOME)} avatar={} @@ -159,7 +167,7 @@ function FeaturedTab() { ) : null} /> selectTab(cons.tabs.DIRECTS)} avatar={} @@ -355,7 +363,7 @@ function SideBar() {
openShortcutSpaces()} avatar={} /> @@ -367,13 +375,13 @@ function SideBar() {
openSearch()} avatar={} /> { totalInvites !== 0 && ( openInviteList()} avatar={} notificationBadge={} diff --git a/src/app/organisms/profile-editor/ProfileEditor.jsx b/src/app/organisms/profile-editor/ProfileEditor.jsx index 972192efd..5eaa3b24f 100644 --- a/src/app/organisms/profile-editor/ProfileEditor.jsx +++ b/src/app/organisms/profile-editor/ProfileEditor.jsx @@ -16,6 +16,11 @@ import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; import './ProfileEditor.scss'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + + // TODO Fix bug that prevents 'Save' button from enabling up until second changed. function ProfileEditor({ userId }) { const [isEditing, setIsEditing] = useState(false); @@ -27,6 +32,8 @@ function ProfileEditor({ userId }) { const [username, setUsername] = useState(user.displayName); const [disabled, setDisabled] = useState(true); + const { t } = useTranslation(); + useEffect(() => { let isMounted = true; mx.getProfileInfo(mx.getUserId()).then((info) => { @@ -42,9 +49,9 @@ function ProfileEditor({ userId }) { const handleAvatarUpload = async (url) => { if (url === null) { const isConfirmed = await confirmDialog( - 'Remove avatar', - 'Are you sure that you want to remove avatar?', - 'Remove', + t("ProfileEditor.remove_avatar"), + t("ProfileViewer.remove_avatar_confirmation"), + t("common.remove"), 'caution', ); if (isConfirmed) { @@ -83,13 +90,13 @@ function ProfileEditor({ userId }) { onSubmit={(e) => { e.preventDefault(); saveDisplayName(); }} > - - + + ); @@ -100,7 +107,7 @@ function ProfileEditor({ userId }) { setIsEditing(true)} />
diff --git a/src/app/organisms/profile-viewer/ProfileViewer.jsx b/src/app/organisms/profile-viewer/ProfileViewer.jsx index d74629f26..731fc9561 100644 --- a/src/app/organisms/profile-viewer/ProfileViewer.jsx +++ b/src/app/organisms/profile-viewer/ProfileViewer.jsx @@ -34,9 +34,16 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import { useForceUpdate } from '../../hooks/useForceUpdate'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + function ModerationTools({ roomId, userId, }) { + + const { t } = useTranslation(); + const mx = initMatrix.matrixClient; const room = mx.getRoom(roomId); const roomMember = room.getMember(userId); @@ -70,14 +77,14 @@ function ModerationTools({
{canIKick && (
- - + +
)} {canIBan && (
- - + +
)}
@@ -93,6 +100,8 @@ function SessionInfo({ userId }) { const [isVisible, setIsVisible] = useState(false); const mx = initMatrix.matrixClient; + const { t } = useTranslation(); + useEffect(() => { let isUnmounted = false; @@ -118,8 +127,8 @@ function SessionInfo({ userId }) { if (!isVisible) return null; return (
- {devices === null && Loading sessions...} - {devices?.length === 0 && No session found.} + {devices === null && {t("ProfileViewer.loading_sessions")}} + {devices?.length === 0 && {t("ProfileViewer.no_sessions_found")}} {devices !== null && (devices.map((device) => ( setIsVisible(!isVisible)} iconSrc={isVisible ? ChevronBottomIC : ChevronRightIC} > - {`View ${devices?.length > 0 ? `${devices.length} ` : ''}sessions`} + {t("ProfileViewer.view_sessions", {count: devices?.length})} {renderSessionChips()}
@@ -252,7 +261,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) { onClick={openDM} disabled={isCreatingDM} > - {isCreatingDM ? 'Creating room...' : 'Message'} + {isCreatingDM ? t("ProfileViewer.creating_dm_room") : t("ProfileViewer.send_direct_message_button")} { isBanned && canIKick && ( )} @@ -281,8 +290,8 @@ function ProfileFooter({ roomId, userId, onRequestClose }) { > { isUserIgnored - ? `${isIgnoring ? 'Unignoring...' : 'Unignore'}` - : `${isIgnoring ? 'Ignoring...' : 'Ignore'}` + ? `${isIgnoring ? t("ProfileViewer.unignoring") : t("ProfileViewer.unignore")}` + : `${isIgnoring ? t("ProfileViewer.ignoring") : t("ProfileViewer.ignore")}` }
@@ -365,16 +374,16 @@ function ProfileViewer() { const handleChangePowerLevel = async (newPowerLevel) => { if (newPowerLevel === powerLevel) return; - const SHARED_POWER_MSG = 'You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?'; - const DEMOTING_MYSELF_MSG = 'You will not be able to undo this change as you are demoting yourself. Are you sure?'; + const SHARED_POWER_MSG = t("ProfileViewer.shared_power_message"); + const DEMOTING_MYSELF_MSG = t("ProfileViewer.demoting_self_message"); const isSharedPower = newPowerLevel === myPowerLevel; const isDemotingMyself = userId === mx.getUserId(); if (isSharedPower || isDemotingMyself) { const isConfirmed = await confirmDialog( - 'Change power level', + t("ProfileViewer.change_power_level"), isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG, - 'Change', + t("common.change"), 'caution', ); if (!isConfirmed) return; @@ -435,7 +444,7 @@ function ProfileViewer() { title={room?.name ?? ''} onAfterClose={handleAfterClose} onRequestClose={closeDialog} - contentOptions={} + contentOptions={} > {roomId ? renderProfile() :
}
diff --git a/src/app/organisms/public-rooms/PublicRooms.jsx b/src/app/organisms/public-rooms/PublicRooms.jsx index d1674c32f..970db63aa 100644 --- a/src/app/organisms/public-rooms/PublicRooms.jsx +++ b/src/app/organisms/public-rooms/PublicRooms.jsx @@ -18,9 +18,15 @@ import RoomTile from '../../molecules/room-tile/RoomTile'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + const SEARCH_LIMIT = 20; function TryJoinWithAlias({ alias, onRequestClose }) { + const { t } = useTranslation(); + const [status, setStatus] = useState({ isJoining: false, error: null, @@ -53,7 +59,7 @@ function TryJoinWithAlias({ alias, onRequestClose }) { } catch (e) { setStatus({ isJoining: false, - error: `Unable to join ${alias}. Either room is private or doesn't exist.`, + error: t("PublicRooms.could_not_join_alias", {alias: alias}), roomId: null, tempRoomId: null, }); @@ -63,16 +69,16 @@ function TryJoinWithAlias({ alias, onRequestClose }) { return (
{status.roomId === null && !status.isJoining && status.error === null && ( - + )} {status.isJoining && ( <> - {`Joining ${alias}...`} + {t("PublicRooms.joining_alias", {alias: alias})} )} {status.roomId !== null && ( - + )} {status.error !== null && {status.error}}
@@ -92,6 +98,7 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { const [searchQuery, updateSearchQuery] = useState({}); const [joiningRooms, updateJoiningRooms] = useState(new Set()); + const { t } = useTranslation(); const roomNameRef = useRef(null); const hsRef = useRef(null); const userId = initMatrix.matrixClient.getUserId(); @@ -140,14 +147,14 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { if (totalRooms.length === 0) { updateSearchQuery({ error: inputRoomName === '' - ? `No public rooms on ${inputHs}` - : `No result found for "${inputRoomName}" on ${inputHs}`, + ? t("PublicRooms.no_public_rooms", {homeserver: inputHs}) + : t("PublicRooms.no_result_found", {homeserver: inputHs, input: inputRoomName}), alias: isInputAlias ? inputRoomName : null, }); } } catch (e) { updatePublicRooms([]); - let err = 'Something went wrong!'; + let err = t("errors.generic"); if (e?.httpStatus >= 400 && e?.httpStatus < 500) { err = e.message; } @@ -206,8 +213,8 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { desc={typeof room.topic === 'string' ? room.topic : null} options={( <> - {isJoined && } - {!isJoined && (joiningRooms.has(room.room_id) ? : )} + {isJoined && } + {!isJoined && (joiningRooms.has(room.room_id) ? : )} )} /> @@ -218,17 +225,17 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { return ( } + title={t("PublicRooms.title")} + contentOptions={} onRequestClose={onRequestClose} >
{ e.preventDefault(); searchRooms(); }}>
- - + +
- +
{ @@ -237,13 +244,13 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { ? (
- {`Loading public rooms from ${searchQuery.homeserver}...`} + {t("PublicRooms.loading", {homeserver: searchQuery.homeserver})}
) : (
- {`Searching for "${searchQuery.name}" on ${searchQuery.homeserver}...`} + {t("PublicRooms.searching", {homeserver: searchQuery.homeserver, query: searchQuery.name})}
) ) @@ -251,8 +258,8 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { { typeof searchQuery.name !== 'undefined' && !isSearching && ( searchQuery.name === '' - ? {`Public rooms on ${searchQuery.homeserver}.`} - : {`Search result for "${searchQuery.name}" on ${searchQuery.homeserver}.`} + ? {t("PublicRooms.result_title", {homeserver: searchQuery.homeserver})} + : {t("PublicRooms.search_result_title", {homeserver: searchQuery.homeserver, query: searchQuery.name})} ) } { searchQuery.error && ( @@ -272,7 +279,7 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { { publicRooms.length !== 0 && publicRooms.length % SEARCH_LIMIT === 0 && (
{ isViewMore !== true && ( - + )} { isViewMore && }
diff --git a/src/app/organisms/room/RoomSettings.jsx b/src/app/organisms/room/RoomSettings.jsx index 50c5e5127..dbd597f10 100644 --- a/src/app/organisms/room/RoomSettings.jsx +++ b/src/app/organisms/room/RoomSettings.jsx @@ -38,6 +38,10 @@ import ChevronTopIC from '../../../../public/res/ic/outlined/chevron-top.svg'; import { useForceUpdate } from '../../hooks/useForceUpdate'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; + const tabText = { GENERAL: 'General', SEARCH: 'Search', @@ -73,6 +77,8 @@ function GeneralSettings({ roomId }) { const room = mx.getRoom(roomId); const canInvite = room.canInvite(mx.getUserId()); + const { t } = useTranslation(); + return ( <>
@@ -88,9 +94,9 @@ function GeneralSettings({ roomId }) { variant="danger" onClick={async () => { const isConfirmed = await confirmDialog( - 'Leave room', - `Are you sure that you want to leave "${room.name}" room?`, - 'Leave', + t("RoomSettings.leave_room"), + t("RoomSettings.leave_room_confirm_message", {room_name: room.name}), + t("RoomSettings.leave_room_confirm_button"), 'danger', ); if (!isConfirmed) return; @@ -102,15 +108,15 @@ function GeneralSettings({ roomId }) {
- Notification (Changing this will only affect you) + {t("RoomSettings.notification_header")}
- Room visibility (who can join) + {t("RoomSettings.visibility_header")}
- Room addresses + {t("RoomSettings.address_header")}
@@ -125,11 +131,11 @@ function SecuritySettings({ roomId }) { return ( <>
- Encryption + {t("RoomSettings.encryption_header")}
- Message history visibility + {t("RoomSettings.message_history_header")}
@@ -181,7 +187,7 @@ function RoomSettings({ roomId }) { {`${room.name}`} - — room settings + — {t("RoomSettings.room_settings_subtitle")} diff --git a/src/app/organisms/room/RoomViewContent.jsx b/src/app/organisms/room/RoomViewContent.jsx index ab1dfbab6..9a5ec5c1c 100644 --- a/src/app/organisms/room/RoomViewContent.jsx +++ b/src/app/organisms/room/RoomViewContent.jsx @@ -29,6 +29,10 @@ import { parseTimelineChange } from './common'; import TimelineScroll from './TimelineScroll'; import EventLimit from './EventLimit'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { Trans } from 'react-i18next'; + const PAG_LIMIT = 30; const MAX_MSG_DIFF_MINUTES = 5; const PLACEHOLDER_COUNT = 2; @@ -55,29 +59,39 @@ function RoomIntroContainer({ event, timeline }) { const [, nameForceUpdate] = useForceUpdate(); const mx = initMatrix.matrixClient; const { roomList } = initMatrix; + + const { t } = useTranslation(); + const { room } = timeline; const roomTopic = room.currentState.getStateEvents('m.room.topic')[0]?.getContent().topic; const isDM = roomList.directs.has(timeline.roomId); let avatarSrc = room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop'); avatarSrc = isDM ? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc; - const heading = isDM ? room.name : `Welcome to ${room.name}`; + const heading = isDM ? room.name : t("RoomViewContent.welcome_to_room", {room_name: room.name}); const topic = twemojify(roomTopic || '', undefined, true); const nameJsx = twemojify(room.name); const desc = isDM ? ( <> - This is the beginning of your direct message history with @ - {nameJsx} - {'. '} - {topic} + }} + /> + {topic == "" ? "" : " - "} + {topic } ) : ( <> - {'This is the beginning of the '} - {nameJsx} - {' room. '} + }} + /> + + {topic == "" ? "" : " - "} {topic} ); @@ -98,7 +112,7 @@ function RoomIntroContainer({ event, timeline }) { name={room.name} heading={twemojify(heading)} desc={desc} - time={event ? `Created at ${dateFormat(event.getDate(), 'dd mmmm yyyy, hh:MM TT')}` : null} + time={event ? t("RoomViewContent.created_on", {date: event.getDate(), formatParams: { date: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}}}) : null} /> ); } @@ -386,6 +400,8 @@ let jumpToItemIndex = -1; function RoomViewContent({ eventId, roomTimeline }) { const [throttle] = useState(new Throttle()); + const { t } = useTranslation(); + const timelineSVRef = useRef(null); const timelineScrollRef = useRef(null); const eventLimitRef = useRef(null); @@ -523,7 +539,7 @@ function RoomViewContent({ eventId, roomTimeline }) { && readUptoEvent.getTs() < mEvent.getTs()); if (unreadDivider) { isNewEvent = true; - tl.push(); + tl.push(); itemCountIndex += 1; if (jumpToItemIndex === -1) jumpToItemIndex = itemCountIndex; } diff --git a/src/app/organisms/room/RoomViewFloating.jsx b/src/app/organisms/room/RoomViewFloating.jsx index d027aff21..bd8c16766 100644 --- a/src/app/organisms/room/RoomViewFloating.jsx +++ b/src/app/organisms/room/RoomViewFloating.jsx @@ -3,6 +3,8 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import './RoomViewFloating.scss'; +import { twemojify } from '../../../util/twemojify'; + import initMatrix from '../../../client/initMatrix'; import cons from '../../../client/state/cons'; import { markAsRead } from '../../../client/action/notifications'; @@ -14,8 +16,14 @@ import MessageIC from '../../../../public/res/ic/outlined/message.svg'; import MessageUnreadIC from '../../../../public/res/ic/outlined/message-unread.svg'; import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg'; +import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil'; + import { getUsersActionJsx } from './common'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; +import { Trans } from 'react-i18next'; + function useJumpToEvent(roomTimeline) { const [eventId, setEventId] = useState(null); @@ -86,32 +94,55 @@ function useScrollToBottom(roomTimeline) { function RoomViewFloating({ roomId, roomTimeline, }) { + + const { t } = useTranslation(); + const [isJumpToEvent, jumpToEvent, cancelJumpToEvent] = useJumpToEvent(roomTimeline); const [typingMembers] = useTypingMembers(roomTimeline); const [isAtBottom, setIsAtBottom] = useScrollToBottom(roomTimeline); + const room = initMatrix.matrixClient.getRoom(roomId) + + const getUserDisplayName = (userId) => { + console.log(userId); + if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId)); + return getUsername(userId); + }; + const handleScrollToBottom = () => { roomTimeline.emit(cons.events.roomTimeline.SCROLL_TO_LIVE); setIsAtBottom(true); }; + console.log(typingMembers) + + let typingMemberValues = [...typingMembers]; + + console.log(typingMemberValues) + return ( <>
0 ? ' room-view__typing--open' : ''}`}>
- {getUsersActionJsx(roomId, [...typingMembers], 'typing...')} + + }} + /> +
diff --git a/src/app/organisms/room/RoomViewHeader.jsx b/src/app/organisms/room/RoomViewHeader.jsx index 849ba14bd..af4ae2c2d 100644 --- a/src/app/organisms/room/RoomViewHeader.jsx +++ b/src/app/organisms/room/RoomViewHeader.jsx @@ -29,6 +29,9 @@ import BackArrowIC from '../../../../public/res/ic/outlined/chevron-left.svg'; import { useForceUpdate } from '../../hooks/useForceUpdate'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function RoomViewHeader({ roomId }) { const [, forceUpdate] = useForceUpdate(); const mx = initMatrix.matrixClient; @@ -37,6 +40,8 @@ function RoomViewHeader({ roomId }) { avatarSrc = isDM ? mx.getRoom(roomId).getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 36, 36, 'crop') : avatarSrc; const roomName = mx.getRoom(roomId).name; + const { t } = useTranslation(); + const roomHeaderBtnRef = useRef(null); useEffect(() => { const settingsToggle = (isVisibile) => { @@ -93,17 +98,18 @@ function RoomViewHeader({ roomId }) { - toggleRoomSettings(tabText.SEARCH)} tooltip="Search" src={SearchIC} /> - - toggleRoomSettings(tabText.MEMBERS)} tooltip="Members" src={UserIC} /> + toggleRoomSettings(tabText.SEARCH)} tooltip={t("RoomViewHeader.search_tooltip")} src={SearchIC} /> + + toggleRoomSettings(tabText.MEMBERS)} tooltip={t("RoomViewHeader.members_tooltip")} src={UserIC} /> ); } + RoomViewHeader.propTypes = { roomId: PropTypes.string.isRequired, }; diff --git a/src/app/organisms/room/RoomViewInput.jsx b/src/app/organisms/room/RoomViewInput.jsx index 37e029893..1959a472d 100644 --- a/src/app/organisms/room/RoomViewInput.jsx +++ b/src/app/organisms/room/RoomViewInput.jsx @@ -30,6 +30,9 @@ import MarkdownIC from '../../../../public/res/ic/outlined/markdown.svg'; import FileIC from '../../../../public/res/ic/outlined/file.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + const CMD_REGEX = /(^\/|:|@)(\S*)$/; let isTyping = false; let isCmdActivated = false; @@ -41,6 +44,8 @@ function RoomViewInput({ const [isMarkdown, setIsMarkdown] = useState(settings.isMarkdown); const [replyTo, setReplyTo] = useState(null); + const { t } = useTranslation(); + const textAreaRef = useRef(null); const inputBaseRef = useRef(null); const uploadInputRef = useRef(null); @@ -81,7 +86,7 @@ function RoomViewInput({ function uploadingProgress(myRoomId, { loaded, total }) { if (myRoomId !== roomId) return; const progressPer = Math.round((loaded * 100) / total); - uploadProgressRef.current.textContent = `Uploading: ${bytesToSize(loaded)}/${bytesToSize(total)} (${progressPer}%)`; + uploadProgressRef.current.textContent = t("RoomViewInput.upload_progress", {progress: bytesToSize(loaded), total:bytesToSize(total), percent: progressPer}); inputBaseRef.current.style.backgroundImage = `linear-gradient(90deg, var(--bg-surface-hover) ${progressPer}%, var(--bg-surface-low) ${progressPer}%)`; } function clearAttachment(myRoomId) { @@ -311,8 +316,8 @@ function RoomViewInput({ { tombstoneEvent - ? tombstoneEvent.getContent()?.body ?? 'This room has been replaced and is no longer active.' - : 'You do not have permission to post to this room' + ? tombstoneEvent.getContent()?.body ?? t("RoomViewInput.tombstone_replaced") + : t("RoomViewInput.tombstone_permission_denied") } ); @@ -321,7 +326,7 @@ function RoomViewInput({ <>
- +
{roomTimeline.isEncrypted() && } @@ -333,7 +338,7 @@ function RoomViewInput({ onChange={handleMsgTyping} onPaste={handlePaste} onKeyDown={handleKeyDown} - placeholder="Send a message..." + placeholder={t("RoomViewInput.send_message_placeholder")} /> @@ -347,10 +352,10 @@ function RoomViewInput({ cords.y -= 250; openEmojiBoard(cords, addEmoji); }} - tooltip="Emoji" + tooltip={t("RoomViewInput.emoji_tooltip")} src={EmojiIC} /> - +
); @@ -368,7 +373,7 @@ function RoomViewInput({
{attachment.name} - {`size: ${bytesToSize(attachment.size)}`} + {t("RoomViewInput.file_size", {size: bytesToSize(attachment.size)})}
); @@ -383,7 +388,7 @@ function RoomViewInput({ setReplyTo(null); }} src={CrossIC} - tooltip="Cancel reply" + tooltip={t("RoomViewInput.cancel_reply_tooltip")} size="extra-small" /> - {twemojify(user)} - {' joined the room'} + }} + /> ); }, @@ -19,118 +28,145 @@ function getTimelineJSXMessages() { const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; return ( <> - {twemojify(user)} - {' left the room'} - {twemojify(reasonMsg)} + }} + /> ); }, invite(inviter, user) { return ( <> - {twemojify(inviter)} - {' invited '} - {twemojify(user)} + }} + /> ); }, cancelInvite(inviter, user) { return ( <> - {twemojify(inviter)} - {' canceled '} - {twemojify(user)} - {'\'s invite'} + }} + /> ); }, rejectInvite(user) { return ( <> - {twemojify(user)} - {' rejected the invitation'} + }} + /> ); }, kick(actor, user, reason) { - const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; + const reasonMsg = (typeof reason === 'string') ? `${reason}` : ''; return ( <> - {twemojify(actor)} - {' kicked '} - {twemojify(user)} - {twemojify(reasonMsg)} + }} + /> ); }, ban(actor, user, reason) { - const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; + const reasonMsg = (typeof reason === 'string') ? `${reason}` : ''; return ( <> - {twemojify(actor)} - {' banned '} - {twemojify(user)} - {twemojify(reasonMsg)} + }} + /> ); }, unban(actor, user) { return ( <> - {twemojify(actor)} - {' unbanned '} - {twemojify(user)} + }} + /> ); }, avatarSets(user) { return ( <> - {twemojify(user)} - {' set a avatar'} + }} + /> ); }, avatarChanged(user) { return ( <> - {twemojify(user)} - {' changed their avatar'} + }} + /> ); }, avatarRemoved(user) { return ( <> - {twemojify(user)} - {' removed their avatar'} + }} + /> ); }, nameSets(user, newName) { return ( <> - {twemojify(user)} - {' set display name to '} - {twemojify(newName)} + }} + /> ); }, nameChanged(user, newName) { return ( <> - {twemojify(user)} - {' changed their display name to '} - {twemojify(newName)} + }} + /> ); }, nameRemoved(user, lastName) { return ( <> - {twemojify(user)} - {' removed their display name '} - {twemojify(lastName)} + }} + /> ); }, diff --git a/src/app/organisms/search/Search.jsx b/src/app/organisms/search/Search.jsx index d40d8615e..14e8058c5 100644 --- a/src/app/organisms/search/Search.jsx +++ b/src/app/organisms/search/Search.jsx @@ -20,6 +20,9 @@ import RoomSelector from '../../molecules/room-selector/RoomSelector'; import SearchIC from '../../../../public/res/ic/outlined/search.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function useVisiblityToggle(setResult) { const [isOpen, setIsOpen] = useState(false); @@ -81,6 +84,8 @@ function Search() { const searchRef = useRef(null); const mx = initMatrix.matrixClient; + const { t } = useTranslation(); + const handleSearchResults = (chunk, term) => { setResult({ term, @@ -212,7 +217,7 @@ function Search() { @@ -224,7 +229,7 @@ function Search() {
- Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k + {t("Search.description")}
diff --git a/src/app/organisms/settings/AuthRequest.jsx b/src/app/organisms/settings/AuthRequest.jsx index ca07c2a22..3310ac30a 100644 --- a/src/app/organisms/settings/AuthRequest.jsx +++ b/src/app/organisms/settings/AuthRequest.jsx @@ -12,6 +12,9 @@ import Spinner from '../../atoms/spinner/Spinner'; import { useStore } from '../../hooks/useStore'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + let lastUsedPassword; const getAuthId = (password) => ({ type: 'm.login.password', @@ -25,6 +28,7 @@ const getAuthId = (password) => ({ function AuthRequest({ onComplete, makeRequest }) { const [status, setStatus] = useState(false); const mountStore = useStore(); + const { t } = useTranslation(); const handleForm = async (e) => { mountStore.setItem(true); @@ -41,10 +45,10 @@ function AuthRequest({ onComplete, makeRequest }) { lastUsedPassword = undefined; if (!mountStore.getItem()) return; if (err.errcode === 'M_FORBIDDEN') { - setStatus({ error: 'Wrong password. Please enter correct password.' }); + setStatus({ error: t("AuthRequest.wrong_password") }); return; } - setStatus({ error: 'Request failed!' }); + setStatus({ error: t("AuthRequest.request_failed") }); } }; @@ -57,14 +61,14 @@ function AuthRequest({ onComplete, makeRequest }) {
{status.ongoing && } {status.error && {status.error}} - {(status === false || status.error) && } + {(status === false || status.error) && }
); diff --git a/src/app/organisms/settings/CrossSigning.jsx b/src/app/organisms/settings/CrossSigning.jsx index 9213e9dae..138282acb 100644 --- a/src/app/organisms/settings/CrossSigning.jsx +++ b/src/app/organisms/settings/CrossSigning.jsx @@ -19,57 +19,66 @@ import SettingTile from '../../molecules/setting-tile/SettingTile'; import { authRequest } from './AuthRequest'; import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus'; -const failedDialog = () => { - const renderFailure = (requestClose) => ( -
- {twemojify('❌')} - Failed to setup cross signing. Please try again. - -
- ); - openReusableDialog( - Setup cross signing, - renderFailure, - ); -}; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; -const securityKeyDialog = (key) => { - const downloadKey = () => { - const blob = new Blob([key.encodedPrivateKey], { - type: 'text/plain;charset=us-ascii', - }); - FileSaver.saveAs(blob, 'security-key.txt'); - }; - const copyKey = () => { - copyToClipboard(key.encodedPrivateKey); + +function CrossSigningSetup() { + + const { t } = useTranslation(); + + const initialValues = { phrase: '', confirmPhrase: '' }; + const [genWithPhrase, setGenWithPhrase] = useState(undefined); + + const failedDialog = () => { + const renderFailure = (requestClose) => ( +
+ {twemojify('❌')} + {t("CrossSigning.setup_failed")} + +
+ ); + + openReusableDialog( + {t("CrossSigning.setup")}, + renderFailure, + ); }; - const renderSecurityKey = () => ( -
- Please save this security key somewhere safe. - - {key.encodedPrivateKey} - -
- - + const securityKeyDialog = (key) => { + const downloadKey = () => { + const blob = new Blob([key.encodedPrivateKey], { + type: 'text/plain;charset=us-ascii', + }); + FileSaver.saveAs(blob, 'security-key.txt'); + }; + const copyKey = () => { + copyToClipboard(key.encodedPrivateKey); + }; + + const renderSecurityKey = () => ( +
+ {t("CrossSigning.save_security_key_message")} + + {key.encodedPrivateKey} + +
+ + +
-
- ); + ); - // Download automatically. - downloadKey(); + // Download automatically. + downloadKey(); - openReusableDialog( - Security Key, - () => renderSecurityKey(), - ); -}; + openReusableDialog( + {t("CrossSigning.security_key_dialog_title")}, + () => renderSecurityKey(), + ); + }; -function CrossSigningSetup() { - const initialValues = { phrase: '', confirmPhrase: '' }; - const [genWithPhrase, setGenWithPhrase] = useState(undefined); const setup = async (securityPhrase = undefined) => { const mx = initMatrix.matrixClient; @@ -121,13 +130,12 @@ function CrossSigningSetup() {
- We will generate a Security Key, - which you can use to manage messages backup and session verification. + {t("CrossSigning.security_key_generation_message")} - {genWithPhrase !== false && } + {genWithPhrase !== false && } {genWithPhrase === false && }
- OR + {t("common.or")} setup(values.phrase)} @@ -142,15 +150,13 @@ function CrossSigningSetup() { disabled={genWithPhrase !== undefined} > - Alternatively you can also set a Security Phrase - so you don't have to remember long Security Key, - and optionally save the Key as backup. + {t("CrossSigning.security_phrase_message")} {errors.confirmPhrase && {errors.confirmPhrase}} - {genWithPhrase !== true && } + {genWithPhrase !== true && } {genWithPhrase === true && } )} @@ -180,41 +186,41 @@ const setupDialog = () => { Setup cross signing, () => , ); -}; +} function CrossSigningReset() { + const { t } = useTranslation(); return (
{twemojify('✋🧑‍🚒🤚')} - Resetting cross-signing keys is permanent. + {t("CrossSigning.reset_keys_subtitle")} - Anyone you have verified with will see security alerts and your message backup will lost. - You almost certainly do not want to do this, - unless you have lost Security Key or Phrase and - every session you can cross-sign from. + {t("CrossSigning.reset_keys_message")} - +
); } const resetDialog = () => { + openReusableDialog( Reset cross signing, () => , ); -}; +} function CrossSignin() { + const { t } = useTranslation(); const isCSEnabled = useCrossSigningStatus(); return ( Setup to verify and keep track of all your sessions. Also required to backup encrypted message.} + title={t("CrossSigning.title")} + content={{t("CrossSigning.setup_message")}} options={( isCSEnabled - ? - : + ? + : )} /> ); diff --git a/src/app/organisms/settings/DeviceManage.jsx b/src/app/organisms/settings/DeviceManage.jsx index 062ec0211..175ca1b79 100644 --- a/src/app/organisms/settings/DeviceManage.jsx +++ b/src/app/organisms/settings/DeviceManage.jsx @@ -27,9 +27,15 @@ import { useDeviceList } from '../../hooks/useDeviceList'; import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus'; import { accessSecretStorage } from './SecretStorageAccess'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + const promptDeviceName = async (deviceName) => new Promise((resolve) => { let isCompleted = false; + const { t } = useTranslation(); + const renderContent = (onComplete) => { const handleSubmit = (e) => { e.preventDefault(); @@ -39,17 +45,17 @@ const promptDeviceName = async (deviceName) => new Promise((resolve) => { }; return (
- +
- - + +
); }; openReusableDialog( - Edit session name, + {t("DeviceManage.edit_session_name_title")}, (requestClose) => renderContent((name) => { isCompleted = true; resolve(name); @@ -76,6 +82,41 @@ function DeviceManage() { setProcessing([]); }, [deviceList]); + const { t } = useTranslation(); + + const promptDeviceName = async (deviceName) => new Promise((resolve) => { + let isCompleted = false; + const renderContent = (onComplete) => { + const handleSubmit = (e) => { + e.preventDefault(); + const name = e.target.session.value; + if (typeof name !== 'string') onComplete(null); + onComplete(name); + }; + return ( +
+ +
+ + +
+
+ ); + }; + + openReusableDialog( + {t("DeviceManage.edit_session_name_title")}, + (requestClose) => renderContent((name) => { + isCompleted = true; + resolve(name); + requestClose(); + }), + () => { + if (!isCompleted) resolve(null); + }, + ); + }); + const addToProcessing = (device) => { const old = [...processing]; old.push(device.device_id); @@ -91,7 +132,7 @@ function DeviceManage() {
- Loading devices... + {t("DeviceManage.loading_devices")}
); @@ -114,14 +155,14 @@ function DeviceManage() { const handleRemove = async (device) => { const isConfirmed = await confirmDialog( - `Logout ${device.display_name}`, - `You are about to logout "${device.display_name}" session.`, - 'Logout', + t("DeviceManage.logout_device_title", {device: device.display_name}), + t("DeviceManage.logout_device_message", {device: device.display_name}), + t("DeviceManage.logout_device_confirm"), 'danger', ); if (!isConfirmed) return; addToProcessing(device); - await authRequest(`Logout "${device.display_name}"`, async (auth) => { + await authRequest(t("DeviceManage.logout_device_title", {device: device.display_name}), async (auth) => { await mx.deleteDevice(device.device_id, auth); }); @@ -130,7 +171,7 @@ function DeviceManage() { }; const verifyWithKey = async (device) => { - const keyData = await accessSecretStorage('Session verification'); + const keyData = await accessSecretStorage(t("DeviceManage.session_verification_title")); if (!keyData) return; addToProcessing(device); await mx.checkOwnCrossSigningTrust(); @@ -164,7 +205,7 @@ function DeviceManage() { {displayName} {`${displayName ? ' — ' : ''}${deviceId}`} - {isCurrentDevice && Current} + {isCurrentDevice && {t("DeviceManage.current_device_label")}} )} options={ @@ -172,9 +213,9 @@ function DeviceManage() { ? : ( <> - {(isCSEnabled && canVerify) && } - handleRename(device)} src={PencilIC} tooltip="Rename" /> - handleRemove(device)} src={BinIC} tooltip="Remove session" /> + {(isCSEnabled && canVerify) && } + handleRename(device)} src={PencilIC} tooltip={t("DeviceManage.edit_session_name_tooltip")} /> + handleRemove(device)} src={BinIC} tooltip={t("DeviceManage.logout_device_tooltip")}/> ) } @@ -211,49 +252,50 @@ function DeviceManage() { noEncryption.push(device); } }); + return (
- Unverified sessions + {t("DeviceManage.unverified_sessions_title")} {!isCSEnabled && (
)} { unverified.length > 0 ? unverified.map((device) => renderDevice(device, false)) - : No unverified sessions + : {t("DeviceManage.unverified_sessions_none")} }
{noEncryption.length > 0 && (
- Sessions without encryption support + {t("DeviceManage.unencrypted_sessions_title")} {noEncryption.map((device) => renderDevice(device, null))}
)}
- Verified sessions + {t("DeviceManage.verified_sessions_title")} { verified.length > 0 ? verified.map((device, index) => { if (truncated && index >= TRUNCATED_COUNT) return null; return renderDevice(device, true); }) - : No verified sessions + : {t("DeviceManage.verified_sessions_none")} } { verified.length > TRUNCATED_COUNT && ( )} { deviceList.length > 0 && ( - Session names are visible to everyone, so do not put any private info here. + {t("DeviceManage.session_name_privacy_message")} )}
diff --git a/src/app/organisms/settings/KeyBackup.jsx b/src/app/organisms/settings/KeyBackup.jsx index 75f032bc3..dd5d257b6 100644 --- a/src/app/organisms/settings/KeyBackup.jsx +++ b/src/app/organisms/settings/KeyBackup.jsx @@ -24,11 +24,17 @@ import DownloadIC from '../../../../public/res/ic/outlined/download.svg'; import { useStore } from '../../hooks/useStore'; import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + function CreateKeyBackupDialog({ keyData }) { const [done, setDone] = useState(false); const mx = initMatrix.matrixClient; const mountStore = useStore(); + const { t } = useTranslation(); + const doBackup = async () => { setDone(false); let info; @@ -60,19 +66,19 @@ function CreateKeyBackupDialog({ keyData }) { {done === false && (
- Creating backup... + {t("KeyBackup.creating_backup")}
)} {done === true && ( <> {twemojify('✅')} - Successfully created backup + {t("KeyBackup.backup_created")} )} {done === null && ( <> - Failed to create backup - + {t("KeyBackup.backup_failed")} + )}
@@ -83,6 +89,9 @@ CreateKeyBackupDialog.propTypes = { }; function RestoreKeyBackupDialog({ keyData }) { + + const { t } = useTranslation(); + const [status, setStatus] = useState(false); const mx = initMatrix.matrixClient; const mountStore = useStore(); @@ -99,7 +108,7 @@ function RestoreKeyBackupDialog({ keyData }) { meBreath = true; }, 200); - setStatus({ message: `Restoring backup keys... (${progress.successes}/${progress.total})` }); + setStatus({ message: t("KeyBackup.restoring_progress", {progress: progress.successes, total: progress.total}) }); }; try { @@ -111,14 +120,14 @@ function RestoreKeyBackupDialog({ keyData }) { { progressCallback }, ); if (!mountStore.getItem()) return; - setStatus({ done: `Successfully restored backup keys (${info.imported}/${info.total}).` }); + setStatus({ done: t("KeyBackup.restore_complete", {progress: info.imported, total: info.total})}); } catch (e) { if (!mountStore.getItem()) return; if (e.errcode === 'RESTORE_BACKUP_ERROR_BAD_KEY') { deletePrivateKey(keyData.keyId); - setStatus({ error: 'Failed to restore backup. Key is invalid!', errorCode: 'BAD_KEY' }); + setStatus({ error: t("KeyBackup.restore_failed_bad_key"), errorCode: 'BAD_KEY' }); } else { - setStatus({ error: 'Failed to restore backup.', errCode: 'UNKNOWN' }); + setStatus({ error: t("KeyBackup.restore_failed_unknown"), errCode: 'UNKNOWN' }); } } }; @@ -133,7 +142,7 @@ function RestoreKeyBackupDialog({ keyData }) { {(status === false || status.message) && (
- {status.message ?? 'Restoring backup keys...'} + {status.message ?? t("KeyBackup.restoring")}
)} {status.done && ( @@ -145,7 +154,7 @@ function RestoreKeyBackupDialog({ keyData }) { {status.error && ( <> {status.error} - + )}
@@ -159,6 +168,7 @@ function DeleteKeyBackupDialog({ requestClose }) { const [isDeleting, setIsDeleting] = useState(false); const mx = initMatrix.matrixClient; const mountStore = useStore(); + const { t } = useTranslation(); const deleteBackup = async () => { mountStore.setItem(true); @@ -177,12 +187,12 @@ function DeleteKeyBackupDialog({ requestClose }) { return (
{twemojify('🗑')} - Deleting key backup is permanent. - All encrypted messages keys stored on server will be deleted. + {t("KeyBackup.delete_key_backup_subtitle")} + {t("KeyBackup.delete_key_backup_message")} { isDeleting ? - : + : }
); @@ -196,6 +206,7 @@ function KeyBackup() { const isCSEnabled = useCrossSigningStatus(); const [keyBackup, setKeyBackup] = useState(undefined); const mountStore = useStore(); + const { t } = useTranslation(); const fetchKeyBackupVersion = async () => { const info = await mx.getKeyBackupVersion(); @@ -220,28 +231,28 @@ function KeyBackup() { }, [isCSEnabled]); const openCreateKeyBackup = async () => { - const keyData = await accessSecretStorage('Create Key Backup'); + const keyData = await accessSecretStorage(t('KeyBackup.create_backup_title')); if (keyData === null) return; openReusableDialog( - Create Key Backup, + {t('KeyBackup.create_backup_title')}, () => , () => fetchKeyBackupVersion(), ); }; const openRestoreKeyBackup = async () => { - const keyData = await accessSecretStorage('Restore Key Backup'); + const keyData = await accessSecretStorage(t('KeyBackup.restore_backup_title')); if (keyData === null) return; openReusableDialog( - Restore Key Backup, + {t('KeyBackup.restore_backup_title')}, () => , ); }; const openDeleteKeyBackup = () => openReusableDialog( - Delete Key Backup, + {t('KeyBackup.delete_key_backup_title')}, (requestClose) => ( { @@ -254,28 +265,28 @@ function KeyBackup() { const renderOptions = () => { if (keyBackup === undefined) return ; - if (keyBackup === null) return ; + if (keyBackup === null) return ; return ( <> - - + + ); }; return ( - Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key. + {t("KeyBackup.encrypted_messages_backup_description")} {!isCSEnabled && ( )} diff --git a/src/app/organisms/settings/SecretStorageAccess.jsx b/src/app/organisms/settings/SecretStorageAccess.jsx index e4fceb078..51f47cb94 100644 --- a/src/app/organisms/settings/SecretStorageAccess.jsx +++ b/src/app/organisms/settings/SecretStorageAccess.jsx @@ -13,6 +13,11 @@ import Button from '../../atoms/button/Button'; import Input from '../../atoms/input/Input'; import Spinner from '../../atoms/spinner/Spinner'; + +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + import { useStore } from '../../hooks/useStore'; function SecretStorageAccess({ onComplete }) { @@ -24,6 +29,7 @@ function SecretStorageAccess({ onComplete }) { const [process, setProcess] = useState(false); const [error, setError] = useState(null); const mountStore = useStore(); + const { t } = useTranslation(); const toggleWithPhrase = () => setWithPhrase(!withPhrase); @@ -39,7 +45,7 @@ function SecretStorageAccess({ onComplete }) { if (!mountStore.getItem()) return; if (!isCorrect) { - setError(`Incorrect Security ${key ? 'Key' : 'Phrase'}`); + setError(t(key ? "SecretStorageAccess.incorrect_security_key" : "SecretStorageAccess.incorrect_security_phrase")); setProcess(false); return; } @@ -51,7 +57,7 @@ function SecretStorageAccess({ onComplete }) { }); } catch (e) { if (!mountStore.getItem()) return; - setError(`Incorrect Security ${key ? 'Key' : 'Phrase'}`); + setError(t(key ? "SecretStorageAccess.incorrect_security_key" : "SecretStorageAccess.incorrect_security_phrase")); setProcess(false); } }; @@ -76,7 +82,7 @@ function SecretStorageAccess({ onComplete }) {
{error}} {!process && (
- - {isPassphrase && } + + {isPassphrase && }
)}
diff --git a/src/app/organisms/settings/Settings.jsx b/src/app/organisms/settings/Settings.jsx index 82b948adf..bc8f5b163 100644 --- a/src/app/organisms/settings/Settings.jsx +++ b/src/app/organisms/settings/Settings.jsx @@ -40,34 +40,39 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CinnySVG from '../../../../public/res/svg/cinny.svg'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + function AppearanceSection() { const [, updateState] = useState({}); + const { t } = useTranslation(); + return (
Theme { toggleSystemTheme(); updateState({}); }} /> )} - content={Use light or dark mode based on the system settings.} + content={{t("Settings.theme.follow_system.description")}} /> {!settings.useSystemTheme && ( settings.setTheme(index)} /> @@ -78,34 +83,34 @@ function AppearanceSection() {
Room messages { toggleMarkdown(); updateState({}); }} /> )} - content={Format messages with markdown syntax before sending.} + content={{t("Settings.markdown.description")}} /> { toggleMembershipEvents(); updateState({}); }} /> )} - content={Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)} + content={{t("Settings.hide_membership_events.description")}} /> { toggleNickAvatarEvents(); updateState({}); }} /> )} - content={Hide nick and avatar change messages from room timeline.} + content={{t("Settings.hide_nickname_avatar_events.description")}} />
@@ -117,9 +122,11 @@ function NotificationsSection() { const [, updateState] = useState({}); + const { t } = useTranslation(); + const renderOptions = () => { if (window.Notification === undefined) { - return Not supported in this browser.; + return {t("errors.browser_not_supported")}; } if (permission === 'granted') { @@ -147,51 +154,54 @@ function NotificationsSection() { return (
- Notification & Sound + {t("Settings.notifications_and_sound.title")} Show desktop notification when new messages arrive.} + content={{t("Settings.notifications_and_sound.desktop.description")}} /> { toggleNotificationSounds(); updateState({}); }} /> )} - content={Play sound when new messages arrive.} + content={{t("Settings.notifications_and_sound.desktop.description")}} />
); } function SecuritySection() { + + const { t } = useTranslation(); + return (
- Cross signing and backup + {t("Settings.security.cross_signing.title")}
- Export/Import encryption keys + {t("Settings.security.export_import_encryption_keys.title")} - Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing. + {t("Settings.security.export_encryption_keys.description")} )} /> - {'To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\'ll have to enter the password you set in order to decrypt it.'} + {t("Settings.security.import_encryption_keys.description")} )} @@ -202,28 +212,31 @@ function SecuritySection() { } function AboutSection() { + + const { t } = useTranslation(); + return (
- Application + {t("Settings.about.application")}
Cinny logo
- Cinny + {t("common.cinny")} {`v${cons.version}`} - Yet another matrix client + {t("common.slogan")}
- - + +
- Credits + {t("Settings.about.credits")}
  • @@ -297,9 +310,11 @@ function Settings() { const [selectedTab, setSelectedTab] = useState(tabItems[0]); const [isOpen, requestClose] = useWindowToggle(setSelectedTab); + const { t } = useTranslation(); + const handleTabChange = (tabItem) => setSelectedTab(tabItem); const handleLogout = async () => { - if (await confirmDialog('Logout', 'Are you sure that you want to logout your session?', 'Logout', 'danger')) { + if (await confirmDialog(t("Settings.logout.dialog.title"), t("Settings.logout.dialog.description"), t("Settings.logout.dialog.confirm"), 'danger')) { logout(); } }; @@ -308,13 +323,13 @@ function Settings() { Settings} + title={{t("Settings.title")}} contentOptions={( <> - + )} onRequestClose={requestClose} diff --git a/src/app/organisms/shortcut-spaces/ShortcutSpaces.jsx b/src/app/organisms/shortcut-spaces/ShortcutSpaces.jsx index 62ec76a31..6ede460d5 100644 --- a/src/app/organisms/shortcut-spaces/ShortcutSpaces.jsx +++ b/src/app/organisms/shortcut-spaces/ShortcutSpaces.jsx @@ -22,6 +22,10 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import { useSpaceShortcut } from '../../hooks/useSpaceShortcut'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + function ShortcutSpacesContent() { const mx = initMatrix.matrixClient; const { spaces, roomIdToParents } = initMatrix.roomList; @@ -73,6 +77,8 @@ function ShortcutSpacesContent() { const toggleSelected = () => toggleSelection(spaceId); const deleteShortcut = () => deleteSpaceShortcut(spaceId); + const { t } = useTranslation(); + return ( - Pinned spaces - {spaceShortcut.length === 0 && No pinned spaces} + {t("ShortcutSpaces.pinned_spaces")} + {spaceShortcut.length === 0 && {t("ShortcutSpaces.no_pinned_spaces")}} {spaceShortcut.map((spaceId) => renderSpace(spaceId, true))} - Unpinned spaces - {spaceWithoutShortcut.length === 0 && No unpinned spaces} + {t("ShortcutSpaces.unpinned_spaces")} + {spaceWithoutShortcut.length === 0 && {t("ShortcutSpaces.no_unpinned_spaces")}} {spaceWithoutShortcut.map((spaceId) => renderSpace(spaceId, false))} {selected.length !== 0 && (
    {process && } - {process || `${selected.length} spaces selected`} + {process || t("ShortcutSpaces.spaces_selected", {count: selected.length})} { !process && ( - + )}
    )} @@ -144,6 +152,7 @@ function useVisibilityToggle() { function ShortcutSpaces() { const [isOpen, requestClose] = useVisibilityToggle(); + const { t } = useTranslation(); return ( - Pin spaces + {t("ShortcutSpaces.header")} )} - contentOptions={} + contentOptions={} onRequestClose={requestClose} > { diff --git a/src/app/organisms/space-manage/SpaceManage.jsx b/src/app/organisms/space-manage/SpaceManage.jsx index cf042da46..e2c035c14 100644 --- a/src/app/organisms/space-manage/SpaceManage.jsx +++ b/src/app/organisms/space-manage/SpaceManage.jsx @@ -32,6 +32,10 @@ import InfoIC from '../../../../public/res/ic/outlined/info.svg'; import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useStore } from '../../hooks/useStore'; +import '../../i18n.jsx' +import { useTranslation } from 'react-i18next'; + + function SpaceManageBreadcrumb({ path, onSelect }) { return (
    @@ -75,6 +79,8 @@ function SpaceManageItem({ const canManage = parentRoom?.currentState.maySendStateEvent('m.space.child', mx.getUserId()) || false; const isSuggested = parentRoom?.currentState.getStateEvents('m.space.child', roomId)?.getContent().suggested === true; + const { t } = useTranslation(); + const room = mx.getRoom(roomId); const isJoined = !!(room?.getMyMembership() === 'join' || null); const name = room?.name || roomInfo.name || roomInfo.canonical_alias || roomId; @@ -114,7 +120,7 @@ function SpaceManageItem({ const roomNameJSX = ( {twemojify(name)} - {` • ${roomInfo.num_joined_members} members`} + • {t("SpaceManage.room_members", {count: roomInfo.num_joined_members})} ); @@ -142,19 +148,21 @@ function SpaceManageItem({ > {roomAvatarJSX} {roomNameJSX} - {isSuggested && Suggested} + {isSuggested && {t("SpaceManage.suggested")}} {roomInfo.topic && expandBtnJsx} { isJoined - ? - : + ? + : }
    {isExpand && roomInfo.topic && {twemojify(roomInfo.topic, undefined, true)}}
); } + + SpaceManageItem.propTypes = { parentId: PropTypes.string.isRequired, roomHierarchy: PropTypes.shape({}).isRequired, @@ -171,21 +179,23 @@ function SpaceManageFooter({ parentId, selected }) { const room = mx.getRoom(parentId); const { currentState } = room; + const { t } = useTranslation(); + const allSuggested = selected.every((roomId) => { const sEvent = currentState.getStateEvents('m.space.child', roomId); return !!sEvent?.getContent()?.suggested; }); const handleRemove = () => { - setProcess(`Removing ${selected.length} items`); + setProcess(t("SpaceManage.remove", {count: selected.length})); selected.forEach((roomId) => { mx.sendStateEvent(parentId, 'm.space.child', {}, roomId); }); }; const handleToggleSuggested = (isMark) => { - if (isMark) setProcess(`Marking as suggested ${selected.length} items`); - else setProcess(`Marking as not suggested ${selected.length} items`); + if (isMark) setProcess(t("SpaceManage.mark_suggested", {count: selected.length})); + else setProcess(t("SpaceManage.mark_not_suggested", {count: selected.length})); selected.forEach((roomId) => { const sEvent = room.currentState.getStateEvents('m.space.child', roomId); if (!sEvent) return; @@ -200,15 +210,15 @@ function SpaceManageFooter({ parentId, selected }) { return (
{process && } - {process || `${selected.length} item selected`} + {process || t("SpaceManage.items_selected", {count: selected.length})} { !process && ( <> - + )} @@ -282,6 +292,9 @@ function useChildUpdate(roomId, roomsHierarchy) { } function SpaceManageContent({ roomId, requestClose }) { + + const { t } = useTranslation(); + const mx = initMatrix.matrixClient; useUpdateOnJoin(roomId); const [, forceUpdate] = useForceUpdate(); @@ -339,11 +352,11 @@ function SpaceManageContent({ roomId, requestClose }) { {spacePath.length > 1 && ( )} - Rooms and spaces + {t("SpaceManage.rooms_and_spaces")}
{!isLoading && currentHierarchy?.rooms?.length === 1 && ( - Either the space contains private rooms or you need to join space to view it's rooms. + {t("SpaceManage.private_rooms_message")} )} {currentHierarchy && (currentHierarchy.rooms?.map((roomInfo) => ( @@ -362,15 +375,15 @@ function SpaceManageContent({ roomId, requestClose }) { /> ) )))} - {!currentHierarchy && loading...} + {!currentHierarchy && {t("common.loading")}}
{currentHierarchy?.canLoadMore && !isLoading && ( - + )} {isLoading && (
- Loading rooms... + {t("common.loading")}
)} {selected.length > 0 && ( @@ -406,6 +419,8 @@ function SpaceManage() { const [roomId, requestClose] = useWindowToggle(); const room = mx.getRoom(roomId); + const { t } = useTranslation(); + return ( {roomId && twemojify(room.name)} - — manage rooms + — {t("SpaceManage.subtitle")} )} contentOptions={} diff --git a/src/app/organisms/space-settings/SpaceSettings.jsx b/src/app/organisms/space-settings/SpaceSettings.jsx index 8ac561ee8..89ba468d9 100644 --- a/src/app/organisms/space-settings/SpaceSettings.jsx +++ b/src/app/organisms/space-settings/SpaceSettings.jsx @@ -83,7 +83,7 @@ function GeneralSettings({ roomId }) { }} iconSrc={isCategorized ? CategoryFilledIC : CategoryIC} > - {isCategorized ? t("space_settings.uncategorize_subspaces") : t("space_settings.categorize_subspaces")} + {isCategorized ? t("SpaceSettings.uncategorize_subspaces") : t("SpaceSettings.categorize_subspaces")} { @@ -93,30 +93,30 @@ function GeneralSettings({ roomId }) { }} iconSrc={isPinned ? PinFilledIC : PinIC} > - {isPinned ? t("space_settings.unpin_sidebar") : t("space_settings.pin_sidebar")} + {isPinned ? t("SpaceSettings.unpin_sidebar") : t("SpaceSettings.pin_sidebar")} { const isConfirmed = await confirmDialog( - t("space_settings.leave.leave_dialog_title"), - t("space_settings.leave.leave_dialog_message", {space: roomName}), - t("space_settings.leave.leave_space"), + t("SpaceSettings.leave.leave_dialog_title"), + t("SpaceSettings.leave.leave_dialog_message", {space: roomName}), + t("SpaceSettings.leave.leave_space"), 'danger', ); if (isConfirmed) leave(roomId); }} iconSrc={LeaveArrowIC} > - {t("space_settings.leave.leave_space")} + {t("SpaceSettings.leave.leave_space")}
- {t("space_settings.visibility.header")} + {t("SpaceSettings.visibility.header")}
- {t("space_settings.addresses.header")} + {t("SpaceSettings.addresses.header")}
@@ -169,7 +169,7 @@ function SpaceSettings() { title={( {isOpen && twemojify(room.name)} - — {t("space_settings.subtitle")} + — {t("SpaceSettings.subtitle")} )} contentOptions={} diff --git a/src/app/organisms/view-source/ViewSource.jsx b/src/app/organisms/view-source/ViewSource.jsx index d117d1673..deb8b9262 100644 --- a/src/app/organisms/view-source/ViewSource.jsx +++ b/src/app/organisms/view-source/ViewSource.jsx @@ -56,15 +56,15 @@ function ViewSource() { const renderViewSource = () => (
- {event.isEncrypted() && } - + {event.isEncrypted() && } +
); return ( setIsOpen(false)} contentOptions={ setIsOpen(false)} tooltip={t("common.close")} />} diff --git a/src/app/organisms/welcome/Welcome.jsx b/src/app/organisms/welcome/Welcome.jsx index 008345b77..1b17db605 100644 --- a/src/app/organisms/welcome/Welcome.jsx +++ b/src/app/organisms/welcome/Welcome.jsx @@ -16,8 +16,8 @@ function Welcome() {
Cinny logo - {t('welcome.heading')} - {t('welcome.subheading')} + {t('Welcome.heading')} + {t('Welcome.subheading')}
); From 09c7aa95895be6bd557774a63a097576c0dd689f Mon Sep 17 00:00:00 2001 From: Dylan Van Nielen Date: Thu, 14 Jul 2022 11:45:50 +0930 Subject: [PATCH 05/21] Added "Organisms." prefix to translations for organims --- public/locales/en/translation.json | 771 +++++++++--------- src/app/organisms/create-room/CreateRoom.jsx | 34 +- src/app/organisms/drag-drop/DragDrop.jsx | 2 +- .../emoji-verification/EmojiVerification.jsx | 16 +- src/app/organisms/invite-list/InviteList.jsx | 16 +- src/app/organisms/invite-user/InviteUser.jsx | 20 +- src/app/organisms/join-alias/JoinAlias.jsx | 14 +- .../organisms/navigation/DrawerBreadcrumb.jsx | 2 +- src/app/organisms/navigation/DrawerHeader.jsx | 20 +- src/app/organisms/navigation/SideBar.jsx | 14 +- .../profile-editor/ProfileEditor.jsx | 6 +- .../profile-viewer/ProfileViewer.jsx | 26 +- .../organisms/public-rooms/PublicRooms.jsx | 24 +- src/app/organisms/room/RoomSettings.jsx | 18 +- src/app/organisms/room/RoomViewContent.jsx | 10 +- src/app/organisms/room/RoomViewFloating.jsx | 8 +- src/app/organisms/room/RoomViewHeader.jsx | 6 +- src/app/organisms/room/RoomViewInput.jsx | 14 +- src/app/organisms/room/common.jsx | 28 +- src/app/organisms/search/Search.jsx | 2 +- src/app/organisms/settings/AuthRequest.jsx | 6 +- src/app/organisms/settings/CrossSigning.jsx | 28 +- src/app/organisms/settings/DeviceManage.jsx | 42 +- src/app/organisms/settings/KeyBackup.jsx | 42 +- src/app/organisms/settings/Settings.jsx | 58 +- .../shortcut-spaces/ShortcutSpaces.jsx | 14 +- .../organisms/space-manage/SpaceManage.jsx | 20 +- .../space-settings/SpaceSettings.jsx | 18 +- src/app/organisms/view-source/ViewSource.jsx | 6 +- src/app/organisms/welcome/Welcome.jsx | 4 +- 30 files changed, 645 insertions(+), 644 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 60aaae8ef..76f1903a0 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -1,5 +1,5 @@ { - "common" : { + "common": { "close": "Close", "open": "Open", "leave": "Leave", @@ -41,402 +41,403 @@ "browser_not_supported": "Not supported in this browser", "generic": "Something went wrong!" }, - "Welcome": { - "heading": "Welcome to Cinny!", - "subheading": "Yet another Matrix client" - }, - "ViewSource":{ - "title": "View Source", - "original_source": "Original source", - "decrypted_source": "Decrypted source" - }, - "SpaceManage": { - "subtitle": "manage rooms", - "load_more": "Load more", - "rooms_and_spaces": "Rooms and spaces", - "private_rooms_message": "Either the space contains private rooms or you need to join space to view it's rooms.", - "items_selected_zero": "No selected items", - "items_selected_one": "{{count}} selected item", - "items_selected_other": "{{count}} selected items", - "room_members_zero": "No room members", - "room_members_one": "{{count}} room member", - "room_members_other": "{{count}} room members", - "mark_suggested_zero": "Marking no rooms as suggested", - "mark_suggested_one": "Marking {{count}} room as suggested", - "mark_suggested_other": "Marking {{count}} rooms as suggested", - "mark_not_suggested_zero": "Marking no rooms as suggested", - "mark_not_suggested_one": "Marking {{count}} room as suggested", - "mark_not_suggested_other": "Marking {{count}} rooms as suggested", - "remove_zero": "Removing no items", - "remove_one": "Removing {{count}} item", - "remove_other": "Removing {{count}} items.", - "suggested": "Suggested", - "mark_as_suggested":"Mark as suggested", - "mark_as_not_suggested": "Mark as not suggested" - }, - "SpaceSettings":{ - "subtitle": "space settings", - "leave":{ - "leave_space": "Leave Space", - "leave_dialog_title": "Leave Space", - "leave_dialog_message": "Are you sure you want to leave {{space}}?" + "Organisms": { + "Welcome": { + "heading": "Welcome to Cinny!", + "subheading": "Yet another Matrix client" }, - "visibility":{ - "header": "Space visibility (who can join)" + "ViewSource": { + "title": "View Source", + "original_source": "Original source", + "decrypted_source": "Decrypted source" }, - "addresses": { - "header": "Space addresses" + "SpaceManage": { + "subtitle": "manage rooms", + "load_more": "Load more", + "rooms_and_spaces": "Rooms and spaces", + "private_rooms_message": "Either the space contains private rooms or you need to join space to view it's rooms.", + "items_selected_zero": "No selected items", + "items_selected_one": "{{count}} selected item", + "items_selected_other": "{{count}} selected items", + "room_members_zero": "No room members", + "room_members_one": "{{count}} room member", + "room_members_other": "{{count}} room members", + "mark_suggested_zero": "Marking no rooms as suggested", + "mark_suggested_one": "Marking {{count}} room as suggested", + "mark_suggested_other": "Marking {{count}} rooms as suggested", + "mark_not_suggested_zero": "Marking no rooms as suggested", + "mark_not_suggested_one": "Marking {{count}} room as suggested", + "mark_not_suggested_other": "Marking {{count}} rooms as suggested", + "remove_zero": "Removing no items", + "remove_one": "Removing {{count}} item", + "remove_other": "Removing {{count}} items.", + "suggested": "Suggested", + "mark_as_suggested": "Mark as suggested", + "mark_as_not_suggested": "Mark as not suggested" }, - "categorize_subspaces": "Categorize subspaces", - "uncategorize_subspaces": "Uncategorize subspaces", - "pin_sidebar": "Pin to sidebar", - "unpin_sidebar": "Unpin from sidebar" - }, - "Settings": { - "title": "Settings", - "theme": { - "follow_system": { - "title": "Follow system theme", - "description": "Use light or dark mode based on the system settings." + "SpaceSettings": { + "subtitle": "space settings", + "leave": { + "leave_space": "Leave Space", + "leave_dialog_title": "Leave Space", + "leave_dialog_message": "Are you sure you want to leave {{space}}?" + }, + "visibility": { + "header": "Space visibility (who can join)" + }, + "addresses": { + "header": "Space addresses" + }, + "categorize_subspaces": "Categorize subspaces", + "uncategorize_subspaces": "Uncategorize subspaces", + "pin_sidebar": "Pin to sidebar", + "unpin_sidebar": "Unpin from sidebar" + }, + "Settings": { + "title": "Settings", + "theme": { + "follow_system": { + "title": "Follow system theme", + "description": "Use light or dark mode based on the system settings." + }, + "title": "Theme", + "theme_light": "Light", + "theme_silver": "Silver", + "theme_dark": "Dark", + "theme_butter": "Butter" + }, + "markdown": { + "title": "Markdown formatting", + "description": "Format messages with markdown before sending" + }, + "hide_membership_events": { + "title": "Hide membership events", + "description": "Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)" + }, + "hide_nickname_avatar_events": { + "title": "Hide nick/avatar events", + "description": "Hide nickname and avatar change messages from the room timeline." + }, + "notifications_and_sound": { + "title": "Notifications & Sound", + "desktop": { + "title": "Desktop notifications", + "description": "Show desktop notifications when new messages arrive." }, - "title": "Theme", - "theme_light": "Light", - "theme_silver": "Silver", - "theme_dark": "Dark", - "theme_butter": "Butter" + "sound": { + "title": "Notification sound", + "description": "Play a sound when new messages arrive." + } + }, + "security": { + "cross_signing": { + "title": "Cross signing and backup" + }, + "export_import_encryption_keys": { + "title": "Export / Import encryption keys" + }, + "export_encryption_keys": { + "title": "Export E2E room keys", + "description": "Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing." + }, + "import_encryption_keys": { + "title": "Import E2E room keys", + "description": "To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\\'ll have to enter the password you set in order to decrypt it." + } + }, + "logout": { + "title": "Logout", + "dialog": { + "title": "Logout", + "description": "Are you sure that you want to logout your session?", + "confirm": "Logout" + } + }, + "about": { + "application": "Application", + "credits": "Credits" + } }, - "markdown": { - "title": "Markdown formatting", - "description": "Format messages with markdown before sending" + "ShortcutSpaces": { + "header": "Pin Spaces", + "pinned_spaces": "Pinned spaces", + "no_pinned_spaces": "No pinned spaces", + "unpinned_spaces": "Unpinned spaces", + "no_unpinned_spaces": "No unpinned spaces", + "spaces_selected_zero": "No selected spaces", + "spaces_selected_one": "{{count}} selected space", + "spaces_selected_other": "{{count}} selected spaces", + "pin_button": "Pin" }, - "hide_membership_events": { - "title": "Hide membership events", - "description": "Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)" + "SecretStorageAccess": { + "incorrect_security_key": "Incorrect security key", + "incorrect_security_phrase": "Incorrect security phrase", + "security_phrase": "Security Phrase", + "security_key": "Security Key", + "use_security_key": "Use Security Key", + "use_security_phrase": "Use Security Phrase" }, - "hide_nickname_avatar_events": { - "title": "Hide nick/avatar events", - "description": "Hide nickname and avatar change messages from the room timeline." + "KeyBackup": { + "create_backup_title": "Create key backup", + "create_backup_tooltip": "Create backup", + "creating_backup": "Creating Backup...", + "backup_created": "Successfully created backup", + "backup_failed": "Failed to create backup", + "restoring": "Restoring backup keys...", + "restoring_progress": "Restoring backup keys... ({{progress}}/{{total}}", + "restore_backup_title": "Restore Key Backup", + "restore_backup_tooltip": "Restore Key Backup", + "restore_complete": "Successfully restored backup keys ({{progress}}/{{total}})", + "restore_failed_bad_key": "Failed to restore backup. Key is invalid!", + "restore_failed_unknown": "Failed to restore backup.", + "delete_key_backup_title": "Delete key backup", + "delete_key_backup_tooltip": "Delete backup", + "delete_key_backup_subtitle": "Deleting key backup is permanent.", + "delete_key_backup_message": "All encrypted message keys stored on the server will be permanently deleted.", + "encrypted_messages_backup_description": "Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.", + "encrypted_messages_backup_title": "Encrypted messages backup", + "encrypted_messages_backup_cross_signing_disabled": "Setup cross signing to backup your encrypted messages." }, - "notifications_and_sound": { - "title": "Notifications & Sound", - "desktop": { - "title": "Desktop notifications", - "description": "Show desktop notifications when new messages arrive." - }, - "sound": { - "title": "Notification sound", - "description": "Play a sound when new messages arrive." - } + "DeviceManage": { + "edit_session_name_title": "Edit session name", + "edit_session_name_subtitle": "Session name", + "edit_session_name_tooltip": "Edit session name", + "current_device_label": "Current", + "verify_session_button": "Verify", + "unverified_sessions_title": "Unverified sessions", + "unverified_sessions_none": "No unverified sessions", + "unencrypted_sessions_title": "Sessions without encryption support", + "verified_sessions_title": "Verified sessions", + "verified_sessions_none": "No verified sessions", + "setup_cross_signing_message": "Setup cross signing in case you lose all your sessions", + "loading_devices": "Loading devices...", + "logout_device_title": "Logout {{device}}", + "logout_device_message": "You are about to log out the session for {{device}}", + "logout_device_confirm": "Logout", + "logout_device_tooltip": "Remove session", + "session_verification_title": "Session Verification", + "session_name_privacy_message": "Session names are visible to everyone, so do not put any private info here." }, - "security": { - "cross_signing": { - "title": "Cross signing and backup" - }, - "export_import_encryption_keys": { - "title": "Export / Import encryption keys" - }, - "export_encryption_keys": { - "title": "Export E2E room keys", - "description": "Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing." + "CrossSigning": { + "title": "Cross Signing", + "setup_failed": "Failed to setup cross signing. Please try again", + "setup": "Setup cross signing", + "save_security_key_message": "Please save this security key somewhere safe", + "security_key_dialog_title": "Security Key", + "security_key_generation_message": "We will generate a Security Key, which you can use to manage message backups and session verification.", + "security_key_generation_button": "Generate Key", + "security_phrase_message": "Alternatively you can set a 'Security Phrase' so you don't have to remember the long Security Key, and optionally save the key as a backup", + "security_phrase_label": "Security Phrase", + "security_phrase_confirm_label": "Confirm Security Phrase", + "security_phrase_set_button": "Set Phrase & Generate Key", + "setup_dialog_title": "Setup cross signing", + "setup_message": "Setup to verify and keep track of all your sessions. Also required to backup encrypted message.", + "reset_keys_subtitle": "Resetting cross-signing keys is permanent.", + "reset_keys_message": "Anyone you have verified with will see security alerts and your message backup will lost. You almost certainly do not want to do this, unless you have lost Security Key or Phrase and every session you can cross-sign from." + }, + "AuthRequest": { + "wrong_password": "Wrong password. Please enter the correct password", + "request_failed": "Request failed!", + "password_label": "Account password" + }, + "Search": { + "description": "Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k" + }, + "RoomViewInput": { + "upload_progress": "Uploading: {{progress}}/{{total}} ({{percent}}%)", + "tombstone_replaced": "This room has been replaced, and is no longer active.", + "tombstone_permission_denied": "You do not have permission to post to this room", + "send_message_placeholder": "Send a message...", + "emoji_tooltip": "Emoji", + "file_size": "Size: {{size}}", + "cancel_reply_tooltip": "Cancel reply" + }, + "RoomViewHeader": { + "search_tooltip": "Search", + "people_tooltip": "People", + "members_tooltip": "Members" + }, + "RoomViewFloating": { + "jump_unread": "Jump to unread messages", + "mark_read": "Mark as read", + "jump_latest": "Jump to latest", + "user_typing_one": "{{user_one}} is typing...", + "user_typing_two": "{{user_one}} and {{user_two}} are typing...", + "user_typing_three": "{{user_one}}, {{user_two}} and {{user_three}} are typing...", + "user_typing_four": "{{user_one}}, {{user_two}}, {{user_three}} and {{user_four}} are typing...", + "user_typing_other": "Several people are typing..." + }, + "RoomViewContent": { + "welcome_to_room": "Welcome to {{room_name}}!", + "beginning_room": "This is the beginning of the {{room_name}} room.", + "beginning_dm": "This is the beginning of your direct message history with @{{user_name}}.", + "created_on": "Created on {{date, datetime}}", + "new_messages": "New messages" + }, + "RoomSettings": { + "leave_room": "Leave room", + "leave_room_confirm_message": "Are you sure you want to leave {{room_name}}?", + "leave_room_confirm_button": "Leave", + "notification_header": "Notifications (Changing this will only affect you)", + "visibility_header": "Room visibility (Who can join)", + "address_header": "Room addresses", + "encryption_header": "Encryption", + "message_history_header": "Message history visibility", + "room_settings_subtitle": "room settings" + }, + "RoomCommon": { + "user_joined": "{{user_name}} joined the room", + "user_left": "{{user_name}} left the room", + "user_invited": "{{inviter_name}} invited {{user_name}}", + "invite_cancelled": "{{inviter_name}} cancelled {{user_name}}'s invite", + "invite_rejected": "{{user_name}} rejected the invitation", + "user_kicked": "{{actor}} kicked {{user_name}}: {{reason}}", + "user_banned": "{{actor}} banned {{user_name}}: {{reason}}", + "user_unbanned": "{{actor}} unbanned {{user_name}}", + "avatar_set": "{{user_name}} set an avatar", + "avatar_changed": "{{user_name}} changed their avatar", + "avatar_removed": "{{user_name}} removed their avatar", + "name_set": "{{user_name}} set their display name to {{new_name}}", + "name_changed": "{{user_name}} changed their display name to {{new_name}}", + "name_removed": "{{user_name}} removed their display name {{new_name}}" + }, + "PublicRooms": { + "could_not_join_alias": "Unable to join {{alias}}. Either the room is private or doesn't exist", + "try_joining_alias": "Try joining {{alias}}", + "joining_alias": "Joining {{alias}}...", + "no_public_rooms": "No public rooms on {{homeserver}}", + "no_result_found": "No result found for '{{input}}' on {{homeserver}}", + "title": "Public Rooms", + "search_room_name_alias": "Room name or alias", + "search_button": "Search", + "loading": "Loading public rooms from {{homeserver}}...", + "searching": "Searching for '{{query}}' on {{homeserver}}...", + "result_title": "Public rooms on {{homeserver}}", + "search_result_title": "Search result for '{{query}}' on {{homeserver}}" + }, + "ProfileViewer": { + "kick_button": "Kick", + "kick_reason_label": "Kick Reason", + "ban_button": "Ban", + "ban_reason_label": "Ban reason", + "loading_sessions": "Loading sessions...", + "no_sessions_found": "No sessions found.", + "view_sessions_one": "View session", + "view_sessions_other": "View {{count}} sessions", + "send_direct_message_button": "Message", + "creating_dm_room": "Creating room...", + "ignore": "Ignore", + "ignoring": "Ignoring...", + "unignore": "Unignore", + "unignoring": "Unignoring...", + "change_power_level": "Change power level", + "shared_power_message": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?", + "demoting_self_message": "You will not be able to undo this change as you are demoting yourself. Are you sure?" + }, + "ProfileEditor": { + "remove_avatar": "Remove avatar", + "remove_avatar_confirmation": "Are you sure that you want to remove your avatar?", + "display_name_message": "Display name of {{user_name}}" + }, + "DrawerBreadcrumb": { + "home": "Home" + }, + "DrawerHeader": { + "add_rooms_or_spaces": "Add rooms or spaces", + "create_new_space": "Create new space", + "create_new_room": "Create new room", + "join_public_room": "Join public room", + "join_with_address": "Join with address", + "add_existing": "Add existing", + "manage_rooms": "Manage rooms", + "home": "Home", + "direct_messages": "Direct messages", + "start_dm_tooltip": "Start DM", + "add_rooms_spaces_tooltip": "Add rooms/spaces" + }, + "SideBar": { + "settings_tooltip": "Settings", + "unverified_sessions_one": "{{count}} unverified session", + "unverified_sessions_other": "{{count}} unverified sessions", + "home_tooltip": "Home", + "direct_messages_tooltip": "People", + "pin_spaces_tooltip": "Pin spaces", + "search_tooltip": "Search", + "invites_tooltip": "Invites" + }, + "JoinAlias": { + "invalid_address": "Invalid address.", + "looking_for_address": "Looking for address...", + "joining_alias": "Joining {{alias_name}}...", + "couldnt_find_room_or_space_alias": "Unable to find room/space with {{alias_name}}. Either the room/space is private or doesn't exist.", + "couldnt_find_room_or_space": "Unable to join {{alias_name}}. Either the room/space is private or doesn't exist.", + "address_label": "Address", + "title": "Join with address" + }, + "InviteUser": { + "user_not_found": "{{user_name}} not found!", + "no_matches_found": "No matches found for {{user_name}}", + "invite_result": { + "invited": "Invited", + "already_joined": "Already joined", + "already_invited": "Already invited", + "banned": "Banned" }, - "import_encryption_keys": { - "title": "Import E2E room keys", - "description": "To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\\'ll have to enter the password you set in order to decrypt it." - } + "search_label": "Name or User ID", + "search_result_title": "Search result for user {{user_name}}", + "searching_for_user": "Searching for user {{user_name}}...", + "invite_to_room": "Invite to {{room}}", + "invite_to_dm": "Direct Message" }, - "logout": { - "title": "Logout", - "dialog": { - "title": "Logout", - "description": "Are you sure that you want to logout your session?", - "confirm": "Logout" - } + "InviteList": { + "accept_invite": "Accept", + "reject_invite": "Reject", + "direct_messages_title": "Direct Messages", + "rooms_title": "Rooms", + "spaces_title": "Spaces", + "title": "Invites" }, - "about":{ - "application": "Application", - "credits": "Credits" - } - }, - "ShortcutSpaces": { - "header": "Pin Spaces", - "pinned_spaces": "Pinned spaces", - "no_pinned_spaces": "No pinned spaces", - "unpinned_spaces": "Unpinned spaces", - "no_unpinned_spaces": "No unpinned spaces", - "spaces_selected_zero": "No selected spaces", - "spaces_selected_one": "{{count}} selected space", - "spaces_selected_other": "{{count}} selected spaces", - "pin_button": "Pin" - }, - "SecretStorageAccess": { - "incorrect_security_key": "Incorrect security key", - "incorrect_security_phrase": "Incorrect security phrase", - "security_phrase": "Security Phrase", - "security_key": "Security Key", - "use_security_key": "Use Security Key", - "use_security_phrase": "Use Security Phrase" - }, - "KeyBackup": { - "create_backup_title": "Create key backup", - "create_backup_tooltip": "Create backup", - "creating_backup": "Creating Backup...", - "backup_created": "Successfully created backup", - "backup_failed": "Failed to create backup", - "restoring": "Restoring backup keys...", - "restoring_progress": "Restoring backup keys... ({{progress}}/{{total}}", - "restore_backup_title": "Restore Key Backup", - "restore_backup_tooltip": "Restore Key Backup", - "restore_complete": "Successfully restored backup keys ({{progress}}/{{total}})", - "restore_failed_bad_key": "Failed to restore backup. Key is invalid!", - "restore_failed_unknown": "Failed to restore backup.", - "delete_key_backup_title": "Delete key backup", - "delete_key_backup_tooltip": "Delete backup", - "delete_key_backup_subtitle": "Deleting key backup is permanent.", - "delete_key_backup_message": "All encrypted message keys stored on the server will be permanently deleted.", - "encrypted_messages_backup_description": "Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.", - "encrypted_messages_backup_title": "Encrypted messages backup", - "encrypted_messages_backup_cross_signing_disabled": "Setup cross signing to backup your encrypted messages." - }, - "DeviceManage": { - "edit_session_name_title": "Edit session name", - "edit_session_name_subtitle": "Session name", - "edit_session_name_tooltip": "Edit session name", - "current_device_label": "Current", - "verify_session_button": "Verify", - "unverified_sessions_title": "Unverified sessions", - "unverified_sessions_none": "No unverified sessions", - "unencrypted_sessions_title": "Sessions without encryption support", - "verified_sessions_title": "Verified sessions", - "verified_sessions_none": "No verified sessions", - "setup_cross_signing_message": "Setup cross signing in case you lose all your sessions", - "loading_devices": "Loading devices...", - "logout_device_title": "Logout {{device}}", - "logout_device_message": "You are about to log out the session for {{device}}", - "logout_device_confirm": "Logout", - "logout_device_tooltip": "Remove session", - "session_verification_title": "Session Verification", - "session_name_privacy_message": "Session names are visible to everyone, so do not put any private info here." - }, - "CrossSigning":{ - "title": "Cross Signing", - "setup_failed": "Failed to setup cross signing. Please try again", - "setup": "Setup cross signing", - "save_security_key_message": "Please save this security key somewhere safe", - "security_key_dialog_title": "Security Key", - "security_key_generation_message": "We will generate a Security Key, which you can use to manage message backups and session verification.", - "security_key_generation_button": "Generate Key", - "security_phrase_message": "Alternatively you can set a 'Security Phrase' so you don't have to remember the long Security Key, and optionally save the key as a backup", - "security_phrase_label": "Security Phrase", - "security_phrase_confirm_label": "Confirm Security Phrase", - "security_phrase_set_button": "Set Phrase & Generate Key", - "setup_dialog_title": "Setup cross signing", - "setup_message": "Setup to verify and keep track of all your sessions. Also required to backup encrypted message.", - "reset_keys_subtitle": "Resetting cross-signing keys is permanent.", - "reset_keys_message": "Anyone you have verified with will see security alerts and your message backup will lost. You almost certainly do not want to do this, unless you have lost Security Key or Phrase and every session you can cross-sign from." - - }, - "AuthRequest" : { - "wrong_password": "Wrong password. Please enter the correct password", - "request_failed": "Request failed!", - "password_label": "Account password" - }, - "Search": { - "description": "Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k" - }, - "RoomViewInput":{ - "upload_progress": "Uploading: {{progress}}/{{total}} ({{percent}}%)", - "tombstone_replaced": "This room has been replaced, and is no longer active.", - "tombstone_permission_denied": "You do not have permission to post to this room", - "send_message_placeholder": "Send a message...", - "emoji_tooltip": "Emoji", - "file_size": "Size: {{size}}", - "cancel_reply_tooltip": "Cancel reply" - }, - "RoomViewHeader": { - "search_tooltip": "Search", - "people_tooltip": "People", - "members_tooltip": "Members" - }, - "RoomViewFloating": { - "jump_unread": "Jump to unread messages", - "mark_read": "Mark as read", - "jump_latest": "Jump to latest", - "user_typing_one": "{{user_one}} is typing...", - "user_typing_two": "{{user_one}} and {{user_two}} are typing...", - "user_typing_three": "{{user_one}}, {{user_two}} and {{user_three}} are typing...", - "user_typing_four": "{{user_one}}, {{user_two}}, {{user_three}} and {{user_four}} are typing...", - "user_typing_other": "Several people are typing..." - }, - "RoomViewContent": { - "welcome_to_room": "Welcome to {{room_name}}!", - "beginning_room": "This is the beginning of the {{room_name}} room.", - "beginning_dm": "This is the beginning of your direct message history with @{{user_name}}.", - "created_on": "Created on {{date, datetime}}", - "new_messages": "New messages" - }, - "RoomSettings" : { - "leave_room": "Leave room", - "leave_room_confirm_message": "Are you sure you want to leave {{room_name}}?", - "leave_room_confirm_button": "Leave", - "notification_header": "Notifications (Changing this will only affect you)", - "visibility_header": "Room visibility (Who can join)", - "address_header": "Room addresses", - "encryption_header": "Encryption", - "message_history_header": "Message history visibility", - "room_settings_subtitle": "room settings" - }, - "RoomCommon": { - "user_joined": "{{user_name}} joined the room", - "user_left": "{{user_name}} left the room", - "user_invited": "{{inviter_name}} invited {{user_name}}", - "invite_cancelled": "{{inviter_name}} cancelled {{user_name}}'s invite", - "invite_rejected": "{{user_name}} rejected the invitation", - "user_kicked": "{{actor}} kicked {{user_name}}: {{reason}}", - "user_banned": "{{actor}} banned {{user_name}}: {{reason}}", - "user_unbanned": "{{actor}} unbanned {{user_name}}", - "avatar_set": "{{user_name}} set an avatar", - "avatar_changed": "{{user_name}} changed their avatar", - "avatar_removed": "{{user_name}} removed their avatar", - "name_set": "{{user_name}} set their display name to {{new_name}}", - "name_changed": "{{user_name}} changed their display name to {{new_name}}", - "name_removed": "{{user_name}} removed their display name {{new_name}}" - }, - "PublicRooms": { - "could_not_join_alias": "Unable to join {{alias}}. Either the room is private or doesn't exist", - "try_joining_alias": "Try joining {{alias}}", - "joining_alias": "Joining {{alias}}...", - "no_public_rooms": "No public rooms on {{homeserver}}", - "no_result_found": "No result found for '{{input}}' on {{homeserver}}", - "title": "Public Rooms", - "search_room_name_alias": "Room name or alias", - "search_button": "Search", - "loading": "Loading public rooms from {{homeserver}}...", - "searching": "Searching for '{{query}}' on {{homeserver}}...", - "result_title": "Public rooms on {{homeserver}}", - "search_result_title": "Search result for '{{query}}' on {{homeserver}}" - }, - "ProfileViewer": { - "kick_button": "Kick", - "kick_reason_label": "Kick Reason", - "ban_button": "Ban", - "ban_reason_label": "Ban reason", - "loading_sessions" : "Loading sessions...", - "no_sessions_found": "No sessions found.", - "view_sessions_one": "View session", - "view_sessions_other": "View {{count}} sessions", - "send_direct_message_button": "Message", - "creating_dm_room": "Creating room...", - "ignore": "Ignore", - "ignoring": "Ignoring...", - "unignore": "Unignore", - "unignoring": "Unignoring...", - "change_power_level": "Change power level", - "shared_power_message": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?", - "demoting_self_message": "You will not be able to undo this change as you are demoting yourself. Are you sure?" - }, - "ProfileEditor": { - "remove_avatar": "Remove avatar", - "remove_avatar_confirmation": "Are you sure that you want to remove your avatar?", - "display_name_message": "Display name of {{user_name}}" - }, - "DrawerBreadcrumb": { - "home": "Home" - }, - "DrawerHeader" : { - "add_rooms_or_spaces": "Add rooms or spaces", - "create_new_space": "Create new space", - "create_new_room": "Create new room", - "join_public_room": "Join public room", - "join_with_address": "Join with address", - "add_existing": "Add existing", - "manage_rooms": "Manage rooms", - "home": "Home", - "direct_messages": "Direct messages", - "start_dm_tooltip": "Start DM", - "add_rooms_spaces_tooltip": "Add rooms/spaces" - }, - "SideBar": { - "settings_tooltip": "Settings", - "unverified_sessions_one": "{{count}} unverified session", - "unverified_sessions_other": "{{count}} unverified sessions", - "home_tooltip": "Home", - "direct_messages_tooltip": "People", - "pin_spaces_tooltip": "Pin spaces", - "search_tooltip": "Search", - "invites_tooltip": "Invites" - }, - "JoinAlias": { - "invalid_address": "Invalid address.", - "looking_for_address": "Looking for address...", - "joining_alias": "Joining {{alias_name}}...", - "couldnt_find_room_or_space_alias": "Unable to find room/space with {{alias_name}}. Either the room/space is private or doesn't exist.", - "couldnt_find_room_or_space": "Unable to join {{alias_name}}. Either the room/space is private or doesn't exist.", - "address_label": "Address", - "title": "Join with address" - }, - "InviteUser": { - "user_not_found": "{{user_name}} not found!", - "no_matches_found": "No matches found for {{user_name}}", - "invite_result": { - "invited": "Invited", - "already_joined": "Already joined", - "already_invited": "Already invited", - "banned": "Banned" + "EmojiVerification": { + "waiting_for_response": "Waiting for response from other device...", + "confirmation_prompt": "Confirm the emoji below are displayed on both devices, in the same order:", + "emojis_match_button": "They match", + "emojis_dont_match_button": "They don't match", + "accept_request_from_other_device_message": "Please accept the request from other device.", + "begin_verification_process_message": "Click accept to start the verification process.", + "begin_verification_button_text": "Accept", + "title": "Emoji Verification" }, - "search_label": "Name or User ID", - "search_result_title": "Search result for user {{user_name}}", - "searching_for_user": "Searching for user {{user_name}}...", - "invite_to_room": "Invite to {{room}}", - "invite_to_dm": "Direct Message" - }, - "InviteList": { - "accept_invite": "Accept", - "reject_invite": "Reject", - "direct_messages_title": "Direct Messages", - "rooms_title": "Rooms", - "spaces_title": "Spaces", - "title": "Invites" - }, - "EmojiVerification": { - "waiting_for_response": "Waiting for response from other device...", - "confirmation_prompt": "Confirm the emoji below are displayed on both devices, in the same order:", - "emojis_match_button": "They match", - "emojis_dont_match_button": "They don't match", - "accept_request_from_other_device_message": "Please accept the request from other device.", - "begin_verification_process_message": "Click accept to start the verification process.", - "begin_verification_button_text": "Accept", - "title": "Emoji Verification" - }, - "DragDrop": { - "drop_file_to_upload_prompt": "Drop file to upload" - }, - "CreateRoom": { - "private_room_short": "Private", - "restricted_room_short": "Restricted", - "public_room_short": "Public", - "private_room_long": "Private (invite only)", - "restricted_room_long": "Restricted (space member can join)", - "public_room_long": "Public (anyone can join)", - "visibility_title": "Visibility", - "visibility_message": "Visibility (who can join)", - "select_who_can_join_space": "Select who can join this space", - "select_who_can_join_room": "Select who can join this room", - "space_address": "Space address", - "room_address": "Room address", - "room_address_already_in_use": "{{room_address}} is already in use", - "e2e_title": "Enable end-to-end encryption", - "e2e_message": "You can’t disable this later. Bridges & most bots won’t work yet.", - "role_title": "Select your role", - "role_message": "Selecting 'Admin' sets your power level to 100 whereas 'Founder' sets it to 101.", - "creating_room": "Creating room...", - "creating_space": "Creating space...", - "topic_label": "Topic (optional)", - "space_name": "Space name", - "room_name": "Room name", - "role_admin": "Admin", - "role_founder": "Founder", - "create_room": "Create room", - "create_space": "Create space", - "home": "Home" + "DragDrop": { + "drop_file_to_upload_prompt": "Drop file to upload" + }, + "CreateRoom": { + "private_room_short": "Private", + "restricted_room_short": "Restricted", + "public_room_short": "Public", + "private_room_long": "Private (invite only)", + "restricted_room_long": "Restricted (space member can join)", + "public_room_long": "Public (anyone can join)", + "visibility_title": "Visibility", + "visibility_message": "Visibility (who can join)", + "select_who_can_join_space": "Select who can join this space", + "select_who_can_join_room": "Select who can join this room", + "space_address": "Space address", + "room_address": "Room address", + "room_address_already_in_use": "{{room_address}} is already in use", + "e2e_title": "Enable end-to-end encryption", + "e2e_message": "You can’t disable this later. Bridges & most bots won’t work yet.", + "role_title": "Select your role", + "role_message": "Selecting 'Admin' sets your power level to 100 whereas 'Founder' sets it to 101.", + "creating_room": "Creating room...", + "creating_space": "Creating space...", + "topic_label": "Topic (optional)", + "space_name": "Space name", + "room_name": "Room name", + "role_admin": "Admin", + "role_founder": "Founder", + "create_room": "Create room", + "create_space": "Create space", + "home": "Home" + } } } \ No newline at end of file diff --git a/src/app/organisms/create-room/CreateRoom.jsx b/src/app/organisms/create-room/CreateRoom.jsx index d9d7d509a..5c10618dd 100644 --- a/src/app/organisms/create-room/CreateRoom.jsx +++ b/src/app/organisms/create-room/CreateRoom.jsx @@ -137,8 +137,8 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) { }; const joinRules = ['invite', 'restricted', 'public']; - const joinRuleShortText = [ t("CreateRoom.private_room_short"), t("CreateRoom.restricted_room_short"), t("CreateRoom.public_room_short")]; - const joinRuleText = [ t("CreateRoom.private_room_long"), t("CreateRoom.restricted_room_long"), t("CreateRoom.public_room_long")]; + const joinRuleShortText = [ t("Organisms.CreateRoom.private_room_short"), t("Organisms.CreateRoom.restricted_room_short"), t("Organisms.CreateRoom.public_room_short")]; + const joinRuleText = [ t("Organisms.CreateRoom.private_room_long"), t("Organisms.CreateRoom.restricted_room_long"), t("Organisms.CreateRoom.public_room_long")]; const jrRoomIC = [HashLockIC, HashIC, HashGlobeIC]; const jrSpaceIC = [SpaceLockIC, SpaceIC, SpaceGlobeIC]; const handleJoinRule = (evt) => { @@ -147,7 +147,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) { getEventCords(evt, '.btn-surface'), (closeMenu) => ( <> - {t("CreateRoom.visibility_message")} + {t("Organisms.CreateRoom.visibility_message")} { joinRules.map((rule) => (
{joinRuleShortText[joinRules.indexOf(joinRule)]} )} - content={{isSpace ? t("CreateRoom.select_who_can_join_space") : t("CreateRoom.select_who_can_join_room")}} + content={{isSpace ? t("Organisms.CreateRoom.select_who_can_join_space") : t("Organisms.CreateRoom.select_who_can_join_room")}} /> {joinRule === 'public' && (
- {isSpace ? t("CreateRoom.space_address") : t("CreateRoom.room_address")} + {isSpace ? t("Organisms.CreateRoom.space_address") : t("Organisms.CreateRoom.room_address")}
# {`:${userHs}`}
- {isValidAddress === false && { t("CreateRoom.room_address_already_in_use", {room_address: `#${addressValue}:${userHs}`})}} + {isValidAddress === false && { t("Organisms.CreateRoom.room_address_already_in_use", {room_address: `#${addressValue}:${userHs}`})}}
)} {!isSpace && joinRule !== 'public' && ( } - content={ {t("CreateRoom.e2e_message")}} + content={ {t("Organisms.CreateRoom.e2e_message")}} /> )} )} content={( - {t("CreateRoom.role_message")} + {t("Organisms.CreateRoom.role_message")} )} /> - +
- +
)} {typeof creatingError === 'string' && {creatingError}} @@ -284,9 +284,9 @@ function CreateRoom() { isOpen={create !== null} title={( - {parentId ? twemojify(room.name) : t("CreateRoom.home")} + {parentId ? twemojify(room.name) : t("Organisms.CreateRoom.home")} - {` — ${isSpace ? t("CreateRoom.create_space") : t("CreateRoom.create_room")}`} + {` — ${isSpace ? t("Organisms.CreateRoom.create_space") : t("Organisms.CreateRoom.create_room")}`} )} diff --git a/src/app/organisms/drag-drop/DragDrop.jsx b/src/app/organisms/drag-drop/DragDrop.jsx index 8576325fc..aae8c1c5d 100644 --- a/src/app/organisms/drag-drop/DragDrop.jsx +++ b/src/app/organisms/drag-drop/DragDrop.jsx @@ -18,7 +18,7 @@ function DragDrop({ isOpen }) { overlayClassName="drag-drop__overlay" isOpen={isOpen} > - {t("DragDrop.drop_file_to_upload_prompt")} + {t("Organisms.DragDrop.drop_file_to_upload_prompt")} ); } diff --git a/src/app/organisms/emoji-verification/EmojiVerification.jsx b/src/app/organisms/emoji-verification/EmojiVerification.jsx index 9d54ba24c..5fa6e0804 100644 --- a/src/app/organisms/emoji-verification/EmojiVerification.jsx +++ b/src/app/organisms/emoji-verification/EmojiVerification.jsx @@ -100,14 +100,14 @@ function EmojiVerificationContent({ data, requestClose }) { const renderWait = () => ( <> - {t("EmojiVerification.waiting_for_response")} + {t("Organisms.EmojiVerification.waiting_for_response")} ); if (sas !== null) { return (
- {t("EmojiVerification.confirmation_prompt")} + {t("Organisms.EmojiVerification.confirmation_prompt")}
{sas.sas.emoji.map((emoji, i) => ( // eslint-disable-next-line react/no-array-index-key @@ -120,8 +120,8 @@ function EmojiVerificationContent({ data, requestClose }) {
{process ? renderWait() : ( <> - - + + )}
@@ -132,7 +132,7 @@ function EmojiVerificationContent({ data, requestClose }) { if (targetDevice) { return (
- {t("EmojiVerification.accept_request_from_other_device_message")} + {t("Organisms.EmojiVerification.accept_request_from_other_device_message")}
{renderWait()}
@@ -142,12 +142,12 @@ function EmojiVerificationContent({ data, requestClose }) { return (
- {t("EmojiVerification.begin_verification_process_message")} + {t("Organisms.EmojiVerification.begin_verification_process_message")}
{ process ? renderWait() - : + : }
@@ -188,7 +188,7 @@ function EmojiVerification() { className="emoji-verification" title={( - {t("EmojiVerification.title")} + {t("Organisms.EmojiVerification.title")} )} contentOptions={} diff --git a/src/app/organisms/invite-list/InviteList.jsx b/src/app/organisms/invite-list/InviteList.jsx index c94950a5a..e0e8553ec 100644 --- a/src/app/organisms/invite-list/InviteList.jsx +++ b/src/app/organisms/invite-list/InviteList.jsx @@ -81,8 +81,8 @@ function InviteList({ isOpen, onRequestClose }) { ? () : (
- - + +
) } @@ -93,14 +93,14 @@ function InviteList({ isOpen, onRequestClose }) { return ( } onRequestClose={onRequestClose} >
{ initMatrix.roomList.inviteDirects.size !== 0 && (
- {t("InviteList.direct_messages_title")} + {t("Organisms.InviteList.direct_messages_title")}
)} { @@ -118,8 +118,8 @@ function InviteList({ isOpen, onRequestClose }) { ? () : (
- - + +
) } @@ -129,14 +129,14 @@ function InviteList({ isOpen, onRequestClose }) { } { initMatrix.roomList.inviteSpaces.size !== 0 && (
- {t("InviteList.spaces_title")} + {t("Organisms.InviteList.spaces_title")}
)} { Array.from(initMatrix.roomList.inviteSpaces).map(renderRoomTile) } { initMatrix.roomList.inviteRooms.size !== 0 && (
- {t("InviteList.rooms_title")} + {t("Organisms.InviteList.rooms_title")}
)} { Array.from(initMatrix.roomList.inviteRooms).map(renderRoomTile) } diff --git a/src/app/organisms/invite-user/InviteUser.jsx b/src/app/organisms/invite-user/InviteUser.jsx index 56ad16fda..db52b2ecb 100644 --- a/src/app/organisms/invite-user/InviteUser.jsx +++ b/src/app/organisms/invite-user/InviteUser.jsx @@ -88,7 +88,7 @@ function InviteUser({ avatar_url: result.avatar_url, }]); } catch (e) { - updateSearchQuery({error: t("InviteUser.user_not_found", {user_name: inputUsername})}); + updateSearchQuery({error: t("Organisms.InviteUser.user_not_found", {user_name: inputUsername})}); } } else { try { @@ -97,7 +97,7 @@ function InviteUser({ limit: 20, }); if (result.results.length === 0) { - updateSearchQuery({ error: t("InviteUser.no_matches_found", {user_name: inputUsername})}); + updateSearchQuery({ error: t("Organisms.InviteUser.no_matches_found", {user_name: inputUsername})}); updateIsSearching(false); return; } @@ -179,7 +179,7 @@ function InviteUser({ return ; } if (invitedUserIds.has(userId)) { - return messageJSX(t("InviteUser.invite_result.invited"), true); + return messageJSX(t("Organisms.InviteUser.invite_result.invited"), true); } if (typeof roomId === 'string') { const member = mx.getRoom(roomId).getMember(userId); @@ -187,11 +187,11 @@ function InviteUser({ const userMembership = member.membership; switch (userMembership) { case 'join': - return messageJSX(t("InviteUser.invite_result.already_joined"), true); + return messageJSX(t("Organisms.InviteUser.invite_result.already_joined"), true); case 'invite': - return messageJSX(t("InviteUser.invite_result.already_invited"), true); + return messageJSX(t("Organisms.InviteUser.invite_result.already_invited"), true); case 'ban': - return messageJSX(t("InviteUser.invite_result.banned"), false); + return messageJSX(t("Organisms.InviteUser.invite_result.banned"), false); default: } } @@ -245,13 +245,13 @@ function InviteUser({ return ( } onRequestClose={onRequestClose} >
{ e.preventDefault(); searchUser(usernameRef.current.value); }}> - +
@@ -259,13 +259,13 @@ function InviteUser({ typeof searchQuery.username !== 'undefined' && isSearching && (
- {t("InviteUser.searching_for_user", {user_name: searchQuery.username})} + {t("Organisms.InviteUser.searching_for_user", {user_name: searchQuery.username})}
) } { typeof searchQuery.username !== 'undefined' && !isSearching && ( - {t("InviteUser.search_result_title", {user_name: searchQuery.username})} + {t("Organisms.InviteUser.search_result_title", {user_name: searchQuery.username})} ) } { diff --git a/src/app/organisms/join-alias/JoinAlias.jsx b/src/app/organisms/join-alias/JoinAlias.jsx index 05e033a33..7089d8bd4 100644 --- a/src/app/organisms/join-alias/JoinAlias.jsx +++ b/src/app/organisms/join-alias/JoinAlias.jsx @@ -61,10 +61,10 @@ function JoinAliasContent({ term, requestClose }) { const alias = e.target.alias.value; if (alias?.trim() === '') return; if (alias.match(ALIAS_OR_ID_REG) === null) { - setError(t("JoinAlias.invalid_address")); + setError(t("Organisms.JoinAlias.invalid_address")); return; } - setProcess(t("JoinAlias.looking_for_address")); + setProcess(t("Organisms.JoinAlias.looking_for_address")); setError(undefined); let via; if (alias.startsWith('#')) { @@ -72,12 +72,12 @@ function JoinAliasContent({ term, requestClose }) { const aliasData = await mx.resolveRoomAlias(alias); via = aliasData?.servers.slice(0, 3) || []; if (mountStore.getItem()) { - setProcess(t("JoinAlias.joining_alias", {alias_name: alias})); + setProcess(t("Organisms.JoinAlias.joining_alias", {alias_name: alias})); } } catch (err) { if (!mountStore.getItem()) return; setProcess(false); - setError(t("JoinAlias.couldnt_find_room_or_space_alias", {alias_name: alias})); + setError(t("Organisms.JoinAlias.couldnt_find_room_or_space_alias", {alias_name: alias})); } } try { @@ -88,14 +88,14 @@ function JoinAliasContent({ term, requestClose }) { } catch { if (!mountStore.getItem()) return; setProcess(false); - setError(t("JoinAlias.couldnt_find_room_or_space", {alias_name: alias})); + setError(t("Organisms.JoinAlias.couldnt_find_room_or_space", {alias_name: alias})); } }; return (
{t("JoinAlias.title")} + {t("Organisms.JoinAlias.title")} )} contentOptions={} onRequestClose={requestClose} diff --git a/src/app/organisms/navigation/DrawerBreadcrumb.jsx b/src/app/organisms/navigation/DrawerBreadcrumb.jsx index f67693bfd..69375bec7 100644 --- a/src/app/organisms/navigation/DrawerBreadcrumb.jsx +++ b/src/app/organisms/navigation/DrawerBreadcrumb.jsx @@ -116,7 +116,7 @@ function DrawerBreadcrumb({ spaceId }) { className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''} onClick={() => selectSpace(id)} > - {id === cons.tabs.HOME ? t("DrawerBreadcrumb.home") : twemojify(mx.getRoom(id).name)} + {id === cons.tabs.HOME ? t("Organisms.DrawerBreadcrumb.home") : twemojify(mx.getRoom(id).name)} { noti !== null && ( - {t("DrawerHeader.add_rooms_or_spaces")} + {t("Organisms.DrawerHeader.add_rooms_or_spaces")} { afterOptionSelect(); openCreateRoom(true, spaceId); }} disabled={!canManage} > - {t("DrawerHeader.create_new_space")} + {t("Organisms.DrawerHeader.create_new_space")} { afterOptionSelect(); openCreateRoom(false, spaceId); }} disabled={!canManage} > - {t("DrawerHeader.create_new_room")} + {t("Organisms.DrawerHeader.create_new_room")} { !spaceId && ( { afterOptionSelect(); openPublicRooms(); }} > - {t("DrawerHeader.join_public_room")} + {t("Organisms.DrawerHeader.join_public_room")} )} { !spaceId && ( @@ -72,7 +72,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { iconSrc={PlusIC} onClick={() => { afterOptionSelect(); openJoinAlias(); }} > - {t("DrawerHeader.join_with_address")} + {t("Organisms.DrawerHeader.join_with_address")} )} { spaceId && ( @@ -81,7 +81,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { onClick={() => { afterOptionSelect(); openSpaceAddExisting(spaceId); }} disabled={!canManage} > - {t("DrawerHeader.add_existing")} + {t("Organisms.DrawerHeader.add_existing")} )} { spaceId && ( @@ -89,7 +89,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { onClick={() => { afterOptionSelect(); openSpaceManage(spaceId); }} iconSrc={HashSearchIC} > - {t("DrawerHeader.manage_rooms")} + {t("Organisms.DrawerHeader.manage_rooms")} )} @@ -105,7 +105,7 @@ HomeSpaceOptions.propTypes = { function DrawerHeader({ selectedTab, spaceId }) { const mx = initMatrix.matrixClient; - const tabName = selectedTab !== cons.tabs.DIRECTS ? t("DrawerHeader.home") : t("DrawerHeader.direct_messages"); + const tabName = selectedTab !== cons.tabs.DIRECTS ? t("Organisms.DrawerHeader.home") : t("Organisms.DrawerHeader.direct_messages"); const isDMTab = selectedTab === cons.tabs.DIRECTS; const room = mx.getRoom(spaceId); @@ -149,8 +149,8 @@ function DrawerHeader({ selectedTab, spaceId }) { )} - { isDMTab && openInviteUser()} tooltip={t("DrawerHeader.start_dm_tooltip")} src={PlusIC} size="small" /> } - { !isDMTab && } + { isDMTab && openInviteUser()} tooltip={t("Organisms.DrawerHeader.start_dm_tooltip")} src={PlusIC} size="small" /> } + { !isDMTab && } ); } diff --git a/src/app/organisms/navigation/SideBar.jsx b/src/app/organisms/navigation/SideBar.jsx index 8f587b126..39a2b0750 100644 --- a/src/app/organisms/navigation/SideBar.jsx +++ b/src/app/organisms/navigation/SideBar.jsx @@ -85,7 +85,7 @@ function ProfileAvatarMenu() { return ( openSettings(settingTabText.SECURITY)} avatar={} /> @@ -155,7 +155,7 @@ function FeaturedTab() { return ( <> selectTab(cons.tabs.HOME)} avatar={} @@ -167,7 +167,7 @@ function FeaturedTab() { ) : null} /> selectTab(cons.tabs.DIRECTS)} avatar={} @@ -363,7 +363,7 @@ function SideBar() {
openShortcutSpaces()} avatar={} /> @@ -375,13 +375,13 @@ function SideBar() {
openSearch()} avatar={} /> { totalInvites !== 0 && ( openInviteList()} avatar={} notificationBadge={} diff --git a/src/app/organisms/profile-editor/ProfileEditor.jsx b/src/app/organisms/profile-editor/ProfileEditor.jsx index 5eaa3b24f..c45f06995 100644 --- a/src/app/organisms/profile-editor/ProfileEditor.jsx +++ b/src/app/organisms/profile-editor/ProfileEditor.jsx @@ -49,8 +49,8 @@ function ProfileEditor({ userId }) { const handleAvatarUpload = async (url) => { if (url === null) { const isConfirmed = await confirmDialog( - t("ProfileEditor.remove_avatar"), - t("ProfileViewer.remove_avatar_confirmation"), + t("Organisms.ProfileEditor.remove_avatar"), + t("Organisms.ProfileViewer.remove_avatar_confirmation"), t("common.remove"), 'caution', ); @@ -90,7 +90,7 @@ function ProfileEditor({ userId }) { onSubmit={(e) => { e.preventDefault(); saveDisplayName(); }} > {canIKick && ( - - + + )} {canIBan && (
- - + +
)}
@@ -127,8 +127,8 @@ function SessionInfo({ userId }) { if (!isVisible) return null; return (
- {devices === null && {t("ProfileViewer.loading_sessions")}} - {devices?.length === 0 && {t("ProfileViewer.no_sessions_found")}} + {devices === null && {t("Organisms.ProfileViewer.loading_sessions")}} + {devices?.length === 0 && {t("Organisms.ProfileViewer.no_sessions_found")}} {devices !== null && (devices.map((device) => ( setIsVisible(!isVisible)} iconSrc={isVisible ? ChevronBottomIC : ChevronRightIC} > - {t("ProfileViewer.view_sessions", {count: devices?.length})} + {t("Organisms.ProfileViewer.view_sessions", {count: devices?.length})} {renderSessionChips()}
@@ -261,7 +261,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) { onClick={openDM} disabled={isCreatingDM} > - {isCreatingDM ? t("ProfileViewer.creating_dm_room") : t("ProfileViewer.send_direct_message_button")} + {isCreatingDM ? t("Organisms.ProfileViewer.creating_dm_room") : t("Organisms.ProfileViewer.send_direct_message_button")} { isBanned && canIKick && (
@@ -374,14 +374,14 @@ function ProfileViewer() { const handleChangePowerLevel = async (newPowerLevel) => { if (newPowerLevel === powerLevel) return; - const SHARED_POWER_MSG = t("ProfileViewer.shared_power_message"); - const DEMOTING_MYSELF_MSG = t("ProfileViewer.demoting_self_message"); + const SHARED_POWER_MSG = t("Organisms.ProfileViewer.shared_power_message"); + const DEMOTING_MYSELF_MSG = t("Organisms.ProfileViewer.demoting_self_message"); const isSharedPower = newPowerLevel === myPowerLevel; const isDemotingMyself = userId === mx.getUserId(); if (isSharedPower || isDemotingMyself) { const isConfirmed = await confirmDialog( - t("ProfileViewer.change_power_level"), + t("Organisms.ProfileViewer.change_power_level"), isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG, t("common.change"), 'caution', diff --git a/src/app/organisms/public-rooms/PublicRooms.jsx b/src/app/organisms/public-rooms/PublicRooms.jsx index 970db63aa..19df1ceaf 100644 --- a/src/app/organisms/public-rooms/PublicRooms.jsx +++ b/src/app/organisms/public-rooms/PublicRooms.jsx @@ -59,7 +59,7 @@ function TryJoinWithAlias({ alias, onRequestClose }) { } catch (e) { setStatus({ isJoining: false, - error: t("PublicRooms.could_not_join_alias", {alias: alias}), + error: t("Organisms.PublicRooms.could_not_join_alias", {alias: alias}), roomId: null, tempRoomId: null, }); @@ -69,12 +69,12 @@ function TryJoinWithAlias({ alias, onRequestClose }) { return (
{status.roomId === null && !status.isJoining && status.error === null && ( - + )} {status.isJoining && ( <> - {t("PublicRooms.joining_alias", {alias: alias})} + {t("Organisms.PublicRooms.joining_alias", {alias: alias})} )} {status.roomId !== null && ( @@ -147,8 +147,8 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { if (totalRooms.length === 0) { updateSearchQuery({ error: inputRoomName === '' - ? t("PublicRooms.no_public_rooms", {homeserver: inputHs}) - : t("PublicRooms.no_result_found", {homeserver: inputHs, input: inputRoomName}), + ? t("Organisms.PublicRooms.no_public_rooms", {homeserver: inputHs}) + : t("Organisms.PublicRooms.no_result_found", {homeserver: inputHs, input: inputRoomName}), alias: isInputAlias ? inputRoomName : null, }); } @@ -225,17 +225,17 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { return ( } onRequestClose={onRequestClose} >
{ e.preventDefault(); searchRooms(); }}>
- +
- +
{ @@ -244,13 +244,13 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { ? (
- {t("PublicRooms.loading", {homeserver: searchQuery.homeserver})} + {t("Organisms.PublicRooms.loading", {homeserver: searchQuery.homeserver})}
) : (
- {t("PublicRooms.searching", {homeserver: searchQuery.homeserver, query: searchQuery.name})} + {t("Organisms.PublicRooms.searching", {homeserver: searchQuery.homeserver, query: searchQuery.name})}
) ) @@ -258,8 +258,8 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) { { typeof searchQuery.name !== 'undefined' && !isSearching && ( searchQuery.name === '' - ? {t("PublicRooms.result_title", {homeserver: searchQuery.homeserver})} - : {t("PublicRooms.search_result_title", {homeserver: searchQuery.homeserver, query: searchQuery.name})} + ? {t("Organisms.PublicRooms.result_title", {homeserver: searchQuery.homeserver})} + : {t("Organisms.PublicRooms.search_result_title", {homeserver: searchQuery.homeserver, query: searchQuery.name})} ) } { searchQuery.error && ( diff --git a/src/app/organisms/room/RoomSettings.jsx b/src/app/organisms/room/RoomSettings.jsx index dbd597f10..3504aa8eb 100644 --- a/src/app/organisms/room/RoomSettings.jsx +++ b/src/app/organisms/room/RoomSettings.jsx @@ -94,9 +94,9 @@ function GeneralSettings({ roomId }) { variant="danger" onClick={async () => { const isConfirmed = await confirmDialog( - t("RoomSettings.leave_room"), - t("RoomSettings.leave_room_confirm_message", {room_name: room.name}), - t("RoomSettings.leave_room_confirm_button"), + t("Organisms.RoomSettings.leave_room"), + t("Organisms.RoomSettings.leave_room_confirm_message", {room_name: room.name}), + t("Organisms.RoomSettings.leave_room_confirm_button"), 'danger', ); if (!isConfirmed) return; @@ -108,15 +108,15 @@ function GeneralSettings({ roomId }) {
- {t("RoomSettings.notification_header")} + {t("Organisms.RoomSettings.notification_header")}
- {t("RoomSettings.visibility_header")} + {t("Organisms.RoomSettings.visibility_header")}
- {t("RoomSettings.address_header")} + {t("Organisms.RoomSettings.address_header")}
@@ -131,11 +131,11 @@ function SecuritySettings({ roomId }) { return ( <>
- {t("RoomSettings.encryption_header")} + {t("Organisms.RoomSettings.encryption_header")}
- {t("RoomSettings.message_history_header")} + {t("Organisms.RoomSettings.message_history_header")}
@@ -187,7 +187,7 @@ function RoomSettings({ roomId }) { {`${room.name}`} - — {t("RoomSettings.room_settings_subtitle")} + — {t("Organisms.RoomSettings.room_settings_subtitle")} diff --git a/src/app/organisms/room/RoomViewContent.jsx b/src/app/organisms/room/RoomViewContent.jsx index 9a5ec5c1c..c856e56ec 100644 --- a/src/app/organisms/room/RoomViewContent.jsx +++ b/src/app/organisms/room/RoomViewContent.jsx @@ -68,14 +68,14 @@ function RoomIntroContainer({ event, timeline }) { let avatarSrc = room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop'); avatarSrc = isDM ? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc; - const heading = isDM ? room.name : t("RoomViewContent.welcome_to_room", {room_name: room.name}); + const heading = isDM ? room.name : t("Organisms.RoomViewContent.welcome_to_room", {room_name: room.name}); const topic = twemojify(roomTopic || '', undefined, true); const nameJsx = twemojify(room.name); const desc = isDM ? ( <> }} /> @@ -86,7 +86,7 @@ function RoomIntroContainer({ event, timeline }) { : ( <> }} /> @@ -112,7 +112,7 @@ function RoomIntroContainer({ event, timeline }) { name={room.name} heading={twemojify(heading)} desc={desc} - time={event ? t("RoomViewContent.created_on", {date: event.getDate(), formatParams: { date: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}}}) : null} + time={event ? t("Organisms.RoomViewContent.created_on", {date: event.getDate(), formatParams: { date: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}}}) : null} /> ); } @@ -539,7 +539,7 @@ function RoomViewContent({ eventId, roomTimeline }) { && readUptoEvent.getTs() < mEvent.getTs()); if (unreadDivider) { isNewEvent = true; - tl.push(); + tl.push(); itemCountIndex += 1; if (jumpToItemIndex === -1) jumpToItemIndex = itemCountIndex; } diff --git a/src/app/organisms/room/RoomViewFloating.jsx b/src/app/organisms/room/RoomViewFloating.jsx index bd8c16766..cda05fe87 100644 --- a/src/app/organisms/room/RoomViewFloating.jsx +++ b/src/app/organisms/room/RoomViewFloating.jsx @@ -124,17 +124,17 @@ function RoomViewFloating({ <>
0 ? ' room-view__typing--open' : ''}`}>
}} /> @@ -142,7 +142,7 @@ function RoomViewFloating({
diff --git a/src/app/organisms/room/RoomViewHeader.jsx b/src/app/organisms/room/RoomViewHeader.jsx index af4ae2c2d..951f1bb2f 100644 --- a/src/app/organisms/room/RoomViewHeader.jsx +++ b/src/app/organisms/room/RoomViewHeader.jsx @@ -98,9 +98,9 @@ function RoomViewHeader({ roomId }) { - toggleRoomSettings(tabText.SEARCH)} tooltip={t("RoomViewHeader.search_tooltip")} src={SearchIC} /> - - toggleRoomSettings(tabText.MEMBERS)} tooltip={t("RoomViewHeader.members_tooltip")} src={UserIC} /> + toggleRoomSettings(tabText.SEARCH)} tooltip={t("Organisms.RoomViewHeader.search_tooltip")} src={SearchIC} /> + + toggleRoomSettings(tabText.MEMBERS)} tooltip={t("Organisms.RoomViewHeader.members_tooltip")} src={UserIC} /> { tombstoneEvent - ? tombstoneEvent.getContent()?.body ?? t("RoomViewInput.tombstone_replaced") - : t("RoomViewInput.tombstone_permission_denied") + ? tombstoneEvent.getContent()?.body ?? t("Organisms.RoomViewInput.tombstone_replaced") + : t("Organisms.RoomViewInput.tombstone_permission_denied") } ); @@ -338,7 +338,7 @@ function RoomViewInput({ onChange={handleMsgTyping} onPaste={handlePaste} onKeyDown={handleKeyDown} - placeholder={t("RoomViewInput.send_message_placeholder")} + placeholder={t("Organisms.RoomViewInput.send_message_placeholder")} /> @@ -352,7 +352,7 @@ function RoomViewInput({ cords.y -= 250; openEmojiBoard(cords, addEmoji); }} - tooltip={t("RoomViewInput.emoji_tooltip")} + tooltip={t("Organisms.RoomViewInput.emoji_tooltip")} src={EmojiIC} /> @@ -373,7 +373,7 @@ function RoomViewInput({
{attachment.name} - {t("RoomViewInput.file_size", {size: bytesToSize(attachment.size)})} + {t("Organisms.RoomViewInput.file_size", {size: bytesToSize(attachment.size)})}
); @@ -388,7 +388,7 @@ function RoomViewInput({ setReplyTo(null); }} src={CrossIC} - tooltip={t("RoomViewInput.cancel_reply_tooltip")} + tooltip={t("Organisms.RoomViewInput.cancel_reply_tooltip")} size="extra-small" /> }} /> @@ -29,7 +29,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -40,7 +40,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -51,7 +51,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -62,7 +62,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -74,7 +74,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -86,7 +86,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -97,7 +97,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -108,7 +108,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -119,7 +119,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -130,7 +130,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -141,7 +141,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -152,7 +152,7 @@ function getTimelineJSXMessages() { return ( <> }} /> @@ -163,7 +163,7 @@ function getTimelineJSXMessages() { return ( <> }} /> diff --git a/src/app/organisms/search/Search.jsx b/src/app/organisms/search/Search.jsx index 14e8058c5..02a11e12c 100644 --- a/src/app/organisms/search/Search.jsx +++ b/src/app/organisms/search/Search.jsx @@ -229,7 +229,7 @@ function Search() {
- {t("Search.description")} + {t("Organisms.Search.description")}
diff --git a/src/app/organisms/settings/AuthRequest.jsx b/src/app/organisms/settings/AuthRequest.jsx index 3310ac30a..2ddf22cd4 100644 --- a/src/app/organisms/settings/AuthRequest.jsx +++ b/src/app/organisms/settings/AuthRequest.jsx @@ -45,10 +45,10 @@ function AuthRequest({ onComplete, makeRequest }) { lastUsedPassword = undefined; if (!mountStore.getItem()) return; if (err.errcode === 'M_FORBIDDEN') { - setStatus({ error: t("AuthRequest.wrong_password") }); + setStatus({ error: t("Organisms.AuthRequest.wrong_password") }); return; } - setStatus({ error: t("AuthRequest.request_failed") }); + setStatus({ error: t("Organisms.AuthRequest.request_failed") }); } }; @@ -61,7 +61,7 @@ function AuthRequest({ onComplete, makeRequest }) {
(
{twemojify('❌')} - {t("CrossSigning.setup_failed")} + {t("Organisms.CrossSigning.setup_failed")}
); openReusableDialog( - {t("CrossSigning.setup")}, + {t("Organisms.CrossSigning.setup")}, renderFailure, ); }; @@ -59,7 +59,7 @@ function CrossSigningSetup() { const renderSecurityKey = () => (
- {t("CrossSigning.save_security_key_message")} + {t("Organisms.CrossSigning.save_security_key_message")} {key.encodedPrivateKey} @@ -74,7 +74,7 @@ function CrossSigningSetup() { downloadKey(); openReusableDialog( - {t("CrossSigning.security_key_dialog_title")}, + {t("Organisms.CrossSigning.security_key_dialog_title")}, () => renderSecurityKey(), ); }; @@ -130,9 +130,9 @@ function CrossSigningSetup() {
- {t("CrossSigning.security_key_generation_message")} + {t("Organisms.CrossSigning.security_key_generation_message")} - {genWithPhrase !== false && } + {genWithPhrase !== false && } {genWithPhrase === false && }
{t("common.or")} @@ -150,13 +150,13 @@ function CrossSigningSetup() { disabled={genWithPhrase !== undefined} > - {t("CrossSigning.security_phrase_message")} + {t("Organisms.CrossSigning.security_phrase_message")} {errors.confirmPhrase && {errors.confirmPhrase}} - {genWithPhrase !== true && } + {genWithPhrase !== true && } {genWithPhrase === true && } )} @@ -193,9 +193,9 @@ function CrossSigningReset() { return (
{twemojify('✋🧑‍🚒🤚')} - {t("CrossSigning.reset_keys_subtitle")} + {t("Organisms.CrossSigning.reset_keys_subtitle")} - {t("CrossSigning.reset_keys_message")} + {t("Organisms.CrossSigning.reset_keys_message")}
@@ -215,8 +215,8 @@ function CrossSignin() { const isCSEnabled = useCrossSigningStatus(); return ( {t("CrossSigning.setup_message")}} + title={t("Organisms.CrossSigning.title")} + content={{t("Organisms.CrossSigning.setup_message")}} options={( isCSEnabled ? diff --git a/src/app/organisms/settings/DeviceManage.jsx b/src/app/organisms/settings/DeviceManage.jsx index 175ca1b79..db6591a15 100644 --- a/src/app/organisms/settings/DeviceManage.jsx +++ b/src/app/organisms/settings/DeviceManage.jsx @@ -45,7 +45,7 @@ const promptDeviceName = async (deviceName) => new Promise((resolve) => { }; return (
- +
@@ -55,7 +55,7 @@ const promptDeviceName = async (deviceName) => new Promise((resolve) => { }; openReusableDialog( - {t("DeviceManage.edit_session_name_title")}, + {t("Organisms.DeviceManage.edit_session_name_title")}, (requestClose) => renderContent((name) => { isCompleted = true; resolve(name); @@ -95,7 +95,7 @@ function DeviceManage() { }; return ( - +
@@ -105,7 +105,7 @@ function DeviceManage() { }; openReusableDialog( - {t("DeviceManage.edit_session_name_title")}, + {t("Organisms.DeviceManage.edit_session_name_title")}, (requestClose) => renderContent((name) => { isCompleted = true; resolve(name); @@ -132,7 +132,7 @@ function DeviceManage() {
- {t("DeviceManage.loading_devices")} + {t("Organisms.DeviceManage.loading_devices")}
); @@ -155,14 +155,14 @@ function DeviceManage() { const handleRemove = async (device) => { const isConfirmed = await confirmDialog( - t("DeviceManage.logout_device_title", {device: device.display_name}), - t("DeviceManage.logout_device_message", {device: device.display_name}), - t("DeviceManage.logout_device_confirm"), + t("Organisms.DeviceManage.logout_device_title", {device: device.display_name}), + t("Organisms.DeviceManage.logout_device_message", {device: device.display_name}), + t("Organisms.DeviceManage.logout_device_confirm"), 'danger', ); if (!isConfirmed) return; addToProcessing(device); - await authRequest(t("DeviceManage.logout_device_title", {device: device.display_name}), async (auth) => { + await authRequest(t("Organisms.DeviceManage.logout_device_title", {device: device.display_name}), async (auth) => { await mx.deleteDevice(device.device_id, auth); }); @@ -171,7 +171,7 @@ function DeviceManage() { }; const verifyWithKey = async (device) => { - const keyData = await accessSecretStorage(t("DeviceManage.session_verification_title")); + const keyData = await accessSecretStorage(t("Organisms.DeviceManage.session_verification_title")); if (!keyData) return; addToProcessing(device); await mx.checkOwnCrossSigningTrust(); @@ -205,7 +205,7 @@ function DeviceManage() { {displayName} {`${displayName ? ' — ' : ''}${deviceId}`} - {isCurrentDevice && {t("DeviceManage.current_device_label")}} + {isCurrentDevice && {t("Organisms.DeviceManage.current_device_label")}} )} options={ @@ -213,9 +213,9 @@ function DeviceManage() { ? : ( <> - {(isCSEnabled && canVerify) && } - handleRename(device)} src={PencilIC} tooltip={t("DeviceManage.edit_session_name_tooltip")} /> - handleRemove(device)} src={BinIC} tooltip={t("DeviceManage.logout_device_tooltip")}/> + {(isCSEnabled && canVerify) && } + handleRename(device)} src={PencilIC} tooltip={t("Organisms.DeviceManage.edit_session_name_tooltip")} /> + handleRemove(device)} src={BinIC} tooltip={t("Organisms.DeviceManage.logout_device_tooltip")}/> ) } @@ -256,38 +256,38 @@ function DeviceManage() { return (
- {t("DeviceManage.unverified_sessions_title")} + {t("Organisms.DeviceManage.unverified_sessions_title")} {!isCSEnabled && (
)} { unverified.length > 0 ? unverified.map((device) => renderDevice(device, false)) - : {t("DeviceManage.unverified_sessions_none")} + : {t("Organisms.DeviceManage.unverified_sessions_none")} }
{noEncryption.length > 0 && (
- {t("DeviceManage.unencrypted_sessions_title")} + {t("Organisms.DeviceManage.unencrypted_sessions_title")} {noEncryption.map((device) => renderDevice(device, null))}
)}
- {t("DeviceManage.verified_sessions_title")} + {t("Organisms.DeviceManage.verified_sessions_title")} { verified.length > 0 ? verified.map((device, index) => { if (truncated && index >= TRUNCATED_COUNT) return null; return renderDevice(device, true); }) - : {t("DeviceManage.verified_sessions_none")} + : {t("Organisms.DeviceManage.verified_sessions_none")} } { verified.length > TRUNCATED_COUNT && ( )} { deviceList.length > 0 && ( - {t("DeviceManage.session_name_privacy_message")} + {t("Organisms.DeviceManage.session_name_privacy_message")} )}
diff --git a/src/app/organisms/settings/KeyBackup.jsx b/src/app/organisms/settings/KeyBackup.jsx index dd5d257b6..5437968b2 100644 --- a/src/app/organisms/settings/KeyBackup.jsx +++ b/src/app/organisms/settings/KeyBackup.jsx @@ -66,18 +66,18 @@ function CreateKeyBackupDialog({ keyData }) { {done === false && (
- {t("KeyBackup.creating_backup")} + {t("Organisms.KeyBackup.creating_backup")}
)} {done === true && ( <> {twemojify('✅')} - {t("KeyBackup.backup_created")} + {t("Organisms.KeyBackup.backup_created")} )} {done === null && ( <> - {t("KeyBackup.backup_failed")} + {t("Organisms.KeyBackup.backup_failed")} )} @@ -108,7 +108,7 @@ function RestoreKeyBackupDialog({ keyData }) { meBreath = true; }, 200); - setStatus({ message: t("KeyBackup.restoring_progress", {progress: progress.successes, total: progress.total}) }); + setStatus({ message: t("Organisms.KeyBackup.restoring_progress", {progress: progress.successes, total: progress.total}) }); }; try { @@ -120,14 +120,14 @@ function RestoreKeyBackupDialog({ keyData }) { { progressCallback }, ); if (!mountStore.getItem()) return; - setStatus({ done: t("KeyBackup.restore_complete", {progress: info.imported, total: info.total})}); + setStatus({ done: t("Organisms.KeyBackup.restore_complete", {progress: info.imported, total: info.total})}); } catch (e) { if (!mountStore.getItem()) return; if (e.errcode === 'RESTORE_BACKUP_ERROR_BAD_KEY') { deletePrivateKey(keyData.keyId); - setStatus({ error: t("KeyBackup.restore_failed_bad_key"), errorCode: 'BAD_KEY' }); + setStatus({ error: t("Organisms.KeyBackup.restore_failed_bad_key"), errorCode: 'BAD_KEY' }); } else { - setStatus({ error: t("KeyBackup.restore_failed_unknown"), errCode: 'UNKNOWN' }); + setStatus({ error: t("Organisms.KeyBackup.restore_failed_unknown"), errCode: 'UNKNOWN' }); } } }; @@ -142,7 +142,7 @@ function RestoreKeyBackupDialog({ keyData }) { {(status === false || status.message) && (
- {status.message ?? t("KeyBackup.restoring")} + {status.message ?? t("Organisms.KeyBackup.restoring")}
)} {status.done && ( @@ -187,8 +187,8 @@ function DeleteKeyBackupDialog({ requestClose }) { return (
{twemojify('🗑')} - {t("KeyBackup.delete_key_backup_subtitle")} - {t("KeyBackup.delete_key_backup_message")} + {t("Organisms.KeyBackup.delete_key_backup_subtitle")} + {t("Organisms.KeyBackup.delete_key_backup_message")} { isDeleting ? @@ -231,28 +231,28 @@ function KeyBackup() { }, [isCSEnabled]); const openCreateKeyBackup = async () => { - const keyData = await accessSecretStorage(t('KeyBackup.create_backup_title')); + const keyData = await accessSecretStorage(t('Organisms.KeyBackup.create_backup_title')); if (keyData === null) return; openReusableDialog( - {t('KeyBackup.create_backup_title')}, + {t('Organisms.KeyBackup.create_backup_title')}, () => , () => fetchKeyBackupVersion(), ); }; const openRestoreKeyBackup = async () => { - const keyData = await accessSecretStorage(t('KeyBackup.restore_backup_title')); + const keyData = await accessSecretStorage(t('Organisms.KeyBackup.restore_backup_title')); if (keyData === null) return; openReusableDialog( - {t('KeyBackup.restore_backup_title')}, + {t('Organisms.KeyBackup.restore_backup_title')}, () => , ); }; const openDeleteKeyBackup = () => openReusableDialog( - {t('KeyBackup.delete_key_backup_title')}, + {t('Organisms.KeyBackup.delete_key_backup_title')}, (requestClose) => ( { @@ -265,28 +265,28 @@ function KeyBackup() { const renderOptions = () => { if (keyBackup === undefined) return ; - if (keyBackup === null) return ; + if (keyBackup === null) return ; return ( <> - - + + ); }; return ( - {t("KeyBackup.encrypted_messages_backup_description")} + {t("Organisms.KeyBackup.encrypted_messages_backup_description")} {!isCSEnabled && ( )} diff --git a/src/app/organisms/settings/Settings.jsx b/src/app/organisms/settings/Settings.jsx index bc8f5b163..4d6ef2077 100644 --- a/src/app/organisms/settings/Settings.jsx +++ b/src/app/organisms/settings/Settings.jsx @@ -53,26 +53,26 @@ function AppearanceSection() {
Theme { toggleSystemTheme(); updateState({}); }} /> )} - content={{t("Settings.theme.follow_system.description")}} + content={{t("Organisms.Settings.theme.follow_system.description")}} /> {!settings.useSystemTheme && ( settings.setTheme(index)} /> @@ -83,34 +83,34 @@ function AppearanceSection() {
Room messages { toggleMarkdown(); updateState({}); }} /> )} - content={{t("Settings.markdown.description")}} + content={{t("Organisms.Settings.markdown.description")}} /> { toggleMembershipEvents(); updateState({}); }} /> )} - content={{t("Settings.hide_membership_events.description")}} + content={{t("Organisms.Settings.hide_membership_events.description")}} /> { toggleNickAvatarEvents(); updateState({}); }} /> )} - content={{t("Settings.hide_nickname_avatar_events.description")}} + content={{t("Organisms.Settings.hide_nickname_avatar_events.description")}} />
@@ -154,21 +154,21 @@ function NotificationsSection() { return (
- {t("Settings.notifications_and_sound.title")} + {t("Organisms.Settings.notifications_and_sound.title")} {t("Settings.notifications_and_sound.desktop.description")}} + content={{t("Organisms.Settings.notifications_and_sound.desktop.description")}} /> { toggleNotificationSounds(); updateState({}); }} /> )} - content={{t("Settings.notifications_and_sound.desktop.description")}} + content={{t("Organisms.Settings.notifications_and_sound.desktop.description")}} />
); @@ -181,27 +181,27 @@ function SecuritySection() { return (
- {t("Settings.security.cross_signing.title")} + {t("Organisms.Settings.security.cross_signing.title")}
- {t("Settings.security.export_import_encryption_keys.title")} + {t("Organisms.Settings.security.export_import_encryption_keys.title")} - {t("Settings.security.export_encryption_keys.description")} + {t("Organisms.Settings.security.export_encryption_keys.description")} )} /> - {t("Settings.security.import_encryption_keys.description")} + {t("Organisms.Settings.security.import_encryption_keys.description")} )} @@ -218,7 +218,7 @@ function AboutSection() { return (
- {t("Settings.about.application")} + {t("Organisms.Settings.about.application")}
Cinny logo
@@ -236,7 +236,7 @@ function AboutSection() {
- {t("Settings.about.credits")} + {t("Organisms.Settings.about.credits")}