mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-01-11 22:40:31 +00:00
feat: implement interface language
This commit is contained in:
parent
0771cd7fd7
commit
fc0dff4ec2
8 changed files with 355 additions and 30 deletions
136
package-lock.json
generated
136
package-lock.json
generated
|
|
@ -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==",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<React.StrictMode>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
150
src/common/interfaceLanguages.json
Normal file
150
src/common/interfaceLanguages.json
Normal file
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
18
src/index.js
18
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(<App />);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
|||
<MainNavBars className={styles['settings-container']} route={'settings'}>
|
||||
<div className={classnames(styles['settings-content'], 'animation-fade-in')}>
|
||||
<div className={styles['side-menu-container']}>
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === GENERAL_SECTION })} title={'General'} data-section={GENERAL_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
General
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === GENERAL_SECTION })} title={ t('SETTINGS_NAV_GENERAL') } data-section={GENERAL_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
{ t('SETTINGS_NAV_GENERAL') }
|
||||
</Button>
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === PLAYER_SECTION })} title={'Player'} data-section={PLAYER_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
Player
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === PLAYER_SECTION })} title={ t('SETTINGS_NAV_PLAYER') }data-section={PLAYER_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
{ t('SETTINGS_NAV_PLAYER') }
|
||||
</Button>
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === STREAMING_SECTION })} title={'Streaming server'} data-section={STREAMING_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
Streaming server
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === STREAMING_SECTION })} title={ t('SETTINGS_NAV_STREAMING') } data-section={STREAMING_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
{ t('SETTINGS_NAV_STREAMING') }
|
||||
</Button>
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === SHORTCUTS_SECTION })} title={'Shortcuts'} data-section={SHORTCUTS_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
Shortcuts
|
||||
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === SHORTCUTS_SECTION })} title={ t('SETTINGS_NAV_SHORTCUTS') } data-section={SHORTCUTS_SECTION} onClick={sideMenuButtonOnClick}>
|
||||
{ t('SETTINGS_NAV_SHORTCUTS') }
|
||||
</Button>
|
||||
<div className={styles['spacing']} />
|
||||
<div className={styles['version-info-label']} title={process.env.VERSION}>App Version: {process.env.VERSION}</div>
|
||||
|
|
@ -161,7 +163,7 @@ const Settings = () => {
|
|||
</div>
|
||||
<div ref={sectionsContainerRef} className={styles['sections-container']} onScroll={sectionsContainerOnScorll}>
|
||||
<div ref={generalSectionRef} className={styles['section-container']}>
|
||||
<div className={styles['section-title']}>General</div>
|
||||
<div className={styles['section-title']}>{ t('SETTINGS_NAV_GENERAL') }</div>
|
||||
<div className={classnames(styles['option-container'], styles['user-info-option-container'])}>
|
||||
<div
|
||||
className={styles['avatar-container']}
|
||||
|
|
@ -207,7 +209,6 @@ const Settings = () => {
|
|||
</div>
|
||||
<Multiselect
|
||||
className={classnames(styles['option-input-container'], styles['multiselect-container'])}
|
||||
disabled={true}
|
||||
tabIndex={-1}
|
||||
{...interfaceLanguageSelect}
|
||||
/>
|
||||
|
|
@ -266,7 +267,7 @@ const Settings = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div ref={playerSectionRef} className={styles['section-container']}>
|
||||
<div className={styles['section-title']}>Player</div>
|
||||
<div className={styles['section-title']}>{ t('SETTINGS_NAV_PLAYER') }</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>Subtitles language</div>
|
||||
|
|
@ -384,15 +385,15 @@ const Settings = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div ref={streamingServerSectionRef} className={styles['section-container']}>
|
||||
<div className={styles['section-title']}>Streaming Server</div>
|
||||
<div className={styles['section-title']}>{ t('SETTINGS_NAV_STREAMING') }</div>
|
||||
<div className={styles['option-container']}>
|
||||
<Button className={classnames(styles['option-input-container'], styles['button-container'])} title={'Reload'} onClick={reloadStreamingServer}>
|
||||
<div className={styles['label']}>Reload</div>
|
||||
<div className={styles['label']}>{ t('RELOAD') }</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>Status</div>
|
||||
<div className={styles['label']}>{ t('STATUS') }</div>
|
||||
</div>
|
||||
<div className={classnames(styles['option-input-container'], styles['info-container'])}>
|
||||
<div className={styles['label']}>
|
||||
|
|
@ -452,10 +453,10 @@ const Settings = () => {
|
|||
}
|
||||
</div>
|
||||
<div ref={shortcutsSectionRef} className={styles['section-container']}>
|
||||
<div className={styles['section-title']}>Shortcuts</div>
|
||||
<div className={styles['section-title']}>{ t('SETTINGS_NAV_SHORTCUTS') }</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>Play / Pause</div>
|
||||
<div className={styles['label']}>{ t('SETTINGS_SHORTCUT_PLAY_PAUSE') }</div>
|
||||
</div>
|
||||
<div className={classnames(styles['option-input-container'], styles['shortcut-container'])}>
|
||||
<kbd>Space</kbd>
|
||||
|
|
@ -487,7 +488,7 @@ const Settings = () => {
|
|||
</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>Volume Up</div>
|
||||
<div className={styles['label']}>{ t('SETTINGS_SHORTCUT_VOLUME_UP') }</div>
|
||||
</div>
|
||||
<div className={classnames(styles['option-input-container'], styles['shortcut-container'])}>
|
||||
<kbd>↑</kbd>
|
||||
|
|
@ -495,7 +496,7 @@ const Settings = () => {
|
|||
</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>Volume Down</div>
|
||||
<div className={styles['label']}>{ t('SETTINGS_SHORTCUT_VOLUME_DOWN') }</div>
|
||||
</div>
|
||||
<div className={classnames(styles['option-input-container'], styles['shortcut-container'])}>
|
||||
<kbd>↓</kbd>
|
||||
|
|
@ -545,7 +546,7 @@ const Settings = () => {
|
|||
</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>Close Menu or Modal</div>
|
||||
<div className={styles['label']}>{ t('SETTINGS_SHORTCUT_EXIT_BACK') }</div>
|
||||
</div>
|
||||
<div className={classnames(styles['option-input-container'], styles['shortcut-container'])}>
|
||||
<kbd>Esc</kbd>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in a new issue