From fc0dff4ec2f09acad5e6080cb0d2707da7c01165 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Dec 2022 21:27:38 +0100 Subject: [PATCH 01/17] feat: implement interface language --- package-lock.json | 136 +++++++++++++++- package.json | 3 + src/App/App.js | 25 +++ src/common/index.js | 2 + src/common/interfaceLanguages.json | 150 ++++++++++++++++++ src/index.js | 18 +++ src/routes/Settings/Settings.js | 39 ++--- .../Settings/useProfileSettingsInputs.js | 12 +- 8 files changed, 355 insertions(+), 30 deletions(-) create mode 100644 src/common/interfaceLanguages.json diff --git a/package-lock.json b/package-lock.json index ef6320fc5..9b86afa50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "eventemitter3": "4.0.7", "filter-invalid-dom-props": "2.1.0", "hat": "0.0.3", + "i18next": "^22.4.3", "langs": "^2.0.0", "lodash.debounce": "4.0.8", "lodash.intersection": "4.4.0", @@ -32,8 +33,10 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-focus-lock": "2.9.1", + "react-i18next": "^12.1.1", "react-is": "18.2.0", "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "stremio-translations": "git+https://git@github.com/Stremio/stremio-translations.git#e47383bbbb3da8708bb727acd73a093604cdf5c8", "url": "0.11.0" }, "devDependencies": { @@ -7009,6 +7012,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-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -7153,6 +7164,39 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.3.tgz", + "integrity": "sha512-rnAabD3+i/rMzdg85Eq4VkZjy0Uxe33J1069IQ4R6+cpcM+wL4lWMRClfSweINA0QEfqzSdsfsyLO7SnGAF4fg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.20.6" + } + }, + "node_modules/i18next/node_modules/@babel/runtime": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -11920,6 +11964,27 @@ } } }, + "node_modules/react-i18next": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", + "integrity": "sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==", + "dependencies": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -11982,9 +12047,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -12781,6 +12846,12 @@ "node": ">= 0.6" } }, + "node_modules/stremio-translations": { + "version": "1.43.15", + "resolved": "git+https://git@github.com/Stremio/stremio-translations.git#e47383bbbb3da8708bb727acd73a093604cdf5c8", + "integrity": "sha512-33qlTmLytSgTAf0fFO242pq2Ir52xREhMs0AGCBmaft4maceuM1cRf/lRJswwu/yMkEBH92C2hGkLGpd9qtw2A==", + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -13702,6 +13773,14 @@ "resolved": "https://registry.npmjs.org/video-name-parser/-/video-name-parser-1.4.6.tgz", "integrity": "sha512-ZdeYjh8X4ms1EzjY/UoiTZ6JWbi8SYyOPGY0jESSLq2BAmdc5sZHi+F8J19Qz0y7H1WSpaltojsCkO1p2dH4YA==" }, + "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/vtt.js": { "version": "0.13.0", "resolved": "git+ssh://git@github.com/jaruba/vtt.js.git#e4f5f5603730866bacb174a93f51b734c9f29e6a", @@ -20111,6 +20190,14 @@ } } }, + "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-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -20212,6 +20299,24 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "i18next": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.3.tgz", + "integrity": "sha512-rnAabD3+i/rMzdg85Eq4VkZjy0Uxe33J1069IQ4R6+cpcM+wL4lWMRClfSweINA0QEfqzSdsfsyLO7SnGAF4fg==", + "requires": { + "@babel/runtime": "^7.20.6" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -23691,6 +23796,15 @@ "use-sidecar": "^1.1.2" } }, + "react-i18next": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", + "integrity": "sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==", + "requires": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + } + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -23741,9 +23855,9 @@ } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.14.5", @@ -24393,6 +24507,11 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, + "stremio-translations": { + "version": "git+https://git@github.com/Stremio/stremio-translations.git#e47383bbbb3da8708bb727acd73a093604cdf5c8", + "integrity": "sha512-33qlTmLytSgTAf0fFO242pq2Ir52xREhMs0AGCBmaft4maceuM1cRf/lRJswwu/yMkEBH92C2hGkLGpd9qtw2A==", + "from": "stremio-translations@git+https://git@github.com/Stremio/stremio-translations.git#e47383bbbb3da8708bb727acd73a093604cdf5c8" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -25067,6 +25186,11 @@ "resolved": "https://registry.npmjs.org/video-name-parser/-/video-name-parser-1.4.6.tgz", "integrity": "sha512-ZdeYjh8X4ms1EzjY/UoiTZ6JWbi8SYyOPGY0jESSLq2BAmdc5sZHi+F8J19Qz0y7H1WSpaltojsCkO1p2dH4YA==" }, + "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==" + }, "vtt.js": { "version": "git+ssh://git@github.com/jaruba/vtt.js.git#e4f5f5603730866bacb174a93f51b734c9f29e6a", "integrity": "sha512-RXV60lPGrmjuRcV/jRuydLC2thMaMlmK4Vc3DtBmVSotFA3986sgW0H5AH9IUmHzQo4bFR2gELYLcfwVh7Dqow==", diff --git a/package.json b/package.json index 2242f5f80..e0add18d5 100755 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "eventemitter3": "4.0.7", "filter-invalid-dom-props": "2.1.0", "hat": "0.0.3", + "i18next": "^22.4.3", "langs": "^2.0.0", "lodash.debounce": "4.0.8", "lodash.intersection": "4.4.0", @@ -35,8 +36,10 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-focus-lock": "2.9.1", + "react-i18next": "^12.1.1", "react-is": "18.2.0", "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "stremio-translations": "git+https://git@github.com/Stremio/stremio-translations.git#e47383bbbb3da8708bb727acd73a093604cdf5c8", "url": "0.11.0" }, "devDependencies": { diff --git a/src/App/App.js b/src/App/App.js index c0a79b727..97e32b920 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -2,6 +2,7 @@ require('spatial-navigation-polyfill'); const React = require('react'); +const { useTranslation } = require('react-i18next'); const { Router } = require('stremio-router'); const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services'); const { NotFound } = require('stremio/routes'); @@ -13,6 +14,7 @@ const routerViewsConfig = require('./routerViewsConfig'); const styles = require('./styles'); const App = () => { + const { i18n } = useTranslation(); const onPathNotMatch = React.useCallback(() => { return NotFound; }, []); @@ -90,6 +92,21 @@ const App = () => { }; }, []); React.useEffect(() => { + const onCoreEvent = ({ event, args }) => { + switch (event) { + case 'SettingsUpdated': { + if (args && args.settings && typeof args.settings.interfaceLanguage === 'string') { + i18n.changeLanguage(args.settings.interfaceLanguage); + } + break; + } + } + }; + const onCtxState = (state) => { + if (state && state.profile && state.profile.settings && typeof state.profile.settings.interfaceLanguage === 'string') { + i18n.changeLanguage(state.profile.settings.interfaceLanguage); + } + }; if (services.core.active) { services.core.transport.dispatch({ action: 'Ctx', @@ -109,7 +126,15 @@ const App = () => { action: 'SyncLibraryWithAPI' } }); + services.core.transport.on('CoreEvent', onCoreEvent); + services.core.transport + .getState('ctx') + .then(onCtxState) + .catch((e) => console.error(e)); } + return () => { + services.core.transport.off('CoreEvent', onCoreEvent); + }; }, [initialized]); return ( diff --git a/src/common/index.js b/src/common/index.js index 22dcad52a..5110dd669 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -27,6 +27,7 @@ const comparatorWithPriorities = require('./comparatorWithPriorities'); const CONSTANTS = require('./CONSTANTS'); const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender'); const getVisibleChildrenRange = require('./getVisibleChildrenRange'); +const interfaceLanguages = require('./interfaceLanguages'); const languageNames = require('./languageNames'); const routesRegexp = require('./routesRegexp'); const useAnimationFrame = require('./useAnimationFrame'); @@ -70,6 +71,7 @@ module.exports = { withCoreSuspender, useCoreSuspender, getVisibleChildrenRange, + interfaceLanguages, languageNames, routesRegexp, useAnimationFrame, diff --git a/src/common/interfaceLanguages.json b/src/common/interfaceLanguages.json new file mode 100644 index 000000000..235193f0d --- /dev/null +++ b/src/common/interfaceLanguages.json @@ -0,0 +1,150 @@ +[ + { + "name": "العربية", + "codes": ["ar-AR", "ara"] + }, + { + "name": "български език", + "codes": ["bg-BG", "bul"] + }, + { + "name": "català", + "codes": ["ca-CA", "cat"] + }, + { + "name": "čeština", + "codes": ["cs-CZ", "ces"] + }, + { + "name": "dansk", + "codes": ["da-DK", "dan"] + }, + { + "name": "Deutsch", + "codes": ["de-DE", "deu"] + }, + { + "name": "ελληνικά", + "codes": ["el-GR", "ell"] + }, + { + "name": "English", + "codes": ["en-US", "eng"] + }, + { + "name": "Esperanto", + "codes": ["eo-EO", "epo"] + }, + { + "name": "español", + "codes": ["es-ES", "spa"] + }, + { + "name": "euskara", + "codes": ["eu-ES", "eus"] + }, + { + "name": "فارسی", + "codes": ["fa-IR", "fas"] + }, + { + "name": "Français", + "codes": ["fr-FR", "fre"] + }, + { + "name": "עברית", + "codes": ["he-IL", "heb"] + }, + { + "name": "हिन्दी", + "codes": ["hi-IN", "hin"] + }, + { + "name": "hrvatski jezik", + "codes": ["hr-HR", "hrv"] + }, + { + "name": "magyar", + "codes": ["hu-HU", "hun"] + }, + { + "name": "Bahasa Indonesia", + "codes": ["id-ID", "ind"] + }, + { + "name": "italiano", + "codes": ["it-IT", "ita"] + }, + { + "name": "македонски јазик", + "codes": ["mk-MK", "mkd"] + }, + { + "name": "ဗမာစာ", + "codes": ["my-BM", "mya"] + }, + { + "name": "Norsk bokmål", + "codes": ["nb-NO", "nob"] + }, + { + "name": "Nederlands", + "codes": ["nl-NL", "nld"] + }, + { + "name": "Norsk nynorsk", + "codes": ["nn-NO", "nno"] + }, + { + "name": "język polski", + "codes": ["pl-PL", "pol"] + }, + { + "name": "português Brazil", + "codes": ["pt-BR", "por"] + }, + { + "name": "português", + "codes": ["pt-PT", "por"] + }, + { + "name": "русский язык", + "codes": ["ru-RU", "rus"] + }, + { + "name": "Svenska", + "codes": ["sv-SE", "swe"] + }, + { + "name": "slovenski jezik", + "codes": ["sl-SL", "slv"] + }, + { + "name": "српски језик", + "codes": ["sr-RS", "srp"] + }, + { + "name": "తెలుగు", + "codes": ["te-IN", "tel"] + }, + { + "name": "Türkçe", + "codes": ["tr-TR", "tur"] + }, + { + "name": "українська мова", + "codes": ["uk-UA", "ukr"] + }, + { + "name": "中文(中华人民共和国)", + "codes": ["zh-CN", "zho"] + }, + { + "name": "中文(香港特别行政區)", + "codes": ["zh-HK", "zho"] + }, + { + "name": "中文(台灣)", + "codes": ["zh-TW", "zho"] + } +] \ No newline at end of file diff --git a/src/index.js b/src/index.js index 27840b0d0..007665568 100755 --- a/src/index.js +++ b/src/index.js @@ -13,8 +13,26 @@ if (browser?.platform?.type === 'desktop') { const React = require('react'); const ReactDOM = require('react-dom/client'); +const i18n = require('i18next'); +const { initReactI18next } = require('react-i18next'); +const stremioTranslations = require('stremio-translations'); const App = require('./App'); +const translations = Object.fromEntries(Object.entries(stremioTranslations()).map(([key, value]) => [key, { + translation: value +}])); + +i18n + .use(initReactI18next) + .init({ + resources: translations, + lng: 'en-US', + fallbackLng: 'en-US', + interpolation: { + escapeValue: false + } + }); + const root = ReactDOM.createRoot(document.getElementById('app')); root.render(); diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 3b99b20fb..d8942b804 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -3,6 +3,7 @@ const React = require('react'); const classnames = require('classnames'); const throttle = require('lodash.throttle'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const { useRouteFocused } = require('stremio-router'); const { useServices } = require('stremio/services'); @@ -17,6 +18,7 @@ const STREAMING_SECTION = 'streaming'; const SHORTCUTS_SECTION = 'shortcuts'; const Settings = () => { + const { t } = useTranslation(); const { core } = useServices(); const { routeFocused } = useRouteFocused(); const profile = useProfile(); @@ -138,17 +140,17 @@ const Settings = () => {
- - - -
App Version: {process.env.VERSION}
@@ -161,7 +163,7 @@ const Settings = () => {
-
General
+
{ t('SETTINGS_NAV_GENERAL') }
{
@@ -266,7 +267,7 @@ const Settings = () => {
-
Player
+
{ t('SETTINGS_NAV_PLAYER') }
Subtitles language
@@ -384,15 +385,15 @@ const Settings = () => {
-
Streaming Server
+
{ t('SETTINGS_NAV_STREAMING') }
-
Status
+
{ t('STATUS') }
@@ -452,10 +453,10 @@ const Settings = () => { }
-
Shortcuts
+
{ t('SETTINGS_NAV_SHORTCUTS') }
-
Play / Pause
+
{ t('SETTINGS_SHORTCUT_PLAY_PAUSE') }
Space @@ -487,7 +488,7 @@ const Settings = () => {
-
Volume Up
+
{ t('SETTINGS_SHORTCUT_VOLUME_UP') }
@@ -495,7 +496,7 @@ const Settings = () => {
-
Volume Down
+
{ t('SETTINGS_SHORTCUT_VOLUME_DOWN') }
@@ -545,7 +546,7 @@ const Settings = () => {
-
Close Menu or Modal
+
{ t('SETTINGS_SHORTCUT_EXIT_BACK') }
Esc diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index e861f15ed..417e4ec89 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -2,17 +2,19 @@ const React = require('react'); const { useServices } = require('stremio/services'); -const { CONSTANTS, languageNames } = require('stremio/common'); +const { CONSTANTS, interfaceLanguages, languageNames } = require('stremio/common'); const useProfileSettingsInputs = (profile) => { const { core } = useServices(); // TODO combine those useMemo in one const interfaceLanguageSelect = React.useMemo(() => ({ - options: Object.keys(languageNames).map((code) => ({ - value: code, - label: languageNames[code] + options: interfaceLanguages.map(({ name, codes }) => ({ + value: codes[0], + label: name, })), - selected: [profile.settings.interfaceLanguage], + selected: [ + interfaceLanguages.find(({ codes }) => codes[1] === profile.settings.interfaceLanguage)?.codes?.[0] || profile.settings.interfaceLanguage + ], onSelect: (event) => { core.transport.dispatch({ action: 'Ctx', From f06f21ce75eab026e5f88e3d426e0be9babbf1e7 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Dec 2022 22:11:51 +0100 Subject: [PATCH 02/17] refactor: translate more strings --- src/common/MainNavBars/MainNavBars.js | 4 +- src/common/MetaPreview/MetaPreview.js | 12 +++--- .../NavMenu/NavMenuContent.js | 38 ++++++++++--------- .../HorizontalNavBar/SearchBar/SearchBar.js | 29 ++++++++------ .../NavBar/VerticalNavBar/VerticalNavBar.js | 4 +- src/common/SharePrompt/SharePrompt.js | 4 +- .../StreamingServerWarning.js | 4 +- src/routes/Addons/Addon/Addon.js | 10 +++-- src/routes/Addons/Addons.js | 6 ++- src/routes/Board/Board.js | 4 +- .../MetaDetails/StreamsList/StreamsList.js | 6 ++- src/routes/Player/SpeedMenu/SpeedMenu.js | 4 +- src/routes/Search/Search.js | 8 ++-- 13 files changed, 80 insertions(+), 53 deletions(-) diff --git a/src/common/MainNavBars/MainNavBars.js b/src/common/MainNavBars/MainNavBars.js index f9311c6d6..87d028f94 100644 --- a/src/common/MainNavBars/MainNavBars.js +++ b/src/common/MainNavBars/MainNavBars.js @@ -10,8 +10,8 @@ const TABS = [ { id: 'board', label: 'Board', icon: 'ic_board', href: '#/' }, { id: 'discover', label: 'Discover', icon: 'ic_discover', href: '#/discover' }, { id: 'library', label: 'Library', icon: 'ic_library', href: '#/library' }, - { id: 'settings', label: 'Settings', icon: 'ic_settings', href: '#/settings' }, - { id: 'addons', label: 'Addons', icon: 'ic_addons', href: '#/addons' } + { id: 'settings', label: 'SETTINGS', icon: 'ic_settings', href: '#/settings' }, + { id: 'addons', label: 'ADDONS', icon: 'ic_addons', href: '#/addons' } ]; const MainNavBars = React.memo(({ className, route, query, children }) => { diff --git a/src/common/MetaPreview/MetaPreview.js b/src/common/MetaPreview/MetaPreview.js index f9ec1bef3..a0c3d9fd8 100644 --- a/src/common/MetaPreview/MetaPreview.js +++ b/src/common/MetaPreview/MetaPreview.js @@ -4,6 +4,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const UrlUtils = require('url'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const Button = require('stremio/common/Button'); const Image = require('stremio/common/Image'); @@ -24,6 +25,7 @@ const ALLOWED_LINK_REDIRECTS = [ ]; const MetaPreview = ({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary }) => { + const { t } = useTranslation(); const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false); const linksGroups = React.useMemo(() => { return Array.isArray(links) ? @@ -192,7 +194,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele @@ -204,7 +206,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele @@ -216,7 +218,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele @@ -229,13 +231,13 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele { shareModalOpen ? - + { + const { t } = useTranslation(); const { core } = useServices(); const profile = useProfile(); const { createTorrentFromMagnet } = useTorrent(); @@ -48,43 +50,43 @@ const NavMenuContent = ({ onClick }) => {
{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}
-
-
- - - -
- - -
diff --git a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js index dd76c8e84..f454b541d 100644 --- a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js +++ b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js @@ -3,6 +3,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const { useRouteFocused } = require('stremio-router'); const Button = require('stremio/common/Button'); @@ -12,6 +13,7 @@ const { withCoreSuspender } = require('stremio/common/CoreSuspender'); const styles = require('./styles'); const SearchBar = ({ className, query, active }) => { + const { t } = useTranslation(); const routeFocused = useRouteFocused(); const { createTorrentFromMagnet } = useTorrent(); const searchInputRef = React.useRef(null); @@ -46,7 +48,7 @@ const SearchBar = ({ className, query, active }) => { ref={searchInputRef} className={styles['search-input']} type={'text'} - placeholder={'Search or paste link'} + placeholder={t('SEARCH_PLACEHOLDER')} defaultValue={query} tabIndex={-1} onChange={queryInputOnChange} @@ -54,7 +56,7 @@ const SearchBar = ({ className, query, active }) => { /> :
-
Search or paste link
+
{ t('SEARCH_PLACEHOLDER') }
} - -); +const SearchBarFallback = ({ className }) => { + const { t } = useTranslation(); + return ( + + ); +}; SearchBarFallback.propTypes = SearchBar.propTypes; diff --git a/src/common/NavBar/VerticalNavBar/VerticalNavBar.js b/src/common/NavBar/VerticalNavBar/VerticalNavBar.js index 4c0ef099c..6e48963d2 100644 --- a/src/common/NavBar/VerticalNavBar/VerticalNavBar.js +++ b/src/common/NavBar/VerticalNavBar/VerticalNavBar.js @@ -3,10 +3,12 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const NavTabButton = require('./NavTabButton'); const styles = require('./styles'); const VerticalNavBar = React.memo(({ className, selected, tabs }) => { + const { t } = useTranslation(); return (
diff --git a/src/common/StreamingServerWarning/StreamingServerWarning.js b/src/common/StreamingServerWarning/StreamingServerWarning.js index b0775b2e6..7ff2ff6b9 100644 --- a/src/common/StreamingServerWarning/StreamingServerWarning.js +++ b/src/common/StreamingServerWarning/StreamingServerWarning.js @@ -4,12 +4,14 @@ const React = require('react'); const { useServices } = require('stremio/services'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Button = require('stremio/common/Button'); const useProfile = require('stremio/common/useProfile'); const { withCoreSuspender } = require('stremio/common/CoreSuspender'); const styles = require('./styles'); const StreamingServerWarning = ({ className }) => { + const { t } = useTranslation(); const { core } = useServices(); const profile = useProfile(); const onLaterClick = React.useCallback(() => { @@ -48,7 +50,7 @@ const StreamingServerWarning = ({ className }) => { return (
-
Streaming server is not available.
+
{ t('SETTINGS_SERVER_UNAVAILABLE') }
diff --git a/src/routes/Addons/Addon/Addon.js b/src/routes/Addons/Addon/Addon.js index a330f9d8d..fed54fcfb 100644 --- a/src/routes/Addons/Addon/Addon.js +++ b/src/routes/Addons/Addon/Addon.js @@ -3,11 +3,13 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const { Button, Image } = require('stremio/common'); const styles = require('./styles'); const Addon = ({ className, id, name, version, logo, description, types, installed, onToggle, onShare, dataset }) => { + const { t } = useTranslation(); const toggleButtonOnClick = React.useCallback((event) => { if (typeof onToggle === 'function') { onToggle({ @@ -82,12 +84,12 @@ const Addon = ({ className, id, name, version, logo, description, types, install }
- -
diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index aa513daaf..95ecbea86 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -3,6 +3,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState, withCoreSuspender } = require('stremio/common'); const Addon = require('./Addon'); @@ -13,6 +14,7 @@ const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); const Addons = ({ urlParams, queryParams }) => { + const { t } = useTranslation(); const installedAddons = useInstalledAddons(urlParams); const remoteAddons = useRemoteAddons(urlParams); const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams); @@ -92,7 +94,7 @@ const Addons = ({ urlParams, queryParams }) => {
@@ -212,7 +214,7 @@ const Addons = ({ urlParams, queryParams }) => { sharedAddon !== null ?
{ + const { t } = useTranslation(); const streamingServer = useStreamingServer(); const continueWatchingPreview = useContinueWatchingPreview(); const [board, loadBoardRows] = useBoard(); @@ -42,7 +44,7 @@ const Board = () => { continueWatchingPreview.libraryItems.length > 0 ? { + const { t } = useTranslation(); const { core } = useServices(); const [selectedAddon, setSelectedAddon] = React.useState(ALL_ADDONS_KEY); const onAddonSelected = React.useCallback((event) => { @@ -115,9 +117,9 @@ const StreamsList = ({ className, ...props }) => {
} -
); diff --git a/src/routes/Player/SpeedMenu/SpeedMenu.js b/src/routes/Player/SpeedMenu/SpeedMenu.js index 836a9ac80..81b18e66d 100644 --- a/src/routes/Player/SpeedMenu/SpeedMenu.js +++ b/src/routes/Player/SpeedMenu/SpeedMenu.js @@ -3,12 +3,14 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Option = require('./Option'); const styles = require('./styles'); const RATES = Array.from(Array(8).keys(), (n) => n * 0.25 + 0.25).reverse(); const SpeedMenu = ({ className, playbackSpeed, onPlaybackSpeedChanged }) => { + const { t } = useTranslation(); const onMouseDown = React.useCallback((event) => { event.nativeEvent.speedMenuClosePrevented = true; }, []); @@ -20,7 +22,7 @@ const SpeedMenu = ({ className, playbackSpeed, onPlaybackSpeedChanged }) => { return (
- Playback Speed + { t('PLAYBACK_SPEED') }
{ diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index 3ea5bd3b5..e5304d463 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -4,6 +4,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const debounce = require('lodash.debounce'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const { Image, MainNavBars, MetaRow, MetaItem, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useSearch = require('./useSearch'); @@ -12,6 +13,7 @@ const styles = require('./styles'); const THRESHOLD = 100; const Search = ({ queryParams }) => { + const { t } = useTranslation(); const [search, loadSearchRows] = useSearch(queryParams); const query = React.useMemo(() => { return search.selected !== null ? @@ -50,11 +52,11 @@ const Search = ({ queryParams }) => {
-
Search for movies, series, YouTube and TV channels
+
{ t('SEARCH_EXPLANATION_CONTENT') }
-
Search for actors, directors and writers
+
{ t('SEARCH_EXPLANATION_PEOPLE') }
: @@ -65,7 +67,7 @@ const Search = ({ queryParams }) => { src={require('/images/empty.png')} alt={' '} /> -
No addons were requested for catalogs!
+
{ t('STREMIO_TV_SEARCH_NO_ADDONS') }
: search.catalogs.map((catalog, index) => { From 9938e3064872b43abe7ca86a117444fb585120da Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Dec 2022 22:42:12 +0100 Subject: [PATCH 03/17] refactor: add more translations --- src/routes/Addons/Addons.js | 4 ++-- src/routes/Settings/Settings.js | 36 ++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index 95ecbea86..86ccb6140 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -31,13 +31,13 @@ const Addons = ({ urlParams, queryParams }) => { return [ { className: styles['cancel-button'], - label: 'Cancel', + label: t('BUTTON_CANCEL'), props: { onClick: closeAddAddonModal } }, { - label: 'Add', + label: t('ADDON_ADD'), props: { onClick: addAddonOnSubmit } diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index d8942b804..bd6a79ae8 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -233,7 +233,7 @@ const Settings = () => {
-
Calendar
+
{ t('Calendar') }
-
-
@@ -256,13 +256,13 @@ const Settings = () => {
-
-
@@ -270,7 +270,7 @@ const Settings = () => {
{ t('SETTINGS_NAV_PLAYER') }
-
Subtitles language
+
{ t('SETTINGS_SUBTITLES_LANGUAGE') }
{
-
Subtitles size
+
{ t('SETTINGS_SUBTITLES_SIZE') }
{
-
Subtitles text color
+
{ t('SETTINGS_SUBTITLES_COLOR') }
{
-
Subtitles background color
+
{ t('SETTINGS_SUBTITLES_COLOR_BACKGROUND') }
{
-
Subtitles outline color
+
{ t('SETTINGS_SUBTITLES_COLOR_OUTLINE') }
{
-
Audio Language
+
{ t('SETTINGS_DEFAULT_AUDIO_TRACK') }
{
-
Auto-play next episode
+
{ t('AUTO_PLAY') }
{
-
Hardware-accelerated decoding
+
{ t('SETTINGS_HWDEC') }
{
-
Toggle Fullscreen
+
{ t('SETTINGS_SHORTCUT_FULLSCREEN') }
F From 7ed6081b746fa023772d6730f7ff596ae5beb9c0 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 15 Dec 2022 16:38:46 +0100 Subject: [PATCH 04/17] refactor(Discover): translate selectable options --- src/routes/Discover/useSelectableInputs.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/routes/Discover/useSelectableInputs.js b/src/routes/Discover/useSelectableInputs.js index 5c7c8b9fb..aca15315c 100644 --- a/src/routes/Discover/useSelectableInputs.js +++ b/src/routes/Discover/useSelectableInputs.js @@ -1,20 +1,30 @@ // Copyright (C) 2017-2022 Smart code 203358507 const React = require('react'); +const { useTranslation } = require('react-i18next'); -const mapSelectableInputs = (discover) => { +const translateOption = (t, option, translateKeyPrefix = '') => { + const translateKey = `${translateKeyPrefix}${option}`; + const translateValue = t(`${translateKeyPrefix}${option}`); + if (translateKey !== translateValue) { + return translateValue; + } + return option.charAt(0).toUpperCase() + option.slice(1); +}; + +const mapSelectableInputs = (discover, t) => { const typeSelect = { title: 'Select type', options: discover.selectable.types .map(({ type, deepLinks }) => ({ value: deepLinks.discover, - label: type + label: translateOption(t, type, 'TYPE_') })), selected: discover.selectable.types .filter(({ selected }) => selected) .map(({ deepLinks }) => deepLinks.discover), renderLabelText: discover.selected !== null ? - () => discover.selected.request.path.type + () => translateOption(t, discover.selected.request.path.type, 'TYPE_') : null, onSelect: (event) => { @@ -48,7 +58,7 @@ const mapSelectableInputs = (discover) => { title: `Select ${name}`, isRequired: isRequired, options: options.map(({ value, deepLinks }) => ({ - label: typeof value === 'string' ? value : 'None', + label: typeof value === 'string' ? translateOption(t, value) : t('NONE'), value: JSON.stringify({ href: deepLinks.discover, value @@ -73,8 +83,9 @@ const mapSelectableInputs = (discover) => { }; const useSelectableInputs = (discover) => { + const { t } = useTranslation(); const selectableInputs = React.useMemo(() => { - return mapSelectableInputs(discover); + return mapSelectableInputs(discover, t); }, [discover.selected, discover.selectable]); return selectableInputs; }; From 2061f65ccf949ca7d24729ffa838eb3102638b2f Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 15 Dec 2022 16:44:56 +0100 Subject: [PATCH 05/17] refactor(Library): translate selectable options --- src/routes/Library/useSelectableInputs.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/routes/Library/useSelectableInputs.js b/src/routes/Library/useSelectableInputs.js index 6f3d58d7e..8e2fa74ad 100644 --- a/src/routes/Library/useSelectableInputs.js +++ b/src/routes/Library/useSelectableInputs.js @@ -1,14 +1,24 @@ // Copyright (C) 2017-2022 Smart code 203358507 const React = require('react'); +const { useTranslation } = require('react-i18next'); -const mapSelectableInputs = (library) => { +const translateOption = (t, option, translateKeyPrefix = '') => { + const translateKey = `${translateKeyPrefix}${option}`; + const translateValue = t(translateKey); + if (translateKey !== translateValue) { + return translateValue; + } + return option.charAt(0).toUpperCase() + option.slice(1); +}; + +const mapSelectableInputs = (library, t) => { const typeSelect = { title: 'Select type', options: library.selectable.types .map(({ type, deepLinks }) => ({ value: deepLinks.library, - label: type === null ? 'All' : type + label: type === null ? 'All' : translateOption(t, type, 'TYPE_') })), selected: library.selectable.types .filter(({ selected }) => selected) @@ -22,7 +32,7 @@ const mapSelectableInputs = (library) => { options: library.selectable.sorts .map(({ sort, deepLinks }) => ({ value: deepLinks.library, - label: sort + label: translateOption(t, sort) })), selected: library.selectable.sorts .filter(({ selected }) => selected) @@ -49,8 +59,9 @@ const mapSelectableInputs = (library) => { }; const useSelectableInputs = (library) => { + const { t } = useTranslation(); const selectableInputs = React.useMemo(() => { - return mapSelectableInputs(library); + return mapSelectableInputs(library, t); }, [library]); return selectableInputs; }; From e9ff812039268b99e8fc798620a58bf8b95c4530 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 15 Dec 2022 17:09:13 +0100 Subject: [PATCH 06/17] refactor(SubtitlesMenu): translate more strings --- src/routes/Player/SubtitlesMenu/SubtitlesMenu.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js index 0834d114e..35093f7e0 100644 --- a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js +++ b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js @@ -6,6 +6,7 @@ const classnames = require('classnames'); const { Button, CONSTANTS, comparatorWithPriorities, languageNames } = require('stremio/common'); const DiscreteSelectInput = require('./DiscreteSelectInput'); const styles = require('./styles'); +const { t } = require('i18next'); const ORIGIN_PRIORITIES = { 'EMBEDDED': 2, @@ -174,8 +175,8 @@ const SubtitlesMenu = React.memo((props) => {
Subtitles Languages
- : @@ -62,7 +63,7 @@ const HorizontalNavBar = React.memo(({ className, route, query, title, backButto } { fullscreenButton ? - : diff --git a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js index 240a9ce89..a6c095224 100644 --- a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js +++ b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js @@ -48,7 +48,7 @@ const NavMenuContent = ({ onClick }) => { }} />
-
{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}
+
{profile.auth === null ? t('ANONYMOUS_USER') : profile.auth.user.email}
{selectInputs.map((selectInput, index) => ( { addAddonModalOpen ? -
You can add an addon via an external link, which will appear under Installed addons.
+
{ t('ADD_ADDON_DESCRIPTION') }
diff --git a/src/routes/Addons/useSelectableInputs.js b/src/routes/Addons/useSelectableInputs.js index c823957b3..9eccc1807 100644 --- a/src/routes/Addons/useSelectableInputs.js +++ b/src/routes/Addons/useSelectableInputs.js @@ -1,10 +1,11 @@ // Copyright (C) 2017-2022 Smart code 203358507 +const { t } = require('i18next'); const React = require('react'); const mapSelectableInputs = (installedAddons, remoteAddons) => { const catalogSelect = { - title: 'Select catalog', + title: t('SELECT_CATALOG'), options: remoteAddons.selectable.catalogs .concat(installedAddons.selectable.catalogs) .map(({ name, deepLinks }) => ({ @@ -29,11 +30,11 @@ const mapSelectableInputs = (installedAddons, remoteAddons) => { } }; const typeSelect = { - title: 'Select type', + title: t('SELECT_TYPE'), options: installedAddons.selected !== null ? installedAddons.selectable.types.map(({ type, deepLinks }) => ({ value: deepLinks.addons, - label: type !== null ? type : 'All' + label: type !== null ? type : t('TYPE_ALL') })) : remoteAddons.selectable.types.map(({ type, deepLinks }) => ({ @@ -51,7 +52,7 @@ const mapSelectableInputs = (installedAddons, remoteAddons) => { renderLabelText: () => { return installedAddons.selected !== null ? installedAddons.selected.request.type === null ? - 'All' + t('TYPE_ALL') : installedAddons.selected.request.type : diff --git a/src/routes/Discover/useSelectableInputs.js b/src/routes/Discover/useSelectableInputs.js index aca15315c..5e466520c 100644 --- a/src/routes/Discover/useSelectableInputs.js +++ b/src/routes/Discover/useSelectableInputs.js @@ -14,7 +14,7 @@ const translateOption = (t, option, translateKeyPrefix = '') => { const mapSelectableInputs = (discover, t) => { const typeSelect = { - title: 'Select type', + title: t('SELECT_TYPE'), options: discover.selectable.types .map(({ type, deepLinks }) => ({ value: deepLinks.discover, @@ -32,7 +32,7 @@ const mapSelectableInputs = (discover, t) => { } }; const catalogSelect = { - title: 'Select catalog', + title: t('SELECT_CATALOG'), options: discover.selectable.catalogs .map(({ name, addon, deepLinks }) => ({ value: deepLinks.discover, diff --git a/src/routes/Library/useSelectableInputs.js b/src/routes/Library/useSelectableInputs.js index 8e2fa74ad..a566e3c40 100644 --- a/src/routes/Library/useSelectableInputs.js +++ b/src/routes/Library/useSelectableInputs.js @@ -14,11 +14,11 @@ const translateOption = (t, option, translateKeyPrefix = '') => { const mapSelectableInputs = (library, t) => { const typeSelect = { - title: 'Select type', + title: t('SELECT_TYPE'), options: library.selectable.types .map(({ type, deepLinks }) => ({ value: deepLinks.library, - label: type === null ? 'All' : translateOption(t, type, 'TYPE_') + label: type === null ? t('TYPE_ALL') : translateOption(t, type, 'TYPE_') })), selected: library.selectable.types .filter(({ selected }) => selected) @@ -28,7 +28,7 @@ const mapSelectableInputs = (library, t) => { } }; const sortSelect = { - title: 'Select sort', + title: t('SELECT_SORT'), options: library.selectable.sorts .map(({ sort, deepLinks }) => ({ value: deepLinks.library, diff --git a/src/routes/MetaDetails/StreamsList/StreamsList.js b/src/routes/MetaDetails/StreamsList/StreamsList.js index 5b8750d26..214c7e567 100644 --- a/src/routes/MetaDetails/StreamsList/StreamsList.js +++ b/src/routes/MetaDetails/StreamsList/StreamsList.js @@ -57,8 +57,8 @@ const StreamsList = ({ className, ...props }) => { options: [ { value: ALL_ADDONS_KEY, - label: 'All', - title: 'All' + label: t('ALL_ADDONS'), + title: t('ALL_ADDONS') }, ...Object.keys(streamsByAddon).map((transportUrl) => ({ value: transportUrl, diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js index 63854a30a..af672369e 100644 --- a/src/routes/Player/ControlBar/ControlBar.js +++ b/src/routes/Player/ControlBar/ControlBar.js @@ -10,6 +10,7 @@ const SeekBar = require('./SeekBar'); const VolumeSlider = require('./VolumeSlider'); const styles = require('./styles'); const { useBinaryState } = require('stremio/common'); +const { t } = require('i18next'); const ControlBar = ({ className, @@ -122,18 +123,18 @@ const ControlBar = ({ onSeekRequested={onSeekRequested} />
- { nextVideo !== null ? - : null } -
-
Subtitles Variants
+
{ t('PLAYER_SUBTITLES_VARIANTS') }
{ subtitlesTracksForLanguage.length > 0 ?
@@ -217,7 +217,7 @@ const SubtitlesMenu = React.memo((props) => { :
- Subtitles are disabled + { t('PLAYER_SUBTITLES_DISABLED') }
} @@ -256,7 +256,7 @@ const SubtitlesMenu = React.memo((props) => { /> {
{ profile.auth !== null ? - : null }
{ profile.auth === null ?
-
: @@ -205,7 +205,7 @@ const Settings = () => { }
-
Interface language
+
{ t('SETTINGS_INTERFACE_LANGUAGE') }
{
@@ -228,7 +228,7 @@ const Settings = () => {
@@ -237,7 +237,7 @@ const Settings = () => {
@@ -504,7 +504,7 @@ const Settings = () => {
-
Toggle Subtitles Menu
+
{ t('SETTINGS_SHORTCUT_MENU_SUBTITLES') }
S @@ -512,7 +512,7 @@ const Settings = () => {
-
Toggle Info Menu
+
{ t('SETTINGS_SHORTCUT_MENU_INFO') }
I @@ -528,7 +528,7 @@ const Settings = () => {
-
Navigate Between Menus
+
{ t('SETTINGS_SHORTCUT_NAVIGATE_MENUS') }
1 @@ -538,7 +538,7 @@ const Settings = () => {
-
Go to Search
+
{ t('SETTINGS_SHORTCUT_GO_TO_SEARCH') }
0 From ec338b16db958eb726d1f3ff1efcdb5e3cfe8122 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 15 Dec 2022 18:52:36 +0100 Subject: [PATCH 09/17] refactor(Addons): translate selectables options --- src/routes/Addons/useSelectableInputs.js | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/routes/Addons/useSelectableInputs.js b/src/routes/Addons/useSelectableInputs.js index 9eccc1807..e55d89950 100644 --- a/src/routes/Addons/useSelectableInputs.js +++ b/src/routes/Addons/useSelectableInputs.js @@ -1,7 +1,20 @@ // Copyright (C) 2017-2022 Smart code 203358507 -const { t } = require('i18next'); const React = require('react'); +const { t } = require('i18next'); + +const translateOption = (option, translateKeyPrefix = '') => { + const translateKey = `${translateKeyPrefix}${option}`; + const translateKeyUppercase = translateKey.toUpperCase(); + const translateValue = t(translateKey); + const translateValueUppercase = t(translateKeyUppercase); + if (translateKey !== translateValue) { + return translateValue; + } else if (translateKeyUppercase !== translateValueUppercase) { + return translateValueUppercase; + } + return option.charAt(0).toUpperCase() + option.slice(1); +}; const mapSelectableInputs = (installedAddons, remoteAddons) => { const catalogSelect = { @@ -10,8 +23,8 @@ const mapSelectableInputs = (installedAddons, remoteAddons) => { .concat(installedAddons.selectable.catalogs) .map(({ name, deepLinks }) => ({ value: deepLinks.addons, - label: name, - title: name + label: translateOption(name, 'ADDON_'), + title: translateOption(name, 'ADDON_'), })), selected: remoteAddons.selectable.catalogs .concat(installedAddons.selectable.catalogs) @@ -21,7 +34,7 @@ const mapSelectableInputs = (installedAddons, remoteAddons) => { () => { const selectableCatalog = remoteAddons.selectable.catalogs .find(({ id }) => id === remoteAddons.selected.request.path.id); - return selectableCatalog ? selectableCatalog.name : remoteAddons.selected.request.path.id; + return selectableCatalog ? translateOption(selectableCatalog.name, 'ADDON_') : remoteAddons.selected.request.path.id; } : null, @@ -34,12 +47,12 @@ const mapSelectableInputs = (installedAddons, remoteAddons) => { options: installedAddons.selected !== null ? installedAddons.selectable.types.map(({ type, deepLinks }) => ({ value: deepLinks.addons, - label: type !== null ? type : t('TYPE_ALL') + label: type !== null ? translateOption(type, 'TYPE_') : t('TYPE_ALL') })) : remoteAddons.selectable.types.map(({ type, deepLinks }) => ({ value: deepLinks.addons, - label: type + label: translateOption(type, 'TYPE_') })), selected: installedAddons.selected !== null ? installedAddons.selectable.types @@ -54,10 +67,10 @@ const mapSelectableInputs = (installedAddons, remoteAddons) => { installedAddons.selected.request.type === null ? t('TYPE_ALL') : - installedAddons.selected.request.type + translateOption(installedAddons.selected.request.type, 'TYPE_') : remoteAddons.selected !== null ? - remoteAddons.selected.request.path.type + translateOption(remoteAddons.selected.request.path.type, 'TYPE_') : typeSelect.title; }, From fd08dcca06e5ff55b5d6113f51b82c5d88b2d7a6 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 15 Dec 2022 18:56:22 +0100 Subject: [PATCH 10/17] refactor: move translateOption to a common file --- src/common/index.js | 2 ++ src/common/translateOption.js | 18 ++++++++++++++++++ src/routes/Addons/useSelectableInputs.js | 14 +------------- src/routes/Discover/useSelectableInputs.js | 16 ++++------------ src/routes/Library/useSelectableInputs.js | 14 +++----------- 5 files changed, 28 insertions(+), 36 deletions(-) create mode 100644 src/common/translateOption.js diff --git a/src/common/index.js b/src/common/index.js index 5110dd669..82fcd579e 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -30,6 +30,7 @@ const getVisibleChildrenRange = require('./getVisibleChildrenRange'); const interfaceLanguages = require('./interfaceLanguages'); const languageNames = require('./languageNames'); const routesRegexp = require('./routesRegexp'); +const translateOption = require('./translateOption'); const useAnimationFrame = require('./useAnimationFrame'); const useBinaryState = require('./useBinaryState'); const useFullscreen = require('./useFullscreen'); @@ -74,6 +75,7 @@ module.exports = { interfaceLanguages, languageNames, routesRegexp, + translateOption, useAnimationFrame, useBinaryState, useFullscreen, diff --git a/src/common/translateOption.js b/src/common/translateOption.js new file mode 100644 index 000000000..726885e17 --- /dev/null +++ b/src/common/translateOption.js @@ -0,0 +1,18 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const { t } = require('i18next'); + +const translateOption = (option, translateKeyPrefix = '') => { + const translateKey = `${translateKeyPrefix}${option}`; + const translateKeyUppercase = translateKey.toUpperCase(); + const translateValue = t(translateKey); + const translateValueUppercase = t(translateKeyUppercase); + if (translateKey !== translateValue) { + return translateValue; + } else if (translateKeyUppercase !== translateValueUppercase) { + return translateValueUppercase; + } + return option.charAt(0).toUpperCase() + option.slice(1); +}; + +module.exports = translateOption; diff --git a/src/routes/Addons/useSelectableInputs.js b/src/routes/Addons/useSelectableInputs.js index e55d89950..c66f48b0b 100644 --- a/src/routes/Addons/useSelectableInputs.js +++ b/src/routes/Addons/useSelectableInputs.js @@ -2,19 +2,7 @@ const React = require('react'); const { t } = require('i18next'); - -const translateOption = (option, translateKeyPrefix = '') => { - const translateKey = `${translateKeyPrefix}${option}`; - const translateKeyUppercase = translateKey.toUpperCase(); - const translateValue = t(translateKey); - const translateValueUppercase = t(translateKeyUppercase); - if (translateKey !== translateValue) { - return translateValue; - } else if (translateKeyUppercase !== translateValueUppercase) { - return translateValueUppercase; - } - return option.charAt(0).toUpperCase() + option.slice(1); -}; +const { translateOption } = require('stremio/common'); const mapSelectableInputs = (installedAddons, remoteAddons) => { const catalogSelect = { diff --git a/src/routes/Discover/useSelectableInputs.js b/src/routes/Discover/useSelectableInputs.js index 5e466520c..7aad6b814 100644 --- a/src/routes/Discover/useSelectableInputs.js +++ b/src/routes/Discover/useSelectableInputs.js @@ -2,15 +2,7 @@ const React = require('react'); const { useTranslation } = require('react-i18next'); - -const translateOption = (t, option, translateKeyPrefix = '') => { - const translateKey = `${translateKeyPrefix}${option}`; - const translateValue = t(`${translateKeyPrefix}${option}`); - if (translateKey !== translateValue) { - return translateValue; - } - return option.charAt(0).toUpperCase() + option.slice(1); -}; +const { translateOption } = require('stremio/common'); const mapSelectableInputs = (discover, t) => { const typeSelect = { @@ -18,13 +10,13 @@ const mapSelectableInputs = (discover, t) => { options: discover.selectable.types .map(({ type, deepLinks }) => ({ value: deepLinks.discover, - label: translateOption(t, type, 'TYPE_') + label: translateOption(type, 'TYPE_') })), selected: discover.selectable.types .filter(({ selected }) => selected) .map(({ deepLinks }) => deepLinks.discover), renderLabelText: discover.selected !== null ? - () => translateOption(t, discover.selected.request.path.type, 'TYPE_') + () => translateOption(discover.selected.request.path.type, 'TYPE_') : null, onSelect: (event) => { @@ -58,7 +50,7 @@ const mapSelectableInputs = (discover, t) => { title: `Select ${name}`, isRequired: isRequired, options: options.map(({ value, deepLinks }) => ({ - label: typeof value === 'string' ? translateOption(t, value) : t('NONE'), + label: typeof value === 'string' ? translateOption(value) : t('NONE'), value: JSON.stringify({ href: deepLinks.discover, value diff --git a/src/routes/Library/useSelectableInputs.js b/src/routes/Library/useSelectableInputs.js index a566e3c40..bf229f3b3 100644 --- a/src/routes/Library/useSelectableInputs.js +++ b/src/routes/Library/useSelectableInputs.js @@ -2,15 +2,7 @@ const React = require('react'); const { useTranslation } = require('react-i18next'); - -const translateOption = (t, option, translateKeyPrefix = '') => { - const translateKey = `${translateKeyPrefix}${option}`; - const translateValue = t(translateKey); - if (translateKey !== translateValue) { - return translateValue; - } - return option.charAt(0).toUpperCase() + option.slice(1); -}; +const { translateOption } = require('stremio/common'); const mapSelectableInputs = (library, t) => { const typeSelect = { @@ -18,7 +10,7 @@ const mapSelectableInputs = (library, t) => { options: library.selectable.types .map(({ type, deepLinks }) => ({ value: deepLinks.library, - label: type === null ? t('TYPE_ALL') : translateOption(t, type, 'TYPE_') + label: type === null ? t('TYPE_ALL') : translateOption(type, 'TYPE_') })), selected: library.selectable.types .filter(({ selected }) => selected) @@ -32,7 +24,7 @@ const mapSelectableInputs = (library, t) => { options: library.selectable.sorts .map(({ sort, deepLinks }) => ({ value: deepLinks.library, - label: translateOption(t, sort) + label: translateOption(sort) })), selected: library.selectable.sorts .filter(({ selected }) => selected) From 445b0934cb19e08e4299dd7564ce596d0fbf9309 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 16 Dec 2022 02:14:57 +0100 Subject: [PATCH 11/17] refactor(MetaLinks): translate buttons --- src/common/MetaPreview/MetaLinks/MetaLinks.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/MetaPreview/MetaLinks/MetaLinks.js b/src/common/MetaPreview/MetaLinks/MetaLinks.js index cf0ec88c2..c19f34e6e 100644 --- a/src/common/MetaPreview/MetaLinks/MetaLinks.js +++ b/src/common/MetaPreview/MetaLinks/MetaLinks.js @@ -3,10 +3,12 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Button = require('stremio/common/Button'); const styles = require('./styles'); const MetaLinks = ({ className, label, links }) => { + const { t } = useTranslation(); return (
{ @@ -20,7 +22,7 @@ const MetaLinks = ({ className, label, links }) => {
{links.map(({ label, href }, index) => ( ))}
From 4008a6202d5b4be0986f739c41e7311d58312e64 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 16 Dec 2022 16:03:13 +0100 Subject: [PATCH 12/17] chore: update stremio-translations --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index e87ba1b38..6ac917efc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "react-i18next": "^12.1.1", "react-is": "18.2.0", "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "git+https://git@github.com/Stremio/stremio-translations.git#cd373a8e61442ab057ec0ddac3707e996bc972ed", + "stremio-translations": "git+https://git@github.com/Stremio/stremio-translations.git#ef047329f5bcb0a8f96008fca05c68b449b34cb1", "url": "0.11.0" }, "devDependencies": { @@ -12848,8 +12848,8 @@ }, "node_modules/stremio-translations": { "version": "1.43.15", - "resolved": "git+https://git@github.com/Stremio/stremio-translations.git#cd373a8e61442ab057ec0ddac3707e996bc972ed", - "integrity": "sha512-GV4Tu/XfW1b+Ona+9aihR83aBb2rRN4XaL1YuZvgB0DwoYL9deUVWHYzcj0681CfTUcW5LNnD0dX8pBXcBOjyQ==", + "resolved": "git+https://git@github.com/Stremio/stremio-translations.git#ef047329f5bcb0a8f96008fca05c68b449b34cb1", + "integrity": "sha512-cG1GZIbCy2xWQpyYXJvlvnkWLtMBypL+BhMFJmC9LRHteAmJU++t37b5Wc6Sm1IbWoXqG508Tc9YfQ28bHTzaQ==", "license": "MIT" }, "node_modules/string_decoder": { @@ -24508,9 +24508,9 @@ "dev": true }, "stremio-translations": { - "version": "git+https://git@github.com/Stremio/stremio-translations.git#cd373a8e61442ab057ec0ddac3707e996bc972ed", - "integrity": "sha512-GV4Tu/XfW1b+Ona+9aihR83aBb2rRN4XaL1YuZvgB0DwoYL9deUVWHYzcj0681CfTUcW5LNnD0dX8pBXcBOjyQ==", - "from": "stremio-translations@git+https://git@github.com/Stremio/stremio-translations.git#cd373a8e61442ab057ec0ddac3707e996bc972ed" + "version": "git+https://git@github.com/Stremio/stremio-translations.git#ef047329f5bcb0a8f96008fca05c68b449b34cb1", + "integrity": "sha512-cG1GZIbCy2xWQpyYXJvlvnkWLtMBypL+BhMFJmC9LRHteAmJU++t37b5Wc6Sm1IbWoXqG508Tc9YfQ28bHTzaQ==", + "from": "stremio-translations@git+https://git@github.com/Stremio/stremio-translations.git#ef047329f5bcb0a8f96008fca05c68b449b34cb1" }, "string_decoder": { "version": "1.1.1", diff --git a/package.json b/package.json index e93c17d6f..e3c33933f 100755 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "react-i18next": "^12.1.1", "react-is": "18.2.0", "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "git+https://git@github.com/Stremio/stremio-translations.git#cd373a8e61442ab057ec0ddac3707e996bc972ed", + "stremio-translations": "git+https://git@github.com/Stremio/stremio-translations.git#ef047329f5bcb0a8f96008fca05c68b449b34cb1", "url": "0.11.0" }, "devDependencies": { From fed28bf6ea6b63c8108bd05f3752293d7296e7a2 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 16 Dec 2022 16:03:26 +0100 Subject: [PATCH 13/17] refactor: translate remaining strings --- src/common/ColorInput/ColorInput.js | 6 ++- src/common/MetaPreview/MetaLinks/MetaLinks.js | 4 +- src/common/MetaRow/MetaRow.js | 6 ++- .../MetaRowPlaceholder/MetaRowPlaceholder.js | 6 ++- .../StreamingServerWarning.js | 8 ++-- src/routes/Settings/Settings.js | 38 +++++++++---------- .../Settings/useProfileSettingsInputs.js | 10 +++-- 7 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index 1c5d450bc..c60f02a23 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -4,6 +4,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const AColorPicker = require('a-color-picker'); +const { useTranslation } = require('react-i18next'); const Button = require('stremio/common/Button'); const ModalDialog = require('stremio/common/ModalDialog'); const useBinaryState = require('stremio/common/useBinaryState'); @@ -16,6 +17,7 @@ const parseColor = (value) => { }; const ColorInput = ({ className, value, dataset, onChange, ...props }) => { + const { t } = useTranslation(); const [modalOpen, openModal, closeModal] = useBinaryState(false); const [tempValue, setTempValue] = React.useState(() => { return parseColor(value); @@ -69,11 +71,11 @@ const ColorInput = ({ className, value, dataset, onChange, ...props }) => { setTempValue(parseColor(value)); }, [value, modalOpen]); return ( - : diff --git a/src/common/MetaRow/MetaRowPlaceholder/MetaRowPlaceholder.js b/src/common/MetaRow/MetaRowPlaceholder/MetaRowPlaceholder.js index f49c84c17..469faa349 100644 --- a/src/common/MetaRow/MetaRowPlaceholder/MetaRowPlaceholder.js +++ b/src/common/MetaRow/MetaRowPlaceholder/MetaRowPlaceholder.js @@ -3,12 +3,14 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { useTranslation } = require('react-i18next'); const Icon = require('@stremio/stremio-icons/dom'); const Button = require('stremio/common/Button'); const CONSTANTS = require('stremio/common/CONSTANTS'); const styles = require('./styles'); const MetaRowPlaceholder = ({ className, title, deepLinks }) => { + const { t } = useTranslation(); return (
@@ -17,8 +19,8 @@ const MetaRowPlaceholder = ({ className, title, deepLinks }) => {
{ deepLinks && typeof deepLinks.discover === 'string' ? - : diff --git a/src/common/StreamingServerWarning/StreamingServerWarning.js b/src/common/StreamingServerWarning/StreamingServerWarning.js index 7ff2ff6b9..f095ce1d1 100644 --- a/src/common/StreamingServerWarning/StreamingServerWarning.js +++ b/src/common/StreamingServerWarning/StreamingServerWarning.js @@ -51,11 +51,11 @@ const StreamingServerWarning = ({ className }) => { return (
{ t('SETTINGS_SERVER_UNAVAILABLE') }
- -
); diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index e1f8db275..f00acfddf 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -324,7 +324,7 @@ const Settings = () => {
-
Rewind & Fast-forward duration
+
{ t('SETTINGS_REWIND_FAST_FORWARD_DURATION') }
{
-
Next video popup duration
+
{ t('SETTINGS_NEXT_VIDEO_POPUP_DURATION') }
{
-
Play in background
+
{ t('SETTINGS_PLAY_IN_BACKGROUND') }
{
-
Play in external player
+
{ t('SETTINGS_PLAY_IN_EXTERNAL_PLAYER') }
{ 'NotLoaded' : streamingServer.settings.type === 'Ready' ? - 'Online' + t('SETTINGS_SERVER_STATUS_ONLINE') : streamingServer.settings.type === 'Error' ? - `Error: (${streamingServer.settings.content})` + `${t('SETTINGS_SERVER_STATUS_ERROR')}: (${streamingServer.settings.content})` : streamingServer.settings.type } @@ -427,7 +427,7 @@ const Settings = () => { cacheSizeSelect !== null ?
-
Cache size
+
{ t('SETTINGS_SERVER_CACHE_SIZE') }
{ torrentProfileSelect !== null ?
-
Torrent profile
+
{ t('SETTINGS_SERVER_TORRENT_PROFILE') }
{
{ t('SETTINGS_SHORTCUT_PLAY_PAUSE') }
- Space + { t('SETTINGS_SHORTCUT_SPACE') }
-
Seek Forward
+
{ t('SETTINGS_SHORTCUT_SEEK_FORWARD') }
-
or
- ⇧ Shift +
{ t('SETTINGS_SHORTCUT_OR') }
+ ⇧ { t('SETTINGS_SHORTCUT_SHIFT') }
+
-
Seek Backward
+
{ t('SETTINGS_SHORTCUT_SEEK_BACKWARD') }
-
or
- ⇧ Shift +
{ t('SETTINGS_SHORTCUT_OR') }
+ ⇧ { t('SETTINGS_SHORTCUT_SHIFT') }
+
@@ -532,7 +532,7 @@ const Settings = () => {
1 -
to
+
{ t('SETTINGS_SHORTCUT_TO') }
5
@@ -549,7 +549,7 @@ const Settings = () => {
{ t('SETTINGS_SHORTCUT_EXIT_BACK') }
- Esc + { t('SETTINGS_SHORTCUT_ESC') }
@@ -559,7 +559,7 @@ const Settings = () => { configureServerUrlModalOpen ? { className={styles['server-url-input']} type={'text'} defaultValue={streamingServerUrlInput.value} - placeholder={'Enter a streaming server url'} + placeholder={t('SETTINGS_SERVER_CONFIGURE_INPUT')} onSubmit={configureServerUrlOnSubmit} /> diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index 417e4ec89..3c4c00bfc 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -1,10 +1,12 @@ // Copyright (C) 2017-2022 Smart code 203358507 const React = require('react'); +const { useTranslation } = require('react-i18next'); const { useServices } = require('stremio/services'); const { CONSTANTS, interfaceLanguages, languageNames } = require('stremio/common'); const useProfileSettingsInputs = (profile) => { + const { t } = useTranslation(); const { core } = useServices(); // TODO combine those useMemo in one const interfaceLanguageSelect = React.useMemo(() => ({ @@ -136,11 +138,11 @@ const useProfileSettingsInputs = (profile) => { const seekTimeDurationSelect = React.useMemo(() => ({ options: CONSTANTS.SEEK_TIME_DURATIONS.map((size) => ({ value: `${size}`, - label: `${size / 1000} seconds` + label: `${size / 1000} ${t('SECONDS')}` })), selected: [`${profile.settings.seekTimeDuration}`], renderLabelText: () => { - return `${profile.settings.seekTimeDuration / 1000} seconds`; + return `${profile.settings.seekTimeDuration / 1000} ${t('SECONDS')}`; }, onSelect: (event) => { core.transport.dispatch({ @@ -158,14 +160,14 @@ const useProfileSettingsInputs = (profile) => { const nextVideoPopupDurationSelect = React.useMemo(() => ({ options: CONSTANTS.NEXT_VIDEO_POPUP_DURATIONS.map((duration) => ({ value: `${duration}`, - label: duration === 0 ? 'Disabled' : `${duration / 1000} seconds` + label: duration === 0 ? 'Disabled' : `${duration / 1000} ${t('SECONDS')}` })), selected: [`${profile.settings.nextVideoNotificationDuration}`], renderLabelText: () => { return profile.settings.nextVideoNotificationDuration === 0 ? 'Disabled' : - `${profile.settings.nextVideoNotificationDuration / 1000} seconds`; + `${profile.settings.nextVideoNotificationDuration / 1000} ${t('SECONDS')}`; }, onSelect: (event) => { core.transport.dispatch({ From 567af0b83be7982b5541cf43711d744c87529ebd Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 10 Mar 2023 14:33:21 +0100 Subject: [PATCH 14/17] refactor(MetaDetails): add missing translations --- src/routes/MetaDetails/StreamsList/StreamsList.js | 2 +- src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js | 5 +++-- src/routes/MetaDetails/VideosList/VideosList.js | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/routes/MetaDetails/StreamsList/StreamsList.js b/src/routes/MetaDetails/StreamsList/StreamsList.js index 214c7e567..853e337b3 100644 --- a/src/routes/MetaDetails/StreamsList/StreamsList.js +++ b/src/routes/MetaDetails/StreamsList/StreamsList.js @@ -82,7 +82,7 @@ const StreamsList = ({ className, ...props }) => { props.streams.every((streams) => streams.content.type === 'Err') ?
{' -
No streams were found!
+
{t('NO_STREAM')}
: filteredStreams.length === 0 ? diff --git a/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js b/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js index 59141db05..14a4004f5 100644 --- a/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js +++ b/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js @@ -3,6 +3,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { t } = require('i18next'); const Icon = require('@stremio/stremio-icons/dom'); const { Button, Multiselect } = require('stremio/common'); const SeasonsBarPlaceholder = require('./SeasonsBarPlaceholder'); @@ -12,7 +13,7 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => { const options = React.useMemo(() => { return seasons.map((season) => ({ value: String(season), - label: season > 0 ? `Season ${season}` : 'Specials' + label: season > 0 ? `${t('SEASON')} ${season}` : t('SPECIAL') })); }, [seasons]); const selected = React.useMemo(() => { @@ -53,7 +54,7 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => { 0 ? `Season ${season}` : 'Specials'} + title={season > 0 ? `${t('SEASON')} ${season}` : t('SPECIAL')} direction={'bottom-left'} options={options} selected={selected} diff --git a/src/routes/MetaDetails/VideosList/VideosList.js b/src/routes/MetaDetails/VideosList/VideosList.js index 478324d28..e67d7660c 100644 --- a/src/routes/MetaDetails/VideosList/VideosList.js +++ b/src/routes/MetaDetails/VideosList/VideosList.js @@ -3,6 +3,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); +const { t } = require('i18next'); const Image = require('stremio/common/Image'); const SearchBar = require('stremio/common/SearchBar'); const SeasonsBar = require('./SeasonsBar'); @@ -62,7 +63,7 @@ const VideosList = ({ className, metaItem, season, seasonOnSelect }) => { !metaItem || metaItem.content.type === 'Loading' ? - +
@@ -92,7 +93,7 @@ const VideosList = ({ className, metaItem, season, seasonOnSelect }) => { } From 32a7117493418d335de092107cfc7b20160f1486 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 12 Apr 2023 13:36:10 +0200 Subject: [PATCH 15/17] refactor(translateOption): use Sleeyax suggestion --- src/common/translateOption.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/common/translateOption.js b/src/common/translateOption.js index 726885e17..085306515 100644 --- a/src/common/translateOption.js +++ b/src/common/translateOption.js @@ -4,15 +4,12 @@ const { t } = require('i18next'); const translateOption = (option, translateKeyPrefix = '') => { const translateKey = `${translateKeyPrefix}${option}`; - const translateKeyUppercase = translateKey.toUpperCase(); - const translateValue = t(translateKey); - const translateValueUppercase = t(translateKeyUppercase); - if (translateKey !== translateValue) { - return translateValue; - } else if (translateKeyUppercase !== translateValueUppercase) { - return translateValueUppercase; - } - return option.charAt(0).toUpperCase() + option.slice(1); + const translateValue = t(translateKey, { + defaultValue: t(translateKey.toUpperCase(), { + defaultValue: null + }) + }); + return translateValue ?? option.charAt(0).toUpperCase() + option.slice(1); }; module.exports = translateOption; From b41cc418c5b64b03ac3609475ad2076e81df891f Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 12 Apr 2023 13:43:01 +0200 Subject: [PATCH 16/17] refactor(Discover): add translation on extra selectable --- src/routes/Discover/useSelectableInputs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Discover/useSelectableInputs.js b/src/routes/Discover/useSelectableInputs.js index 7aad6b814..f55987bda 100644 --- a/src/routes/Discover/useSelectableInputs.js +++ b/src/routes/Discover/useSelectableInputs.js @@ -47,7 +47,7 @@ const mapSelectableInputs = (discover, t) => { } }; const extraSelects = discover.selectable.extra.map(({ name, isRequired, options }) => ({ - title: `Select ${name}`, + title: translateOption(name, 'SELECT_'), isRequired: isRequired, options: options.map(({ value, deepLinks }) => ({ label: typeof value === 'string' ? translateOption(value) : t('NONE'), @@ -63,7 +63,7 @@ const mapSelectableInputs = (discover, t) => { value })), renderLabelText: options.some(({ selected, value }) => selected && value === null) ? - () => `Select ${name}` + () => translateOption(name, 'SELECT_') : null, onSelect: (event) => { From 02fceea35731957e5fdbfd676201dab0b374f3c0 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 12 Apr 2023 13:46:08 +0200 Subject: [PATCH 17/17] refactor(Library): add translation to sort selectable --- src/routes/Library/useSelectableInputs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Library/useSelectableInputs.js b/src/routes/Library/useSelectableInputs.js index bf229f3b3..e63ad811b 100644 --- a/src/routes/Library/useSelectableInputs.js +++ b/src/routes/Library/useSelectableInputs.js @@ -24,7 +24,7 @@ const mapSelectableInputs = (library, t) => { options: library.selectable.sorts .map(({ sort, deepLinks }) => ({ value: deepLinks.library, - label: translateOption(sort) + label: translateOption(sort, 'SORT_') })), selected: library.selectable.sorts .filter(({ selected }) => selected)