mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-11 22:22:00 +00:00
First iteration for internationalization of the loader
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { SidebarNavigation } from 'decky-frontend-lib';
|
||||
import { lazy } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useSetting } from '../../utils/hooks/useSetting';
|
||||
import WithSuspense from '../WithSuspense';
|
||||
@@ -8,17 +9,19 @@ import PluginList from './pages/plugin_list';
|
||||
|
||||
const DeveloperSettings = lazy(() => import('./pages/developer'));
|
||||
|
||||
const { t } = useTranslation('SettingsIndex');
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [isDeveloper, setIsDeveloper] = useSetting<boolean>('developer.enabled', false);
|
||||
|
||||
const pages = [
|
||||
{
|
||||
title: 'General',
|
||||
title: t('general_title'),
|
||||
content: <GeneralSettings isDeveloper={isDeveloper} setIsDeveloper={setIsDeveloper} />,
|
||||
route: '/decky/settings/general',
|
||||
},
|
||||
{
|
||||
title: 'Plugins',
|
||||
title: t('plugins_title'),
|
||||
content: <PluginList />,
|
||||
route: '/decky/settings/plugins',
|
||||
},
|
||||
@@ -26,7 +29,7 @@ export default function SettingsPage() {
|
||||
|
||||
if (isDeveloper)
|
||||
pages.push({
|
||||
title: 'Developer',
|
||||
title: t('developer_title'),
|
||||
content: (
|
||||
<WithSuspense>
|
||||
<DeveloperSettings />
|
||||
@@ -35,5 +38,5 @@ export default function SettingsPage() {
|
||||
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 { useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaReact, FaSteamSymbol } from 'react-icons/fa';
|
||||
|
||||
import { setShouldConnectToReactDevTools, setShowValveInternal } from '../../../../developer';
|
||||
@@ -10,15 +11,16 @@ export default function DeveloperSettings() {
|
||||
const [reactDevtoolsEnabled, setReactDevtoolsEnabled] = useSetting<boolean>('developer.rdt.enabled', false);
|
||||
const [reactDevtoolsIP, setReactDevtoolsIP] = useSetting<string>('developer.rdt.ip', '');
|
||||
const textRef = useRef<HTMLDivElement>(null);
|
||||
const { t } = useTranslation('DeveloperIndex');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field
|
||||
label="Enable Valve Internal"
|
||||
label={t('label_valve_internal')}
|
||||
description={
|
||||
<span style={{ whiteSpace: 'pre-line' }}>
|
||||
Enables the Valve internal developer menu.{' '}
|
||||
<span style={{ color: 'red' }}>Do not touch anything in this menu unless you know what it does.</span>
|
||||
{t('valve_internal_desc1')}{' '}
|
||||
<span style={{ color: 'red' }}>{t('valve_internal_desc2')}</span>
|
||||
</span>
|
||||
}
|
||||
icon={<FaSteamSymbol style={{ display: 'block' }} />}
|
||||
@@ -55,12 +57,11 @@ export default function DeveloperSettings() {
|
||||
}
|
||||
>
|
||||
<Field
|
||||
label="Enable React DevTools"
|
||||
label={t('react_devtools_label')}
|
||||
description={
|
||||
<>
|
||||
<span style={{ whiteSpace: 'pre-line' }}>
|
||||
Enables connection to a computer running React DevTools. Changing this setting will reload Steam. Set
|
||||
the IP address before enabling.
|
||||
{t('react_devtools_desc')}
|
||||
</span>
|
||||
<div ref={textRef}>
|
||||
<TextField label={'IP'} value={reactDevtoolsIP} onChange={(e) => setReactDevtoolsIP(e?.target.value)} />
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Dropdown, Field } from 'decky-frontend-lib';
|
||||
import { FunctionComponent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Logger from '../../../../logger';
|
||||
import { callUpdaterMethod } from '../../../../updater';
|
||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||
|
||||
const logger = new Logger('BranchSelect');
|
||||
const { t } = useTranslation('BranchSelect');
|
||||
|
||||
enum UpdateBranch {
|
||||
Stable,
|
||||
@@ -19,7 +21,7 @@ const BranchSelect: FunctionComponent<{}> = () => {
|
||||
return (
|
||||
// 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
|
||||
<Field label="Update Channel">
|
||||
<Field label={t('update_channel_label')}>
|
||||
<Dropdown
|
||||
rgOptions={Object.values(UpdateBranch)
|
||||
.filter((branch) => typeof branch == 'string')
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { Field, Toggle } from 'decky-frontend-lib';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaBug } from 'react-icons/fa';
|
||||
|
||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||
|
||||
export default function RemoteDebuggingSettings() {
|
||||
const [allowRemoteDebugging, setAllowRemoteDebugging] = useSetting<boolean>('cef_forward', false);
|
||||
const { t } = useTranslation('RemoteDebugging');
|
||||
|
||||
return (
|
||||
<Field
|
||||
label="Allow Remote CEF Debugging"
|
||||
label={t('remote_cef_label')}
|
||||
description={
|
||||
<span style={{ whiteSpace: 'pre-line' }}>
|
||||
Allow unauthenticated access to the CEF debugger to anyone in your network
|
||||
{t('remote_cef_desc')}
|
||||
</span>
|
||||
}
|
||||
icon={<FaBug style={{ display: 'block' }} />}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Dropdown, Field, TextField } from 'decky-frontend-lib';
|
||||
import { FunctionComponent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaShapes } from 'react-icons/fa';
|
||||
|
||||
import Logger from '../../../../logger';
|
||||
@@ -7,6 +8,7 @@ import { Store } from '../../../../store';
|
||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||
|
||||
const logger = new Logger('StoreSelect');
|
||||
const { t } = useTranslation('StoreSelect');
|
||||
|
||||
const StoreSelect: FunctionComponent<{}> = () => {
|
||||
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
|
||||
return (
|
||||
<>
|
||||
<Field label="Store Channel">
|
||||
<Field label={t('store_channel_label')}>
|
||||
<Dropdown
|
||||
rgOptions={Object.values(Store)
|
||||
.filter((store) => typeof store == 'string')
|
||||
@@ -33,7 +35,7 @@ const StoreSelect: FunctionComponent<{}> = () => {
|
||||
</Field>
|
||||
{selectedStore == Store.Custom && (
|
||||
<Field
|
||||
label="Custom Store"
|
||||
label={t('custom_store_label')}
|
||||
indentLevel={1}
|
||||
description={
|
||||
<TextField
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { useCallback } from 'react';
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaArrowDown } from 'react-icons/fa';
|
||||
|
||||
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
|
||||
@@ -20,6 +21,7 @@ import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
|
||||
import WithSuspense from '../../../WithSuspense';
|
||||
|
||||
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
|
||||
const { t } = useTranslation('Updater');
|
||||
|
||||
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
|
||||
const SP = findSP();
|
||||
@@ -45,7 +47,7 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
||||
<MarkdownRenderer onDismiss={closeModal}>{versionInfo.all[id].body}</MarkdownRenderer>
|
||||
</WithSuspense>
|
||||
) : (
|
||||
'no patch notes for this version'
|
||||
t("no_patch_notes_desc")
|
||||
)}
|
||||
</div>
|
||||
</Focusable>
|
||||
@@ -58,7 +60,7 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
||||
initialColumn={0}
|
||||
autoFocus={true}
|
||||
fnGetColumnWidth={() => SP.innerWidth}
|
||||
name="Decky Updates"
|
||||
name={t('decky_updates') as string}
|
||||
/>
|
||||
</FocusRing>
|
||||
</Focusable>
|
||||
@@ -95,11 +97,11 @@ export default function UpdaterSettings() {
|
||||
<Field
|
||||
onOptionsActionDescription={versionInfo?.all ? 'Patch Notes' : undefined}
|
||||
onOptionsButton={versionInfo?.all ? showPatchNotes : undefined}
|
||||
label="Updates"
|
||||
label={t('updates_label')}
|
||||
description={
|
||||
versionInfo && (
|
||||
<span style={{ whiteSpace: 'pre-line' }}>{`Current version: ${versionInfo.current}\n${
|
||||
versionInfo.updatable ? `Latest version: ${versionInfo.remote?.tag_name}` : ''
|
||||
<span style={{ whiteSpace: 'pre-line' }}>{`${t('updates_cur_version', versionInfo.current)}\n${
|
||||
versionInfo.updatable ? t('updates_lat_version', versionInfo.remote?.tag_name) : ''
|
||||
}`}</span>
|
||||
)
|
||||
}
|
||||
@@ -129,10 +131,10 @@ export default function UpdaterSettings() {
|
||||
}
|
||||
>
|
||||
{checkingForUpdates
|
||||
? 'Checking'
|
||||
? t('updates_checking')
|
||||
: !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
|
||||
? 'Check For Updates'
|
||||
: 'Install Update'}
|
||||
? t('updates_check_button')
|
||||
: t('updates_install_button')}
|
||||
</DialogButton>
|
||||
) : (
|
||||
<ProgressBarWithInfo
|
||||
@@ -140,7 +142,7 @@ export default function UpdaterSettings() {
|
||||
bottomSeparator="none"
|
||||
nProgress={updateProgress}
|
||||
indeterminate={reloading}
|
||||
sOperationText={reloading ? 'Reloading' : 'Updating'}
|
||||
sOperationText={reloading ? t("updates_reloading") : t("updates_updating")}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DialogButton, Field, TextField, Toggle } from 'decky-frontend-lib';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaShapes, FaTools } from 'react-icons/fa';
|
||||
|
||||
import { installFromURL } from '../../../../store';
|
||||
@@ -16,6 +17,7 @@ export default function GeneralSettings({
|
||||
setIsDeveloper: (val: boolean) => void;
|
||||
}) {
|
||||
const [pluginURL, setPluginURL] = useState('');
|
||||
const { t } = useTranslation('SettingsGeneralIndex');
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -24,8 +26,8 @@ export default function GeneralSettings({
|
||||
<StoreSelect />
|
||||
<RemoteDebuggingSettings />
|
||||
<Field
|
||||
label="Developer mode"
|
||||
description={<span style={{ whiteSpace: 'pre-line' }}>Enables Decky's developer settings.</span>}
|
||||
label={t("developer_mode_label")}
|
||||
description={<span style={{ whiteSpace: 'pre-line' }}>{t("developer_mode_desc")}</span>}
|
||||
icon={<FaTools style={{ display: 'block' }} />}
|
||||
>
|
||||
<Toggle
|
||||
@@ -36,12 +38,12 @@ export default function GeneralSettings({
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Manual plugin install"
|
||||
label={t("manual_plugin_label")}
|
||||
description={<TextField label={'URL'} value={pluginURL} onChange={(e) => setPluginURL(e?.target.value)} />}
|
||||
icon={<FaShapes style={{ display: 'block' }} />}
|
||||
>
|
||||
<DialogButton disabled={pluginURL.length == 0} onClick={() => installFromURL(pluginURL)}>
|
||||
Install
|
||||
{t("manual_plugin_install_button")}
|
||||
</DialogButton>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DialogButton, Focusable, Menu, MenuItem, showContextMenu } from 'decky-frontend-lib';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaDownload, FaEllipsisH } from 'react-icons/fa';
|
||||
|
||||
import { requestPluginInstall } from '../../../../store';
|
||||
@@ -7,6 +8,7 @@ import { useDeckyState } from '../../../DeckyState';
|
||||
|
||||
export default function PluginList() {
|
||||
const { plugins, updates } = useDeckyState();
|
||||
const { t } = useTranslation('PluginListIndex');
|
||||
|
||||
useEffect(() => {
|
||||
window.DeckyPluginLoader.checkPluginUpdates();
|
||||
@@ -15,7 +17,7 @@ export default function PluginList() {
|
||||
if (plugins.length === 0) {
|
||||
return (
|
||||
<div>
|
||||
<p>No plugins installed</p>
|
||||
<p>{t('list_no_plugin')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -36,7 +38,7 @@ export default function PluginList() {
|
||||
onClick={() => requestPluginInstall(name, update)}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
Update to {update.name}
|
||||
{t('list_update_to', update.name)}
|
||||
<FaDownload style={{ paddingLeft: '2rem' }} />
|
||||
</div>
|
||||
</DialogButton>
|
||||
@@ -45,11 +47,11 @@ export default function PluginList() {
|
||||
style={{ height: '40px', width: '40px', padding: '10px 12px', minWidth: '40px' }}
|
||||
onClick={(e: MouseEvent) =>
|
||||
showContextMenu(
|
||||
<Menu label="Plugin Actions">
|
||||
<Menu label={t('list_plug_actions_label')}>
|
||||
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(name, version)}>
|
||||
Reload
|
||||
{t('reload')}
|
||||
</MenuItem>
|
||||
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(name)}>Uninstall</MenuItem>
|
||||
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(name)}>{t('uninstall')}</MenuItem>
|
||||
</Menu>,
|
||||
e.currentTarget ?? window,
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
staticClasses,
|
||||
} from 'decky-frontend-lib';
|
||||
import { FC, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
|
||||
|
||||
@@ -17,6 +18,8 @@ interface PluginCardProps {
|
||||
plugin: StorePlugin;
|
||||
}
|
||||
|
||||
const { t } = useTranslation('PluginCard');
|
||||
|
||||
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
const [selectedOption, setSelectedOption] = useState<number>(0);
|
||||
const buttonRef = useRef<HTMLDivElement>(null);
|
||||
@@ -173,7 +176,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
label: version.name,
|
||||
})) as SingleDropdownOption[]
|
||||
}
|
||||
strDefaultLabel={'Select a version'}
|
||||
strDefaultLabel={t('select_version') as string}
|
||||
selectedOption={selectedOption}
|
||||
onChange={({ data }) => setSelectedOption(data)}
|
||||
/>
|
||||
|
||||
@@ -22,6 +22,8 @@ import { FaReact } from 'react-icons/fa';
|
||||
import Logger from './logger';
|
||||
import { getSetting } from './utils/settings';
|
||||
|
||||
import "i18n.ts";
|
||||
|
||||
const logger = new Logger('DeveloperMode');
|
||||
|
||||
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 { DeckyUpdater } from './updater';
|
||||
import "i18n.ts";
|
||||
|
||||
declare global {
|
||||
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 { VerInfo, callUpdaterMethod } from './updater';
|
||||
import { getSetting } from './utils/settings';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const StorePage = lazy(() => import('./components/store/Store'));
|
||||
const SettingsPage = lazy(() => import('./components/settings'));
|
||||
|
||||
const FilePicker = lazy(() => import('./components/modals/filepicker'));
|
||||
|
||||
const { t } = useTranslation('plugin-loader');
|
||||
|
||||
declare global {
|
||||
interface Window {}
|
||||
}
|
||||
@@ -109,7 +112,7 @@ class PluginLoader extends Logger {
|
||||
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
||||
this.toaster.toast({
|
||||
title: 'Decky',
|
||||
body: `Update to ${versionInfo?.remote?.tag_name} available!`,
|
||||
body: t('decky_update_available', versionInfo?.remote?.tag_name),
|
||||
onClick: () => Router.Navigate('/decky/settings'),
|
||||
});
|
||||
this.deckyState.setHasLoaderUpdate(true);
|
||||
@@ -129,7 +132,8 @@ class PluginLoader extends Logger {
|
||||
if (updates?.size > 0) {
|
||||
this.toaster.toast({
|
||||
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'),
|
||||
});
|
||||
}
|
||||
@@ -158,7 +162,7 @@ class PluginLoader extends Logger {
|
||||
}}
|
||||
>
|
||||
<div className={staticClasses.Title} style={{ flexDirection: 'column' }}>
|
||||
Uninstall {name}?
|
||||
{t('plugin_uninstall', name)}
|
||||
</div>
|
||||
</ConfirmModal>,
|
||||
);
|
||||
@@ -244,16 +248,15 @@ class PluginLoader extends Logger {
|
||||
version: version,
|
||||
});
|
||||
} catch (e) {
|
||||
this.error('Error loading plugin ' + name, e);
|
||||
this.error(t('plugin_load_error', name), e);
|
||||
const TheError: FC<{}> = () => (
|
||||
<>
|
||||
Error:{' '}
|
||||
{t("error")}:{' '}
|
||||
<pre>
|
||||
<code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
|
||||
</pre>
|
||||
<>
|
||||
Please go to <FaCog style={{ display: 'inline' }} /> in the Decky menu if you need to uninstall this
|
||||
plugin.
|
||||
{t('plugin_error_uninstall', <FaCog style={{ display: 'inline' }} />)}
|
||||
</>
|
||||
</>
|
||||
);
|
||||
@@ -263,7 +266,7 @@ class PluginLoader extends Logger {
|
||||
content: <TheError />,
|
||||
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`);
|
||||
}
|
||||
@@ -301,7 +304,7 @@ class PluginLoader extends Logger {
|
||||
// Purposely outside of the FilePicker component as lazy-loaded ModalRoots don't focus correctly
|
||||
<ModalRoot
|
||||
onCancel={() => {
|
||||
reject('User canceled');
|
||||
reject(t('file_picker_cancel_text'));
|
||||
closeModal?.();
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user