mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-17 08:47:49 +00:00
First iteration for internationalization of the loader
This commit is contained in:
@@ -42,7 +42,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decky-frontend-lib": "^3.7.14",
|
"decky-frontend-lib": "^3.7.14",
|
||||||
|
"i18next": "^22.0.6",
|
||||||
|
"i18next-fs-backend": "^2.0.0",
|
||||||
"react-file-icon": "^1.2.0",
|
"react-file-icon": "^1.2.0",
|
||||||
|
"react-i18next": "^12.0.0",
|
||||||
"react-icons": "^4.4.0",
|
"react-icons": "^4.4.0",
|
||||||
"react-markdown": "^8.0.3",
|
"react-markdown": "^8.0.3",
|
||||||
"remark-gfm": "^3.0.1"
|
"remark-gfm": "^3.0.1"
|
||||||
|
|||||||
Generated
+58
@@ -12,6 +12,8 @@ specifiers:
|
|||||||
'@types/webpack': ^5.28.0
|
'@types/webpack': ^5.28.0
|
||||||
decky-frontend-lib: ^3.7.14
|
decky-frontend-lib: ^3.7.14
|
||||||
husky: ^8.0.1
|
husky: ^8.0.1
|
||||||
|
i18next: ^22.0.6
|
||||||
|
i18next-fs-backend: ^2.0.0
|
||||||
import-sort-style-module: ^6.0.0
|
import-sort-style-module: ^6.0.0
|
||||||
inquirer: ^8.2.4
|
inquirer: ^8.2.4
|
||||||
prettier: ^2.7.1
|
prettier: ^2.7.1
|
||||||
@@ -19,6 +21,7 @@ specifiers:
|
|||||||
react: 16.14.0
|
react: 16.14.0
|
||||||
react-dom: 16.14.0
|
react-dom: 16.14.0
|
||||||
react-file-icon: ^1.2.0
|
react-file-icon: ^1.2.0
|
||||||
|
react-i18next: ^12.0.0
|
||||||
react-icons: ^4.4.0
|
react-icons: ^4.4.0
|
||||||
react-markdown: ^8.0.3
|
react-markdown: ^8.0.3
|
||||||
remark-gfm: ^3.0.1
|
remark-gfm: ^3.0.1
|
||||||
@@ -31,7 +34,10 @@ specifiers:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
decky-frontend-lib: 3.7.14
|
decky-frontend-lib: 3.7.14
|
||||||
|
i18next: 22.0.6
|
||||||
|
i18next-fs-backend: 2.0.0
|
||||||
react-file-icon: 1.2.0_wcqkhtmu7mswc6yz4uyexck3ty
|
react-file-icon: 1.2.0_wcqkhtmu7mswc6yz4uyexck3ty
|
||||||
|
react-i18next: 12.0.0_l5i64r2igudm3ypjcze5pllh6e
|
||||||
react-icons: 4.4.0_react@16.14.0
|
react-icons: 4.4.0_react@16.14.0
|
||||||
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
|
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
|
||||||
remark-gfm: 3.0.1
|
remark-gfm: 3.0.1
|
||||||
@@ -222,6 +228,13 @@ packages:
|
|||||||
'@babel/types': 7.18.8
|
'@babel/types': 7.18.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/runtime/7.20.1:
|
||||||
|
resolution: {integrity: sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime: 0.13.11
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@babel/template/7.18.6:
|
/@babel/template/7.18.6:
|
||||||
resolution: {integrity: sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==}
|
resolution: {integrity: sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -1233,12 +1246,28 @@ packages:
|
|||||||
resolution: {integrity: sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg==}
|
resolution: {integrity: sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/html-parse-stringify/3.0.1:
|
||||||
|
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
|
||||||
|
dependencies:
|
||||||
|
void-elements: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/husky/8.0.1:
|
/husky/8.0.1:
|
||||||
resolution: {integrity: sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==}
|
resolution: {integrity: sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/i18next-fs-backend/2.0.0:
|
||||||
|
resolution: {integrity: sha512-zlwzcoUKlveoRt/SxgeP8i/6p1rxwxZ+x0w4sfCTY1zgUlhhnoxBSRX3GjVIsDJm5mky8Hpr//UX93UIknK7yQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/i18next/22.0.6:
|
||||||
|
resolution: {integrity: sha512-RlreNGoPIdDP4QG+qSA9PxZKGwlzmcozbI9ObI6+OyUa/Rp0EjZZA9ubyBjw887zVNZsC+7FI3sXX8oiTzAfig==}
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.20.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/iconv-lite/0.4.24:
|
/iconv-lite/0.4.24:
|
||||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2107,6 +2136,26 @@ packages:
|
|||||||
tinycolor2: 1.4.2
|
tinycolor2: 1.4.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-i18next/12.0.0_l5i64r2igudm3ypjcze5pllh6e:
|
||||||
|
resolution: {integrity: sha512-/O7N6aIEAl1FaWZBNvhdIo9itvF/MO/nRKr9pYqRc9LhuC1u21SlfwpiYQqvaeNSEW3g3qUXLREOWMt+gxrWbg==}
|
||||||
|
peerDependencies:
|
||||||
|
i18next: '>= 19.0.0'
|
||||||
|
react: '>= 16.8.0'
|
||||||
|
react-dom: '*'
|
||||||
|
react-native: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
react-native:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.20.1
|
||||||
|
html-parse-stringify: 3.0.1
|
||||||
|
i18next: 22.0.6
|
||||||
|
react: 16.14.0
|
||||||
|
react-dom: 16.14.0_react@16.14.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-icons/4.4.0_react@16.14.0:
|
/react-icons/4.4.0_react@16.14.0:
|
||||||
resolution: {integrity: sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==}
|
resolution: {integrity: sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2166,6 +2215,10 @@ packages:
|
|||||||
util-deprecate: 1.0.2
|
util-deprecate: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/regenerator-runtime/0.13.11:
|
||||||
|
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/remark-gfm/3.0.1:
|
/remark-gfm/3.0.1:
|
||||||
resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==}
|
resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2617,6 +2670,11 @@ packages:
|
|||||||
vfile-message: 3.1.2
|
vfile-message: 3.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/void-elements/3.1.0:
|
||||||
|
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/watchpack/2.4.0:
|
/watchpack/2.4.0:
|
||||||
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
|
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { SidebarNavigation } from 'decky-frontend-lib';
|
import { SidebarNavigation } from 'decky-frontend-lib';
|
||||||
import { lazy } from 'react';
|
import { lazy } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { useSetting } from '../../utils/hooks/useSetting';
|
import { useSetting } from '../../utils/hooks/useSetting';
|
||||||
import WithSuspense from '../WithSuspense';
|
import WithSuspense from '../WithSuspense';
|
||||||
@@ -8,17 +9,19 @@ import PluginList from './pages/plugin_list';
|
|||||||
|
|
||||||
const DeveloperSettings = lazy(() => import('./pages/developer'));
|
const DeveloperSettings = lazy(() => import('./pages/developer'));
|
||||||
|
|
||||||
|
const { t } = useTranslation('SettingsIndex');
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
const [isDeveloper, setIsDeveloper] = useSetting<boolean>('developer.enabled', false);
|
const [isDeveloper, setIsDeveloper] = useSetting<boolean>('developer.enabled', false);
|
||||||
|
|
||||||
const pages = [
|
const pages = [
|
||||||
{
|
{
|
||||||
title: 'General',
|
title: t('general_title'),
|
||||||
content: <GeneralSettings isDeveloper={isDeveloper} setIsDeveloper={setIsDeveloper} />,
|
content: <GeneralSettings isDeveloper={isDeveloper} setIsDeveloper={setIsDeveloper} />,
|
||||||
route: '/decky/settings/general',
|
route: '/decky/settings/general',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Plugins',
|
title: t('plugins_title'),
|
||||||
content: <PluginList />,
|
content: <PluginList />,
|
||||||
route: '/decky/settings/plugins',
|
route: '/decky/settings/plugins',
|
||||||
},
|
},
|
||||||
@@ -26,7 +29,7 @@ export default function SettingsPage() {
|
|||||||
|
|
||||||
if (isDeveloper)
|
if (isDeveloper)
|
||||||
pages.push({
|
pages.push({
|
||||||
title: 'Developer',
|
title: t('developer_title'),
|
||||||
content: (
|
content: (
|
||||||
<WithSuspense>
|
<WithSuspense>
|
||||||
<DeveloperSettings />
|
<DeveloperSettings />
|
||||||
@@ -35,5 +38,5 @@ export default function SettingsPage() {
|
|||||||
route: '/decky/settings/developer',
|
route: '/decky/settings/developer',
|
||||||
});
|
});
|
||||||
|
|
||||||
return <SidebarNavigation title="Decky Settings" showTitle pages={pages} />;
|
return <SidebarNavigation title={t('settings_navbar') as string}showTitle pages={pages} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Field, Focusable, TextField, Toggle } from 'decky-frontend-lib';
|
import { Field, Focusable, TextField, Toggle } from 'decky-frontend-lib';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaReact, FaSteamSymbol } from 'react-icons/fa';
|
import { FaReact, FaSteamSymbol } from 'react-icons/fa';
|
||||||
|
|
||||||
import { setShouldConnectToReactDevTools, setShowValveInternal } from '../../../../developer';
|
import { setShouldConnectToReactDevTools, setShowValveInternal } from '../../../../developer';
|
||||||
@@ -10,15 +11,16 @@ export default function DeveloperSettings() {
|
|||||||
const [reactDevtoolsEnabled, setReactDevtoolsEnabled] = useSetting<boolean>('developer.rdt.enabled', false);
|
const [reactDevtoolsEnabled, setReactDevtoolsEnabled] = useSetting<boolean>('developer.rdt.enabled', false);
|
||||||
const [reactDevtoolsIP, setReactDevtoolsIP] = useSetting<string>('developer.rdt.ip', '');
|
const [reactDevtoolsIP, setReactDevtoolsIP] = useSetting<string>('developer.rdt.ip', '');
|
||||||
const textRef = useRef<HTMLDivElement>(null);
|
const textRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { t } = useTranslation('DeveloperIndex');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Field
|
<Field
|
||||||
label="Enable Valve Internal"
|
label={t('label_valve_internal')}
|
||||||
description={
|
description={
|
||||||
<span style={{ whiteSpace: 'pre-line' }}>
|
<span style={{ whiteSpace: 'pre-line' }}>
|
||||||
Enables the Valve internal developer menu.{' '}
|
{t('valve_internal_desc1')}{' '}
|
||||||
<span style={{ color: 'red' }}>Do not touch anything in this menu unless you know what it does.</span>
|
<span style={{ color: 'red' }}>{t('valve_internal_desc2')}</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
icon={<FaSteamSymbol style={{ display: 'block' }} />}
|
icon={<FaSteamSymbol style={{ display: 'block' }} />}
|
||||||
@@ -55,12 +57,11 @@ export default function DeveloperSettings() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Field
|
<Field
|
||||||
label="Enable React DevTools"
|
label={t('react_devtools_label')}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<span style={{ whiteSpace: 'pre-line' }}>
|
<span style={{ whiteSpace: 'pre-line' }}>
|
||||||
Enables connection to a computer running React DevTools. Changing this setting will reload Steam. Set
|
{t('react_devtools_desc')}
|
||||||
the IP address before enabling.
|
|
||||||
</span>
|
</span>
|
||||||
<div ref={textRef}>
|
<div ref={textRef}>
|
||||||
<TextField label={'IP'} value={reactDevtoolsIP} onChange={(e) => setReactDevtoolsIP(e?.target.value)} />
|
<TextField label={'IP'} value={reactDevtoolsIP} onChange={(e) => setReactDevtoolsIP(e?.target.value)} />
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Dropdown, Field } from 'decky-frontend-lib';
|
import { Dropdown, Field } from 'decky-frontend-lib';
|
||||||
import { FunctionComponent } from 'react';
|
import { FunctionComponent } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import Logger from '../../../../logger';
|
import Logger from '../../../../logger';
|
||||||
import { callUpdaterMethod } from '../../../../updater';
|
import { callUpdaterMethod } from '../../../../updater';
|
||||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||||
|
|
||||||
const logger = new Logger('BranchSelect');
|
const logger = new Logger('BranchSelect');
|
||||||
|
const { t } = useTranslation('BranchSelect');
|
||||||
|
|
||||||
enum UpdateBranch {
|
enum UpdateBranch {
|
||||||
Stable,
|
Stable,
|
||||||
@@ -19,7 +21,7 @@ const BranchSelect: FunctionComponent<{}> = () => {
|
|||||||
return (
|
return (
|
||||||
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
|
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
|
||||||
// 0 being stable, 1 being pre-release and 2 being nightly
|
// 0 being stable, 1 being pre-release and 2 being nightly
|
||||||
<Field label="Update Channel">
|
<Field label={t('update_channel_label')}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
rgOptions={Object.values(UpdateBranch)
|
rgOptions={Object.values(UpdateBranch)
|
||||||
.filter((branch) => typeof branch == 'string')
|
.filter((branch) => typeof branch == 'string')
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { Field, Toggle } from 'decky-frontend-lib';
|
import { Field, Toggle } from 'decky-frontend-lib';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaBug } from 'react-icons/fa';
|
import { FaBug } from 'react-icons/fa';
|
||||||
|
|
||||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||||
|
|
||||||
export default function RemoteDebuggingSettings() {
|
export default function RemoteDebuggingSettings() {
|
||||||
const [allowRemoteDebugging, setAllowRemoteDebugging] = useSetting<boolean>('cef_forward', false);
|
const [allowRemoteDebugging, setAllowRemoteDebugging] = useSetting<boolean>('cef_forward', false);
|
||||||
|
const { t } = useTranslation('RemoteDebugging');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
label="Allow Remote CEF Debugging"
|
label={t('remote_cef_label')}
|
||||||
description={
|
description={
|
||||||
<span style={{ whiteSpace: 'pre-line' }}>
|
<span style={{ whiteSpace: 'pre-line' }}>
|
||||||
Allow unauthenticated access to the CEF debugger to anyone in your network
|
{t('remote_cef_desc')}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
icon={<FaBug style={{ display: 'block' }} />}
|
icon={<FaBug style={{ display: 'block' }} />}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Dropdown, Field, TextField } from 'decky-frontend-lib';
|
import { Dropdown, Field, TextField } from 'decky-frontend-lib';
|
||||||
import { FunctionComponent } from 'react';
|
import { FunctionComponent } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaShapes } from 'react-icons/fa';
|
import { FaShapes } from 'react-icons/fa';
|
||||||
|
|
||||||
import Logger from '../../../../logger';
|
import Logger from '../../../../logger';
|
||||||
@@ -7,6 +8,7 @@ import { Store } from '../../../../store';
|
|||||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||||
|
|
||||||
const logger = new Logger('StoreSelect');
|
const logger = new Logger('StoreSelect');
|
||||||
|
const { t } = useTranslation('StoreSelect');
|
||||||
|
|
||||||
const StoreSelect: FunctionComponent<{}> = () => {
|
const StoreSelect: FunctionComponent<{}> = () => {
|
||||||
const [selectedStore, setSelectedStore] = useSetting<Store>('store', Store.Default);
|
const [selectedStore, setSelectedStore] = useSetting<Store>('store', Store.Default);
|
||||||
@@ -16,7 +18,7 @@ const StoreSelect: FunctionComponent<{}> = () => {
|
|||||||
// 0 being Default, 1 being Testing and 2 being Custom
|
// 0 being Default, 1 being Testing and 2 being Custom
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Field label="Store Channel">
|
<Field label={t('store_channel_label')}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
rgOptions={Object.values(Store)
|
rgOptions={Object.values(Store)
|
||||||
.filter((store) => typeof store == 'string')
|
.filter((store) => typeof store == 'string')
|
||||||
@@ -33,7 +35,7 @@ const StoreSelect: FunctionComponent<{}> = () => {
|
|||||||
</Field>
|
</Field>
|
||||||
{selectedStore == Store.Custom && (
|
{selectedStore == Store.Custom && (
|
||||||
<Field
|
<Field
|
||||||
label="Custom Store"
|
label={t('custom_store_label')}
|
||||||
indentLevel={1}
|
indentLevel={1}
|
||||||
description={
|
description={
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Suspense, lazy } from 'react';
|
import { Suspense, lazy } from 'react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaArrowDown } from 'react-icons/fa';
|
import { FaArrowDown } from 'react-icons/fa';
|
||||||
|
|
||||||
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
|
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
|
||||||
@@ -20,6 +21,7 @@ import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
|
|||||||
import WithSuspense from '../../../WithSuspense';
|
import WithSuspense from '../../../WithSuspense';
|
||||||
|
|
||||||
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
|
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
|
||||||
|
const { t } = useTranslation('Updater');
|
||||||
|
|
||||||
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
|
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
|
||||||
const SP = findSP();
|
const SP = findSP();
|
||||||
@@ -45,7 +47,7 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
|||||||
<MarkdownRenderer onDismiss={closeModal}>{versionInfo.all[id].body}</MarkdownRenderer>
|
<MarkdownRenderer onDismiss={closeModal}>{versionInfo.all[id].body}</MarkdownRenderer>
|
||||||
</WithSuspense>
|
</WithSuspense>
|
||||||
) : (
|
) : (
|
||||||
'no patch notes for this version'
|
t("no_patch_notes_desc")
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Focusable>
|
</Focusable>
|
||||||
@@ -58,7 +60,7 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
|||||||
initialColumn={0}
|
initialColumn={0}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
fnGetColumnWidth={() => SP.innerWidth}
|
fnGetColumnWidth={() => SP.innerWidth}
|
||||||
name="Decky Updates"
|
name={t('decky_updates') as string}
|
||||||
/>
|
/>
|
||||||
</FocusRing>
|
</FocusRing>
|
||||||
</Focusable>
|
</Focusable>
|
||||||
@@ -95,11 +97,11 @@ export default function UpdaterSettings() {
|
|||||||
<Field
|
<Field
|
||||||
onOptionsActionDescription={versionInfo?.all ? 'Patch Notes' : undefined}
|
onOptionsActionDescription={versionInfo?.all ? 'Patch Notes' : undefined}
|
||||||
onOptionsButton={versionInfo?.all ? showPatchNotes : undefined}
|
onOptionsButton={versionInfo?.all ? showPatchNotes : undefined}
|
||||||
label="Updates"
|
label={t('updates_label')}
|
||||||
description={
|
description={
|
||||||
versionInfo && (
|
versionInfo && (
|
||||||
<span style={{ whiteSpace: 'pre-line' }}>{`Current version: ${versionInfo.current}\n${
|
<span style={{ whiteSpace: 'pre-line' }}>{`${t('updates_cur_version', versionInfo.current)}\n${
|
||||||
versionInfo.updatable ? `Latest version: ${versionInfo.remote?.tag_name}` : ''
|
versionInfo.updatable ? t('updates_lat_version', versionInfo.remote?.tag_name) : ''
|
||||||
}`}</span>
|
}`}</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -129,10 +131,10 @@ export default function UpdaterSettings() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{checkingForUpdates
|
{checkingForUpdates
|
||||||
? 'Checking'
|
? t('updates_checking')
|
||||||
: !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
|
: !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
|
||||||
? 'Check For Updates'
|
? t('updates_check_button')
|
||||||
: 'Install Update'}
|
: t('updates_install_button')}
|
||||||
</DialogButton>
|
</DialogButton>
|
||||||
) : (
|
) : (
|
||||||
<ProgressBarWithInfo
|
<ProgressBarWithInfo
|
||||||
@@ -140,7 +142,7 @@ export default function UpdaterSettings() {
|
|||||||
bottomSeparator="none"
|
bottomSeparator="none"
|
||||||
nProgress={updateProgress}
|
nProgress={updateProgress}
|
||||||
indeterminate={reloading}
|
indeterminate={reloading}
|
||||||
sOperationText={reloading ? 'Reloading' : 'Updating'}
|
sOperationText={reloading ? t("updates_reloading") : t("updates_updating")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DialogButton, Field, TextField, Toggle } from 'decky-frontend-lib';
|
import { DialogButton, Field, TextField, Toggle } from 'decky-frontend-lib';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaShapes, FaTools } from 'react-icons/fa';
|
import { FaShapes, FaTools } from 'react-icons/fa';
|
||||||
|
|
||||||
import { installFromURL } from '../../../../store';
|
import { installFromURL } from '../../../../store';
|
||||||
@@ -16,6 +17,7 @@ export default function GeneralSettings({
|
|||||||
setIsDeveloper: (val: boolean) => void;
|
setIsDeveloper: (val: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const [pluginURL, setPluginURL] = useState('');
|
const [pluginURL, setPluginURL] = useState('');
|
||||||
|
const { t } = useTranslation('SettingsGeneralIndex');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -24,8 +26,8 @@ export default function GeneralSettings({
|
|||||||
<StoreSelect />
|
<StoreSelect />
|
||||||
<RemoteDebuggingSettings />
|
<RemoteDebuggingSettings />
|
||||||
<Field
|
<Field
|
||||||
label="Developer mode"
|
label={t("developer_mode_label")}
|
||||||
description={<span style={{ whiteSpace: 'pre-line' }}>Enables Decky's developer settings.</span>}
|
description={<span style={{ whiteSpace: 'pre-line' }}>{t("developer_mode_desc")}</span>}
|
||||||
icon={<FaTools style={{ display: 'block' }} />}
|
icon={<FaTools style={{ display: 'block' }} />}
|
||||||
>
|
>
|
||||||
<Toggle
|
<Toggle
|
||||||
@@ -36,12 +38,12 @@ export default function GeneralSettings({
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field
|
<Field
|
||||||
label="Manual plugin install"
|
label={t("manual_plugin_label")}
|
||||||
description={<TextField label={'URL'} value={pluginURL} onChange={(e) => setPluginURL(e?.target.value)} />}
|
description={<TextField label={'URL'} value={pluginURL} onChange={(e) => setPluginURL(e?.target.value)} />}
|
||||||
icon={<FaShapes style={{ display: 'block' }} />}
|
icon={<FaShapes style={{ display: 'block' }} />}
|
||||||
>
|
>
|
||||||
<DialogButton disabled={pluginURL.length == 0} onClick={() => installFromURL(pluginURL)}>
|
<DialogButton disabled={pluginURL.length == 0} onClick={() => installFromURL(pluginURL)}>
|
||||||
Install
|
{t("manual_plugin_install_button")}
|
||||||
</DialogButton>
|
</DialogButton>
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DialogButton, Focusable, Menu, MenuItem, showContextMenu } from 'decky-frontend-lib';
|
import { DialogButton, Focusable, Menu, MenuItem, showContextMenu } from 'decky-frontend-lib';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaDownload, FaEllipsisH } from 'react-icons/fa';
|
import { FaDownload, FaEllipsisH } from 'react-icons/fa';
|
||||||
|
|
||||||
import { requestPluginInstall } from '../../../../store';
|
import { requestPluginInstall } from '../../../../store';
|
||||||
@@ -7,6 +8,7 @@ import { useDeckyState } from '../../../DeckyState';
|
|||||||
|
|
||||||
export default function PluginList() {
|
export default function PluginList() {
|
||||||
const { plugins, updates } = useDeckyState();
|
const { plugins, updates } = useDeckyState();
|
||||||
|
const { t } = useTranslation('PluginListIndex');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.DeckyPluginLoader.checkPluginUpdates();
|
window.DeckyPluginLoader.checkPluginUpdates();
|
||||||
@@ -15,7 +17,7 @@ export default function PluginList() {
|
|||||||
if (plugins.length === 0) {
|
if (plugins.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>No plugins installed</p>
|
<p>{t('list_no_plugin')}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -36,7 +38,7 @@ export default function PluginList() {
|
|||||||
onClick={() => requestPluginInstall(name, update)}
|
onClick={() => requestPluginInstall(name, update)}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||||
Update to {update.name}
|
{t('list_update_to', update.name)}
|
||||||
<FaDownload style={{ paddingLeft: '2rem' }} />
|
<FaDownload style={{ paddingLeft: '2rem' }} />
|
||||||
</div>
|
</div>
|
||||||
</DialogButton>
|
</DialogButton>
|
||||||
@@ -45,11 +47,11 @@ export default function PluginList() {
|
|||||||
style={{ height: '40px', width: '40px', padding: '10px 12px', minWidth: '40px' }}
|
style={{ height: '40px', width: '40px', padding: '10px 12px', minWidth: '40px' }}
|
||||||
onClick={(e: MouseEvent) =>
|
onClick={(e: MouseEvent) =>
|
||||||
showContextMenu(
|
showContextMenu(
|
||||||
<Menu label="Plugin Actions">
|
<Menu label={t('list_plug_actions_label')}>
|
||||||
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(name, version)}>
|
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(name, version)}>
|
||||||
Reload
|
{t('reload')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(name)}>Uninstall</MenuItem>
|
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(name)}>{t('uninstall')}</MenuItem>
|
||||||
</Menu>,
|
</Menu>,
|
||||||
e.currentTarget ?? window,
|
e.currentTarget ?? window,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
staticClasses,
|
staticClasses,
|
||||||
} from 'decky-frontend-lib';
|
} from 'decky-frontend-lib';
|
||||||
import { FC, useRef, useState } from 'react';
|
import { FC, useRef, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
|
import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
|
||||||
|
|
||||||
@@ -17,6 +18,8 @@ interface PluginCardProps {
|
|||||||
plugin: StorePlugin;
|
plugin: StorePlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { t } = useTranslation('PluginCard');
|
||||||
|
|
||||||
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||||
const [selectedOption, setSelectedOption] = useState<number>(0);
|
const [selectedOption, setSelectedOption] = useState<number>(0);
|
||||||
const buttonRef = useRef<HTMLDivElement>(null);
|
const buttonRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -173,7 +176,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
|||||||
label: version.name,
|
label: version.name,
|
||||||
})) as SingleDropdownOption[]
|
})) as SingleDropdownOption[]
|
||||||
}
|
}
|
||||||
strDefaultLabel={'Select a version'}
|
strDefaultLabel={t('select_version') as string}
|
||||||
selectedOption={selectedOption}
|
selectedOption={selectedOption}
|
||||||
onChange={({ data }) => setSelectedOption(data)}
|
onChange={({ data }) => setSelectedOption(data)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { FaReact } from 'react-icons/fa';
|
|||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
import { getSetting } from './utils/settings';
|
import { getSetting } from './utils/settings';
|
||||||
|
|
||||||
|
import "i18n.ts";
|
||||||
|
|
||||||
const logger = new Logger('DeveloperMode');
|
const logger = new Logger('DeveloperMode');
|
||||||
|
|
||||||
let removeSettingsObserver: () => void = () => {};
|
let removeSettingsObserver: () => void = () => {};
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import i18next from "i18next";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
import Backend from 'i18next-fs-backend';
|
||||||
|
|
||||||
|
i18next
|
||||||
|
.use(initReactI18next)
|
||||||
|
.use(Backend)
|
||||||
|
.init({
|
||||||
|
backend:
|
||||||
|
{
|
||||||
|
// path where resources get loaded from, or a function
|
||||||
|
// returning a path:
|
||||||
|
// function(lngs, namespaces) { return customPath; }
|
||||||
|
// the returned path will interpolate lng, ns if provided like giving a static path
|
||||||
|
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||||
|
|
||||||
|
// path to post missing resources
|
||||||
|
// addPath: '/locales/{{lng}}/{{ns}}.missing.json',
|
||||||
|
|
||||||
|
// if you use i18next-fs-backend as caching layer in combination with i18next-chained-backend, you can optionally set an expiration time
|
||||||
|
// an example on how to use it as cache layer can be found here: https://github.com/i18next/i18next-fs-backend/blob/master/example/caching/app.js
|
||||||
|
// expirationTime: 60 * 60 * 1000
|
||||||
|
},
|
||||||
|
fallbackLng:"en",
|
||||||
|
fallbackNS:"Common",
|
||||||
|
lng: "en",
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18next;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import PluginLoader from './plugin-loader';
|
import PluginLoader from './plugin-loader';
|
||||||
import { DeckyUpdater } from './updater';
|
import { DeckyUpdater } from './updater';
|
||||||
|
import "i18n.ts";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"update_channel_label": "Update Channel"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"reload": "Reload",
|
||||||
|
"uninstall": "Uninstall"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"valve_internal_label": "Enable Valve Internal",
|
||||||
|
"valve_internal_desc1": "Enables the Valve internal developer menu.",
|
||||||
|
"valve_internal_desc2": "Do not touch anything in this menu unless you know what it does.",
|
||||||
|
"react_devtools_label": "Enable React DevTools",
|
||||||
|
"react_devtools_desc": "Enables connection to a computer running React DevTools. Changing this setting will reload Steam. Set the IP address before enabling."
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"select_version": "Select a version"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"list_no_plugin": "No plugins installed!",
|
||||||
|
"list_update_to": "Update to {{name}}",
|
||||||
|
"list_plug_actions_label": "Plugin Actions"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"remote_cef_label": "Allow Remote CEF Debugging",
|
||||||
|
"remote_cef_desc": "Allow unauthenticated access to the CEF debugger to anyone in your network"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"developer_mode_label": "Developer mode",
|
||||||
|
"developer_mode_desc": "Enables Decky's developer settings.",
|
||||||
|
"manual_plugin_label": "Manual plugin install",
|
||||||
|
"manual_plugin_install_button": "Install"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"general_title": "General",
|
||||||
|
"plugins_title": "Plugins",
|
||||||
|
"developer_title": "Developer",
|
||||||
|
"navbar_settings": "Decky Settings"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"store_channel_label": "Store Channel",
|
||||||
|
"custom_store_label": "Custom Store"
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"no_patch_notes_desc": "no patch notes for this version",
|
||||||
|
"decky_updates": "Decky Updates",
|
||||||
|
|
||||||
|
"updates_label": "Updates",
|
||||||
|
"updates_cur_version": "Current version: {{ver}}",
|
||||||
|
"updates_lat_version": "Latest version: {{ver}}",
|
||||||
|
|
||||||
|
"updates_checking": "'Checking",
|
||||||
|
"updates_check_button": "Check For Updates",
|
||||||
|
"updates_install_button": "Install Update",
|
||||||
|
"updates_reloading": "Reloading",
|
||||||
|
"updates_updating": "Updating"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"enabling":"Enabling",
|
||||||
|
"disabling":"Disabling",
|
||||||
|
"decky_update_available":"Update to {{tag_name}} available!",
|
||||||
|
"plugin_update_one":"Updates available for 1 plugin!",
|
||||||
|
"plugin_update_other":"Updates available for {{number}} plugins!",
|
||||||
|
"plugin_uninstall":"Uninstall {{name}}?",
|
||||||
|
"plugin_load_error":"Error loading plugin {{name}}",
|
||||||
|
"plugin_load_error_toast": "Error loading {{name}}",
|
||||||
|
"error":"Error",
|
||||||
|
"plugin_error_uninstall":"Please go to {{-icon}} in the Decky menu if you need to uninstall this plugin.",
|
||||||
|
"file_picker_cancel_text": "User canceled"
|
||||||
|
}
|
||||||
@@ -27,12 +27,15 @@ import OldTabsHook from './tabs-hook.old';
|
|||||||
import Toaster from './toaster';
|
import Toaster from './toaster';
|
||||||
import { VerInfo, callUpdaterMethod } from './updater';
|
import { VerInfo, callUpdaterMethod } from './updater';
|
||||||
import { getSetting } from './utils/settings';
|
import { getSetting } from './utils/settings';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const StorePage = lazy(() => import('./components/store/Store'));
|
const StorePage = lazy(() => import('./components/store/Store'));
|
||||||
const SettingsPage = lazy(() => import('./components/settings'));
|
const SettingsPage = lazy(() => import('./components/settings'));
|
||||||
|
|
||||||
const FilePicker = lazy(() => import('./components/modals/filepicker'));
|
const FilePicker = lazy(() => import('./components/modals/filepicker'));
|
||||||
|
|
||||||
|
const { t } = useTranslation('plugin-loader');
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {}
|
interface Window {}
|
||||||
}
|
}
|
||||||
@@ -109,7 +112,7 @@ class PluginLoader extends Logger {
|
|||||||
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
||||||
this.toaster.toast({
|
this.toaster.toast({
|
||||||
title: 'Decky',
|
title: 'Decky',
|
||||||
body: `Update to ${versionInfo?.remote?.tag_name} available!`,
|
body: t('decky_update_available', versionInfo?.remote?.tag_name),
|
||||||
onClick: () => Router.Navigate('/decky/settings'),
|
onClick: () => Router.Navigate('/decky/settings'),
|
||||||
});
|
});
|
||||||
this.deckyState.setHasLoaderUpdate(true);
|
this.deckyState.setHasLoaderUpdate(true);
|
||||||
@@ -129,7 +132,8 @@ class PluginLoader extends Logger {
|
|||||||
if (updates?.size > 0) {
|
if (updates?.size > 0) {
|
||||||
this.toaster.toast({
|
this.toaster.toast({
|
||||||
title: 'Decky',
|
title: 'Decky',
|
||||||
body: `Updates available for ${updates.size} plugin${updates.size > 1 ? 's' : ''}!`,
|
//body: `Updates available for ${updates.size} plugin${updates.size > 1 ? 's' : ''}!`,
|
||||||
|
body: t('plugin_update', updates.size.toString(10), {count: updates.size}),
|
||||||
onClick: () => Router.Navigate('/decky/settings/plugins'),
|
onClick: () => Router.Navigate('/decky/settings/plugins'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,7 +162,7 @@ class PluginLoader extends Logger {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={staticClasses.Title} style={{ flexDirection: 'column' }}>
|
<div className={staticClasses.Title} style={{ flexDirection: 'column' }}>
|
||||||
Uninstall {name}?
|
{t('plugin_uninstall', name)}
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>,
|
</ConfirmModal>,
|
||||||
);
|
);
|
||||||
@@ -244,16 +248,15 @@ class PluginLoader extends Logger {
|
|||||||
version: version,
|
version: version,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error('Error loading plugin ' + name, e);
|
this.error(t('plugin_load_error', name), e);
|
||||||
const TheError: FC<{}> = () => (
|
const TheError: FC<{}> = () => (
|
||||||
<>
|
<>
|
||||||
Error:{' '}
|
{t("error")}:{' '}
|
||||||
<pre>
|
<pre>
|
||||||
<code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
|
<code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
|
||||||
</pre>
|
</pre>
|
||||||
<>
|
<>
|
||||||
Please go to <FaCog style={{ display: 'inline' }} /> in the Decky menu if you need to uninstall this
|
{t('plugin_error_uninstall', <FaCog style={{ display: 'inline' }} />)}
|
||||||
plugin.
|
|
||||||
</>
|
</>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -263,7 +266,7 @@ class PluginLoader extends Logger {
|
|||||||
content: <TheError />,
|
content: <TheError />,
|
||||||
icon: <FaExclamationCircle />,
|
icon: <FaExclamationCircle />,
|
||||||
});
|
});
|
||||||
this.toaster.toast({ title: 'Error loading ' + name, body: '' + e, icon: <FaExclamationCircle /> });
|
this.toaster.toast({ title: t('error_loading_plugin_toast', name), body: '' + e, icon: <FaExclamationCircle /> });
|
||||||
}
|
}
|
||||||
} else throw new Error(`${name} frontend_bundle not OK`);
|
} else throw new Error(`${name} frontend_bundle not OK`);
|
||||||
}
|
}
|
||||||
@@ -301,7 +304,7 @@ class PluginLoader extends Logger {
|
|||||||
// Purposely outside of the FilePicker component as lazy-loaded ModalRoots don't focus correctly
|
// Purposely outside of the FilePicker component as lazy-loaded ModalRoots don't focus correctly
|
||||||
<ModalRoot
|
<ModalRoot
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
reject('User canceled');
|
reject(t('file_picker_cancel_text'));
|
||||||
closeModal?.();
|
closeModal?.();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user