Unified translation classes, fixed missing toaster translation and improved the error styling report.

This commit is contained in:
Marco Rodolfi
2023-05-27 13:55:26 +02:00
parent 3a83062438
commit 6e6f8caca8
6 changed files with 159 additions and 140 deletions
+1 -1
View File
@@ -56,7 +56,7 @@
"decky_title": "Decky",
"decky_update_available": "Update to {{tag_name}} available!",
"error": "Error",
"plugin_error_uninstall": "Please go to <0></0> in the Decky menu if you need to uninstall this plugin.",
"plugin_error_uninstall": "Loading {{name}} has caused an exception as shown above. This usually means that the plugin requires an update for the new version of SteamUI. Check if an update is present or evaluate his removal in <0></0> Settings <1></1> <2></2> Plugins.",
"plugin_load_error": {
"message": "Error loading plugin {{name}}",
"toast": "Error loading {{name}}"
+1 -1
View File
@@ -56,7 +56,7 @@
"decky_title": "Decky",
"decky_update_available": "Disponibile aggiornamento a {{tag_name}}!",
"error": "Errore",
"plugin_error_uninstall": "Per rimuovere questo plugin vai su <0></0> nel menu di Decky.",
"plugin_error_uninstall": "Il plugin {{name}} ha causato un'eccezione che è descritta sopra. Questo tipicamente significa che il plugin deve essere aggiornato per funzionare sulla nuova versione di SteamUI. Controlla se è disponibile un'aggiornamento o valutane la rimozione andando in <0></0> Impostazioni <1></1> <2></2> Plugins.",
"plugin_load_error": {
"message": "Errore caricando il plugin {{name}}",
"toast": "Errore caricando {{name}}"
@@ -2,7 +2,7 @@ import { ConfirmModal, Navigation, QuickAccessTab } from 'decky-frontend-lib';
import { FC, useState } from 'react';
import { useTranslation } from 'react-i18next';
import TPluginInstallModal, { TranslatedPart } from './TPluginInstallModal';
import TranslationHelper, { TranslationClass } from '../../utils/TranslationHelper';
interface PluginInstallModalProps {
artifact: string;
@@ -39,21 +39,47 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
onCancel={async () => {
await onCancel();
}}
strTitle={<TPluginInstallModal trans_part={TranslatedPart.TITLE} trans_type={installType} artifact={artifact} />}
strTitle={
<div>
<TranslationHelper
trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
trans_text="title"
i18n_args={{ artifact: artifact }}
install_type={installType}
/>
</div>
}
strOKButtonText={
loading ? (
<TPluginInstallModal trans_part={TranslatedPart.BUTTON_PROC} trans_type={installType} />
<div>
<TranslationHelper
trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
trans_text="button_processing"
install_type={installType}
/>
</div>
) : (
<TPluginInstallModal trans_part={TranslatedPart.BUTTON_IDLE} trans_type={installType} />
<div>
<TranslationHelper
trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
trans_text="button_idle"
install_type={installType}
/>
</div>
)
}
>
<TPluginInstallModal
trans_part={TranslatedPart.DESC}
trans_type={installType}
artifact={artifact}
version={version ? version : ''}
/>
<div>
<TranslationHelper
trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
trans_text="desc"
i18n_args={{
artifact: artifact,
version: version,
}}
install_type={installType}
/>
</div>
{hash == 'False' && <span style={{ color: 'red' }}>{t('PluginInstallModal.no_hash')}</span>}
</ConfirmModal>
);
@@ -1,95 +0,0 @@
import { FC } from 'react';
import { Translation } from 'react-i18next';
import { InstallType } from '../../plugin';
export enum TranslatedPart {
TITLE,
DESC,
BUTTON_IDLE,
BUTTON_PROC,
}
interface TPluginInstallModalProps {
trans_part: TranslatedPart;
trans_type: number;
artifact?: string;
version?: string;
}
const TPluginInstallModal: FC<TPluginInstallModalProps> = ({ trans_part, trans_type, artifact, version }) => {
return (
<Translation>
{(t, {}) => {
switch (trans_part) {
case TranslatedPart.TITLE:
switch (trans_type) {
case InstallType.INSTALL:
return <div>{t('PluginInstallModal.install.title', { artifact: artifact })}</div>;
case InstallType.REINSTALL:
return <div>{t('PluginInstallModal.reinstall.title', { artifact: artifact })}</div>;
case InstallType.UPDATE:
return <div>{t('PluginInstallModal.update.title', { artifact: artifact })}</div>;
default:
return null;
}
case TranslatedPart.DESC:
switch (trans_type) {
case InstallType.INSTALL:
return (
<div>
{t('PluginInstallModal.install.desc', {
artifact: artifact,
version: version,
})}
</div>
);
case InstallType.REINSTALL:
return (
<div>
{t('PluginInstallModal.reinstall.desc', {
artifact: artifact,
version: version,
})}
</div>
);
case InstallType.UPDATE:
return (
<div>
{t('PluginInstallModal.update.desc', {
artifact: artifact,
version: version,
})}
</div>
);
default:
return null;
}
case TranslatedPart.BUTTON_IDLE:
switch (trans_type) {
case InstallType.INSTALL:
return <div>{t('PluginInstallModal.install.button_idle')}</div>;
case InstallType.REINSTALL:
return <div>{t('PluginInstallModal.reinstall.button_idle')}</div>;
case InstallType.UPDATE:
return <div>{t('PluginInstallModal.update.button_idle')}</div>;
default:
return null;
}
case TranslatedPart.BUTTON_PROC:
switch (trans_type) {
case InstallType.INSTALL:
return <div>{t('PluginInstallModal.install.button_processing')}</div>;
case InstallType.REINSTALL:
return <div>{t('PluginInstallModal.reinstall.button_processing')}</div>;
case InstallType.UPDATE:
return <div>{t('PluginInstallModal.update.button_processing')}</div>;
default:
return null;
}
}
}}
</Translation>
);
};
export default TPluginInstallModal;
+62 -33
View File
@@ -1,8 +1,19 @@
import { ConfirmModal, ModalRoot, Patch, QuickAccessTab, Router, showModal, sleep } from 'decky-frontend-lib';
import { FC, lazy } from 'react';
import { Trans, Translation } from 'react-i18next';
import { IconContext } from 'react-icons';
import { FaCog, FaExclamationCircle, FaPlug } from 'react-icons/fa';
import {
ConfirmModal,
ModalRoot,
PanelSection,
PanelSectionRow,
Patch,
QuickAccessTab,
Router,
showModal,
sleep,
staticClasses,
} from 'decky-frontend-lib';
import { CSSProperties, FC, lazy } from 'react';
import { Trans } from 'react-i18next';
import { BsGearFill } from 'react-icons/bs';
import { FaArrowRight, FaExclamationCircle, FaPlug } from 'react-icons/fa';
import { DeckyState, DeckyStateContextProvider, useDeckyState } from './components/DeckyState';
import LegacyPlugin from './components/LegacyPlugin';
@@ -21,6 +32,7 @@ import OldTabsHook from './tabs-hook.old';
import Toaster from './toaster';
import { VerInfo, callUpdaterMethod } from './updater';
import { getSetting } from './utils/settings';
import TranslationHelper, { TranslationClass } from './utils/TranslationHelper';
const StorePage = lazy(() => import('./components/store/Store'));
const SettingsPage = lazy(() => import('./components/settings'));
@@ -100,10 +112,14 @@ class PluginLoader extends Logger {
const versionInfo = await this.updateVersion();
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
this.toaster.toast({
//title: t('PluginLoader.decky_title'),
title: 'Decky',
//body: t('PluginLoader.decky_update_available', { tag_name: versionInfo?.remote?.tag_name }),
body: `Update to ${versionInfo?.remote?.tag_name} available!`,
title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />,
body: (
<TranslationHelper
trans_class={TranslationClass.PLUGIN_LOADER}
trans_text="decky_update_available"
i18n_args={{ tag_name: versionInfo?.remote?.tag_name }}
/>
),
onClick: () => Router.Navigate('/decky/settings'),
});
this.deckyState.setHasLoaderUpdate(true);
@@ -122,10 +138,14 @@ class PluginLoader extends Logger {
const updates = await this.checkPluginUpdates();
if (updates?.size > 0) {
this.toaster.toast({
//title: t('PluginLoader.decky_title'),
title: 'Decky',
//body: t('PluginLoader.plugin_update', { count: updates.size }),
body: `Updates available for ${updates.size} plugin${updates.size > 1 ? 's' : ''}!`,
title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />,
body: (
<TranslationHelper
trans_class={TranslationClass.PLUGIN_LOADER}
trans_text="plugin_update"
i18n_args={{ count: updates.size }}
/>
),
onClick: () => Router.Navigate('/decky/settings/plugins'),
});
}
@@ -256,25 +276,29 @@ class PluginLoader extends Logger {
});
} catch (e) {
this.error('Error loading plugin ' + name, e);
const style: CSSProperties = { verticalAlign: 'middle' };
const TheError: FC<{}> = () => (
<Translation>
{(t, {}) => {
return (
<>
{t('PluginLoader.error')}:{' '}
<pre>
<code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
</pre>
<div>
<Trans
i18nKey="PluginLoader.plugin_error_uninstall"
components={[<FaCog style={{ verticalAlign: 'middle' }} />]}
/>
</div>
</>
);
}}
</Translation>
<PanelSection>
<PanelSectionRow>
<div className={staticClasses.FriendsTitle} style={{ display: 'flex', justifyContent: 'center' }}>
{<TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="error" />}
</div>
</PanelSectionRow>
<PanelSectionRow>
<pre style={{ overflowX: 'scroll' }}>
<code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
</pre>
</PanelSectionRow>
<PanelSectionRow>
<div className={staticClasses.Text}>
<Trans
i18nKey="PluginLoader.plugin_error_uninstall"
values={{ name: name }}
components={[<BsGearFill style={style} />, <FaArrowRight style={style} />, <FaPlug style={style} />]}
/>
</div>
</PanelSectionRow>
</PanelSection>
);
this.plugins.push({
name: name,
@@ -283,8 +307,13 @@ class PluginLoader extends Logger {
icon: <FaExclamationCircle />,
});
this.toaster.toast({
//title: t('PluginLoader.plugin_load_error.toast', { name: name }),
title: 'Error loading ' + name,
title: (
<TranslationHelper
trans_class={TranslationClass.PLUGIN_LOADER}
trans_text="plugin_load_error.toast"
i18n_args={{ name: name }}
/>
),
body: '' + e,
icon: <FaExclamationCircle />,
});
+59
View File
@@ -0,0 +1,59 @@
import { FC } from 'react';
import { Translation } from 'react-i18next';
import Logger from '../logger';
import { InstallType } from '../plugin';
export enum TranslationClass {
PLUGIN_LOADER = 'PluginLoader',
PLUGIN_INSTALL_MODAL = 'PluginInstallModal',
}
interface TranslationHelperProps {
trans_class: TranslationClass;
trans_text: string;
i18n_args?: {};
install_type?: number;
}
const logger = new Logger('TranslationHelper');
const TranslationHelper: FC<TranslationHelperProps> = ({
trans_class,
trans_text,
i18n_args = null,
install_type = 0,
}) => {
return (
<Translation>
{(t, {}) => {
switch (trans_class) {
case TranslationClass.PLUGIN_LOADER:
return i18n_args
? t(TranslationClass.PLUGIN_LOADER + '.' + trans_text, i18n_args)
: t(TranslationClass.PLUGIN_LOADER + '.' + trans_text);
case TranslationClass.PLUGIN_INSTALL_MODAL:
switch (install_type) {
case InstallType.INSTALL:
return i18n_args
? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.install.' + trans_text, i18n_args)
: t(TranslationClass.PLUGIN_INSTALL_MODAL + '.install.' + trans_text);
case InstallType.REINSTALL:
return i18n_args
? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.reinstall.' + trans_text, i18n_args)
: t(TranslationClass.PLUGIN_INSTALL_MODAL + '.reinstall.' + trans_text);
case InstallType.UPDATE:
return i18n_args
? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.update.' + trans_text, i18n_args)
: t(TranslationClass.PLUGIN_INSTALL_MODAL + '.update.' + trans_text);
}
default:
logger.error('We should never fall in the default case!');
return '';
}
}}
</Translation>
);
};
export default TranslationHelper;