fix: #74 Reduce freezes due to translation.

This commit is contained in:
Quentin “Storm1er” Decaunes 2020-03-19 12:14:26 +01:00 committed by Quentin Decaunes
parent 2cf1007b5d
commit df2d7d6357
24 changed files with 378 additions and 261 deletions

View File

@ -10,12 +10,11 @@ import SysInfoContext, {
} from "./contexts/SysInfoContext"; } from "./contexts/SysInfoContext";
import LightModeContext, { lightModeSettingsKey } from "./contexts/LightModeContext"; import LightModeContext, { lightModeSettingsKey } from "./contexts/LightModeContext";
import { checkNewVersion } from "./contexts/RyzenControllerAppContext"; import { checkNewVersion } from "./contexts/RyzenControllerAppContext";
import LocaleContext, { getTranslation, localeSettingsKey } from "./contexts/LocaleContext"; import LocaleContext, { localeSettingsKey } from "./contexts/LocaleContext";
import LocaleSelectorModal from "./components/LocaleSelectorModal"; import LocaleSelectorModal from "./components/LocaleSelectorModal";
const si = window.require("systeminformation"); const si = window.require("systeminformation");
const electronSettings = window.require("electron-settings"); const electronSettings = window.require("electron-settings");
type AppState = { type AppState = {
sysinfo: SysInfoState; sysinfo: SysInfoState;
lightMode: { lightMode: {
@ -25,7 +24,6 @@ type AppState = {
locale: { locale: {
is: AvailableLanguages; is: AvailableLanguages;
change(to: AvailableLanguages): void; change(to: AvailableLanguages): void;
getTranslation(id: string, fallback?: string, variables?: Record<string, string>): string;
}; };
}; };
@ -52,7 +50,6 @@ class App extends React.Component<{}, AppState> {
locale: { locale: {
is: this.getLatestLocale(), is: this.getLatestLocale(),
change: this.changeLocale.bind(this), change: this.changeLocale.bind(this),
getTranslation: getTranslation,
}, },
}; };

View File

@ -1,13 +1,15 @@
import * as React from "react"; import * as React from "react";
import LocaleContext, { getTranslation } from "../contexts/LocaleContext"; import LocaleContext, { getTranslation } from "../contexts/LocaleContext";
const localeSelectorModalTitle = getTranslation("app.localeSelectorModalTitle", "Change language");
export default function LocaleSelectorModal() { export default function LocaleSelectorModal() {
return ( return (
<LocaleContext.Consumer> <LocaleContext.Consumer>
{locale => ( {locale => (
<div id="locale-selector-modal" uk-modal=""> <div id="locale-selector-modal" uk-modal="">
<div className="uk-modal-dialog uk-modal-body"> <div className="uk-modal-dialog uk-modal-body">
<h2 className="uk-modal-title">{getTranslation("app.localeSelectorModalTitle", "Change language")}</h2> <h2 className="uk-modal-title">{localeSelectorModalTitle}</h2>
<select <select
defaultValue={locale.is} defaultValue={locale.is}
className="uk-select" className="uk-select"

View File

@ -3,6 +3,12 @@ import Card from "./Card";
import RyzenControllerAppContext from "../contexts/RyzenControllerAppContext"; import RyzenControllerAppContext from "../contexts/RyzenControllerAppContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
const whenLaptopPluggedInTitle = getTranslation("presetAutoApply.whenLaptopPluggedIn", "When laptop plugged in");
const nonePresetMessage = getTranslation("presetAutoApply.nonePreset", "None");
const whenLaptopPluggedOutTitle = getTranslation("presetAutoApply.whenLaptopPluggedOut", "When laptop plugged out");
const whenSessionResumeTitle = getTranslation("presetAutoApply.whenSessionResume", "When session resume");
const whenRCStartTitle = getTranslation("presetAutoApply.whenRCStart", "When Ryzen Controller starts");
class PresetAutoApplyCards extends React.PureComponent { class PresetAutoApplyCards extends React.PureComponent {
updateOnLaptopPluggedIn(ryzenControllerAppContext: RyzenControllerAppContextType) { updateOnLaptopPluggedIn(ryzenControllerAppContext: RyzenControllerAppContextType) {
return function(event: React.ChangeEvent<HTMLSelectElement>): void { return function(event: React.ChangeEvent<HTMLSelectElement>): void {
@ -44,16 +50,13 @@ class PresetAutoApplyCards extends React.PureComponent {
<div className="uk-flex uk-flex-wrap"> <div className="uk-flex uk-flex-wrap">
{window.require("os").platform() === "win32" ? ( {window.require("os").platform() === "win32" ? (
<React.Fragment> <React.Fragment>
<Card <Card title={whenLaptopPluggedInTitle} type="small">
title={getTranslation("presetAutoApply.whenLaptopPluggedIn", "When laptop plugged in")}
type="small"
>
<select <select
onChange={this.updateOnLaptopPluggedIn(ryzenControllerAppContext)} onChange={this.updateOnLaptopPluggedIn(ryzenControllerAppContext)}
className="uk-select" className="uk-select"
value={ryzenControllerAppContext.settings.onLaptopPluggedIn || ""} value={ryzenControllerAppContext.settings.onLaptopPluggedIn || ""}
> >
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option> <option value="">{nonePresetMessage}</option>
{presetNames.map(presetName => { {presetNames.map(presetName => {
return ( return (
<option key={`1_${presetName}`} value={presetName}> <option key={`1_${presetName}`} value={presetName}>
@ -63,16 +66,13 @@ class PresetAutoApplyCards extends React.PureComponent {
})} })}
</select> </select>
</Card> </Card>
<Card <Card title={whenLaptopPluggedOutTitle} type="small">
title={getTranslation("presetAutoApply.whenLaptopPluggedOut", "When laptop plugged out")}
type="small"
>
<select <select
onChange={this.updateOnLaptopPluggedOut(ryzenControllerAppContext)} onChange={this.updateOnLaptopPluggedOut(ryzenControllerAppContext)}
className="uk-select" className="uk-select"
value={ryzenControllerAppContext.settings.onLaptopPluggedOut || ""} value={ryzenControllerAppContext.settings.onLaptopPluggedOut || ""}
> >
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option> <option value="">{nonePresetMessage}</option>
{presetNames.map(presetName => { {presetNames.map(presetName => {
return ( return (
<option key={`2_${presetName}`} value={presetName}> <option key={`2_${presetName}`} value={presetName}>
@ -84,13 +84,13 @@ class PresetAutoApplyCards extends React.PureComponent {
</Card> </Card>
</React.Fragment> </React.Fragment>
) : null} ) : null}
<Card title={getTranslation("presetAutoApply.whenSessionResume", "When session resume")} type="small"> <Card title={whenSessionResumeTitle} type="small">
<select <select
onChange={this.updateOnSessionResume(ryzenControllerAppContext)} onChange={this.updateOnSessionResume(ryzenControllerAppContext)}
className="uk-select" className="uk-select"
value={ryzenControllerAppContext.settings.onSessionResume || ""} value={ryzenControllerAppContext.settings.onSessionResume || ""}
> >
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option> <option value="">{nonePresetMessage}</option>
{presetNames.map(presetName => { {presetNames.map(presetName => {
return ( return (
<option key={`2_${presetName}`} value={presetName}> <option key={`2_${presetName}`} value={presetName}>
@ -100,13 +100,13 @@ class PresetAutoApplyCards extends React.PureComponent {
})} })}
</select> </select>
</Card> </Card>
<Card title={getTranslation("presetAutoApply.whenRCStart", "When Ryzen Controller starts")} type="small"> <Card title={whenRCStartTitle} type="small">
<select <select
onChange={this.updateOnRCStart(ryzenControllerAppContext)} onChange={this.updateOnRCStart(ryzenControllerAppContext)}
className="uk-select" className="uk-select"
value={ryzenControllerAppContext.settings.onRCStart || ""} value={ryzenControllerAppContext.settings.onRCStart || ""}
> >
<option value="">{getTranslation("presetAutoApply.nonePreset", "None")}</option> <option value="">{nonePresetMessage}</option>
{presetNames.map(presetName => { {presetNames.map(presetName => {
return ( return (
<option key={`2_${presetName}`} value={presetName}> <option key={`2_${presetName}`} value={presetName}>

View File

@ -3,7 +3,36 @@ import NotificationContext from "../contexts/NotificationContext";
import RyzenControllerAppContext, { executeRyzenAdjUsingPreset } from "../contexts/RyzenControllerAppContext"; import RyzenControllerAppContext, { executeRyzenAdjUsingPreset } from "../contexts/RyzenControllerAppContext";
import SysInfoContext from "../contexts/SysInfoContext"; import SysInfoContext from "../contexts/SysInfoContext";
import PresetsOnlineContext from "../contexts/PresetsOnline"; import PresetsOnlineContext from "../contexts/PresetsOnline";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation, variablesInTranslation } from "../contexts/LocaleContext";
const applyBtnText = getTranslation("presetButtons.apply", "Apply");
const deleteBtnText = getTranslation("presetButtons.delete", "Delete");
const loadBtnText = getTranslation("presetButtons.load", "Load");
const uploadBtnText = getTranslation("presetButtons.upload", "Upload");
const uploadSucceedText = getTranslation("presetButtons.uploadSucceed", "Preset {preset} has been uploaded");
const uploadErrorText = getTranslation("presetButtons.uploadError", "An error occured while uploading the preset");
const loadedPresetText = getTranslation("presetButtons.loadedPreset", "Preset {preset} has been loaded.");
const confirmDeletionText = getTranslation("presetButtons.confirmDeletion", 'Are you sure to delete "{preset}"?');
const applyPresetTooltip = getTranslation(
"presetButtons.applyPresetTooltip",
"The preset will be loaded in RyzenAdj's tabs and applied."
);
const loadPresetTooltip = getTranslation(
"presetButtons.loadPresetTooltip",
"The preset will be loaded in RyzenAdj's tabs but not applied."
);
const mustWaitForSignatureGenText = getTranslation(
"presetButtons.mustWaitForSignatureGen",
"You must wait for laptop signature to be generated"
);
const presetWithSameNameAlreadyExistOnlineText = getTranslation(
"presetButtons.presetWithSameNameAlreadyExistOnline",
"A preset with the same name already exist online"
);
const uploadPresetConfirmationText = getTranslation(
"presetButtons.uploadPresetConfirmation",
"Are you sure to upload the preset {preset}?"
);
type PresetButtonsProps = { type PresetButtonsProps = {
presetName: string; presetName: string;
@ -19,31 +48,25 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
<div className="uk-button-group uk-margin-right"> <div className="uk-button-group uk-margin-right">
<button <button
className="uk-button uk-button-small uk-button-primary" className="uk-button uk-button-small uk-button-primary"
uk-tooltip={`title: ${getTranslation( uk-tooltip={`title: ${applyPresetTooltip}`}
"presetButtons.applyPresetTooltip",
"The preset will be loaded in RyzenAdj's tabs and applied."
)}`}
onClick={this.applyPreset(ryzenControllerAppContext)} onClick={this.applyPreset(ryzenControllerAppContext)}
> >
{getTranslation("presetButtons.apply", "Apply")} {applyBtnText}
</button> </button>
<button <button
className="uk-button uk-button-small uk-button-danger" className="uk-button uk-button-small uk-button-danger"
onClick={this.removePreset(ryzenControllerAppContext)} onClick={this.removePreset(ryzenControllerAppContext)}
> >
{getTranslation("presetButtons.delete", "Delete")} {deleteBtnText}
</button> </button>
</div> </div>
<div className="uk-button-group uk-margin-right"> <div className="uk-button-group uk-margin-right">
<button <button
className="uk-button uk-button-small uk-button-default" className="uk-button uk-button-small uk-button-default"
uk-tooltip={`title: ${getTranslation( uk-tooltip={`title: ${loadPresetTooltip}`}
"presetButtons.loadPresetTooltip",
"The preset will be loaded in RyzenAdj's tabs but not applied."
)}`}
onClick={this.loadPreset(ryzenControllerAppContext)} onClick={this.loadPreset(ryzenControllerAppContext)}
> >
{getTranslation("presetButtons.load", "Load")} {loadBtnText}
</button> </button>
<SysInfoContext.Consumer> <SysInfoContext.Consumer>
{sysinfo => ( {sysinfo => (
@ -57,7 +80,7 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
sysinfo.permissiveSignature sysinfo.permissiveSignature
)} )}
> >
{getTranslation("presetButtons.upload", "Upload")} {uploadBtnText}
</button> </button>
)} )}
</PresetsOnlineContext.Consumer> </PresetsOnlineContext.Consumer>
@ -86,9 +109,7 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void { ): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
return () => { return () => {
if (!signature || !permissiveSignature) { if (!signature || !permissiveSignature) {
NotificationContext.warning( NotificationContext.warning(mustWaitForSignatureGenText);
getTranslation("presetButtons.mustWaitForSignatureGen", "You must wait for laptop signature to be generated")
);
return; return;
} }
let presetsWithSameName = presetsOnlineContext.list.filter((preset: ApiPreset) => { let presetsWithSameName = presetsOnlineContext.list.filter((preset: ApiPreset) => {
@ -98,21 +119,15 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
); );
}); });
if (presetsWithSameName.length > 0) { if (presetsWithSameName.length > 0) {
NotificationContext.warning( NotificationContext.warning(presetWithSameNameAlreadyExistOnlineText);
getTranslation(
"presetButtons.presetWithSameNameAlreadyExistOnline",
"A preset with the same name already exist online"
)
);
return; return;
} }
const uploadPresetConfirmationTextVariable = variablesInTranslation(uploadPresetConfirmationText, {
preset: this.props.presetName,
});
window window
.require("uikit") .require("uikit")
.modal.confirm( .modal.confirm(uploadPresetConfirmationTextVariable)
getTranslation("presetButtons.uploadPresetConfirmation", "Are you sure to upload the preset {preset}?", {
preset: this.props.presetName,
})
)
.then(() => { .then(() => {
presetsOnlineContext presetsOnlineContext
.uploadPreset({ .uploadPreset({
@ -122,17 +137,14 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
ryzenAdjArguments: this.props.preset, ryzenAdjArguments: this.props.preset,
}) })
.then(value => { .then(value => {
NotificationContext.success( const uploadSucceedTextVariable = variablesInTranslation(uploadSucceedText, {
getTranslation("presetButtons.uploadSucceed", "Preset {preset} has been uploaded", {
preset: value.name, preset: value.name,
}) });
); NotificationContext.success(uploadSucceedTextVariable);
presetsOnlineContext.update(); presetsOnlineContext.update();
}) })
.catch(() => { .catch(() => {
NotificationContext.error( NotificationContext.error(uploadErrorText);
getTranslation("presetButtons.uploadError", "An error occured while uploading the preset")
);
}); });
}); });
}; };
@ -143,11 +155,10 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void { ): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
return () => { return () => {
ryzenControllerAppContext.updateCurrentSettings(this.props.preset); ryzenControllerAppContext.updateCurrentSettings(this.props.preset);
NotificationContext.talk( const loadedPresetTextVariable = variablesInTranslation(loadedPresetText, {
getTranslation("presetButtons.loadedPreset", "Preset {preset} has been loaded.", {
preset: this.props.presetName, preset: this.props.presetName,
}) });
); NotificationContext.talk(loadedPresetTextVariable);
}; };
} }
@ -155,12 +166,11 @@ class PresetButtons extends React.Component<PresetButtonsProps, {}> {
ryzenControllerAppContext: RyzenControllerAppContextType ryzenControllerAppContext: RyzenControllerAppContextType
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void { ): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
return () => { return () => {
require("uikit") const confirmDeletionTextVariable = variablesInTranslation(confirmDeletionText, {
.modal.confirm(
getTranslation("presetButtons.confirmDeletion", 'Are you sure to delete "{preset}"?', {
preset: this.props.presetName, preset: this.props.presetName,
}) });
) require("uikit")
.modal.confirm(confirmDeletionTextVariable)
.then(() => { .then(() => {
ryzenControllerAppContext.removePreset(this.props.presetName); ryzenControllerAppContext.removePreset(this.props.presetName);
}) })

View File

@ -2,6 +2,14 @@ import * as React from "react";
import LightModeContext from "../contexts/LightModeContext"; import LightModeContext from "../contexts/LightModeContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
const youDontHaveAnyText = getTranslation("presetListEmpty.youDontHaveAny", "You don't have any preset yet");
const sentencePart1Text = getTranslation("presetListEmpty.sentencePart1", "You can create one by using the");
const createPresetBtnText = getTranslation("presetListEmpty.createPresetBtn", "Create preset");
const sentencePart2Text = getTranslation(
"presetListEmpty.sentencePart2",
"button available on Ryzen Adj settings tabs (CPU, GPU, ...)."
);
function PresetListEmpty() { function PresetListEmpty() {
return ( return (
<LightModeContext.Consumer> <LightModeContext.Consumer>
@ -10,18 +18,13 @@ function PresetListEmpty() {
return ( return (
<div className={`uk-flex uk-flex-center uk-margin-left uk-margin-right`}> <div className={`uk-flex uk-flex-center uk-margin-left uk-margin-right`}>
<div className={`uk-card uk-card-default uk-card-body uk-width-1-2@m ${classes}`}> <div className={`uk-card uk-card-default uk-card-body uk-width-1-2@m ${classes}`}>
<h3 className="uk-card-title"> <h3 className="uk-card-title">{youDontHaveAnyText}</h3>
{getTranslation("presetListEmpty.youDontHaveAny", "You don't have any preset yet")}
</h3>
<p> <p>
{getTranslation("presetListEmpty.sentencePart1", "You can create one by using the")} {sentencePart1Text}
<button className="uk-button uk-button-default uk-margin-small-left uk-margin-small-right"> <button className="uk-button uk-button-default uk-margin-small-left uk-margin-small-right">
{getTranslation("presetListEmpty.createPresetBtn", "Create preset")} {createPresetBtnText}
</button> </button>
{getTranslation( {sentencePart2Text}
"presetListEmpty.sentencePart2",
"button available on Ryzen Adj settings tabs (CPU, GPU, ...)."
)}
</p> </p>
</div> </div>
</div> </div>

View File

@ -6,6 +6,24 @@ import PresetOnlineLine from "../components/PresetOnlineLine";
import { isPresetValid } from "../contexts/RyzenControllerAppContext"; import { isPresetValid } from "../contexts/RyzenControllerAppContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
const errorLoadingPresetsText = getTranslation("PresetOnline.errorLoadingPresets", "Unable to load presets.");
const retryLoadingPresetListBtnText = getTranslation("PresetOnline.retryLoadingPresetListBtn", "Retry");
const loadPresetListBtnText = getTranslation("PresetOnline.loadPresetListBtn", "Load preset list");
const uploadBtnText = getTranslation("PresetOnline.uploadBtn", "Upload");
const sentencePart2Text = getTranslation("PresetOnline.sentencePart2", "button available on your presets.");
const pleaseCheckInternetConnectionText = getTranslation(
"PresetOnline.pleaseCheckInternetConnection",
"Please check your internet connection."
);
const listNotLoadedYetText = getTranslation(
"PresetOnline.listNotLoadedYet",
"List hasn't been loaded or there is no online preset yet."
);
const sentencePart1Text = getTranslation(
"PresetOnline.sentencePart1",
"You can share your own preset by clicking on the"
);
function PresetOnline() { function PresetOnline() {
return ( return (
<SysInfoContext.Consumer> <SysInfoContext.Consumer>
@ -28,14 +46,14 @@ function PresetOnline() {
})} })}
</ul> </ul>
) : presetsOnlineContext.error && !presetsOnlineContext.loading ? ( ) : presetsOnlineContext.error && !presetsOnlineContext.loading ? (
<Card title={getTranslation("PresetOnline.errorLoadingPresets", "Unable to load presets.")}> <Card title={errorLoadingPresetsText}>
{getTranslation("PresetOnline.pleaseCheckInternetConnection", "Please check your internet connection.")} {pleaseCheckInternetConnectionText}
<br /> <br />
<button <button
className="uk-margin-small-bottom uk-button uk-button-small uk-button-default" className="uk-margin-small-bottom uk-button uk-button-small uk-button-default"
onClick={() => presetsOnlineContext.update()} onClick={() => presetsOnlineContext.update()}
> >
{getTranslation("PresetOnline.retryLoadingPresetListBtn", "Retry")} {retryLoadingPresetListBtnText}
</button> </button>
</Card> </Card>
) : presetsOnlineContext.loading || !sysInfoContext?.signature || !sysInfoContext?.permissiveSignature ? ( ) : presetsOnlineContext.loading || !sysInfoContext?.signature || !sysInfoContext?.permissiveSignature ? (
@ -43,27 +61,22 @@ function PresetOnline() {
<div uk-spinner="ratio: 2"></div> <div uk-spinner="ratio: 2"></div>
</div> </div>
) : ( ) : (
<Card <Card title={listNotLoadedYetText}>
title={getTranslation(
"PresetOnline.listNotLoadedYet",
"List hasn't been loaded or there is no online preset yet."
)}
>
<button <button
className="uk-margin-small-bottom uk-button uk-button-small uk-button-default" className="uk-margin-small-bottom uk-button uk-button-small uk-button-default"
onClick={() => presetsOnlineContext.update()} onClick={() => presetsOnlineContext.update()}
> >
{getTranslation("PresetOnline.loadPresetListBtn", "Load preset list")} {loadPresetListBtnText}
</button> </button>
<br /> <br />
{getTranslation("PresetOnline.sentencePart1", "You can share your own preset by clicking on the")} {sentencePart1Text}
<button <button
className="uk-margin-small-right uk-margin-small-left uk-button uk-button-small uk-button-default" className="uk-margin-small-right uk-margin-small-left uk-button uk-button-small uk-button-default"
onClick={() => false} onClick={() => false}
> >
{getTranslation("PresetOnline.uploadBtn", "Upload")} {uploadBtnText}
</button> </button>
{getTranslation("PresetOnline.sentencePart2", "button available on your presets.")} {sentencePart2Text}
</Card> </Card>
); );
}} }}

View File

@ -2,7 +2,31 @@ import * as React from "react";
import RyzenControllerAppContext, { isPresetValid } from "../contexts/RyzenControllerAppContext"; import RyzenControllerAppContext, { isPresetValid } from "../contexts/RyzenControllerAppContext";
import NotificationContext from "../contexts/NotificationContext"; import NotificationContext from "../contexts/NotificationContext";
import PresetsOnlineContext from "../contexts/PresetsOnline"; import PresetsOnlineContext from "../contexts/PresetsOnline";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation, variablesInTranslation } from "../contexts/LocaleContext";
const downloadText = getTranslation("presetOnlineBtn.download", "Download");
const presetLoadedText = getTranslation("presetOnlineBtn.presetLoaded", 'Preset "{presetName}" has been loaded');
const loadText = getTranslation("presetOnlineBtn.load", "Load");
const downloadTooltipText = getTranslation(
"presetOnlineBtn.downloadTooltip",
"Will save the preset to your local preset."
);
const presetSameNameExistText = getTranslation(
"presetOnlineBtn.presetSameNameExist",
"You already have a preset with the same name"
);
const presetInvalidOrObsoleteText = getTranslation(
"presetOnlineBtn.presetInvalidOrObsolete",
'Preset "{presetName}" is invalid or obsolete'
);
const presetDownloadedText = getTranslation(
"presetOnlineBtn.presetDownloaded",
'Preset "{presetName}" has been downloaded'
);
const loadTooltipText = getTranslation(
"presetOnlineBtn.loadTooltip",
"Without saving the preset, it will be loaded in RyzenAdj's tabs but not applied."
);
type PresetOnlineButtonsProps = { type PresetOnlineButtonsProps = {
presetName: string; presetName: string;
@ -20,58 +44,41 @@ function PresetOnlineButtons(props: PresetOnlineButtonsProps) {
<div className="uk-button-group uk-margin-right"> <div className="uk-button-group uk-margin-right">
<button <button
className="uk-button uk-button-small uk-button-primary" className="uk-button uk-button-small uk-button-primary"
uk-tooltip={`title: ${getTranslation( uk-tooltip={`title: ${downloadTooltipText}`}
"presetOnlineBtn.downloadTooltip",
"Will save the preset to your local preset."
)}`}
onClick={() => { onClick={() => {
if (ryzenControllerAppContext.presets.hasOwnProperty(props.presetName)) { if (ryzenControllerAppContext.presets.hasOwnProperty(props.presetName)) {
NotificationContext.warning( NotificationContext.warning(presetSameNameExistText);
getTranslation(
"presetOnlineBtn.presetSameNameExist",
"You already have a preset with the same name"
)
);
return; return;
} }
if (!isPresetValid(props.preset)) { if (!isPresetValid(props.preset)) {
const presetInvalidOrObsoleteMessage = getTranslation( const presetInvalidOrObsoleteTextVariable = variablesInTranslation(presetInvalidOrObsoleteText, {
"presetOnlineBtn.presetInvalidOrObsolete", presetName: props.presetName,
'Preset "{presetName}" is invalid or obsolete', });
{ presetName: props.presetName } NotificationContext.error(presetInvalidOrObsoleteTextVariable);
);
NotificationContext.error(presetInvalidOrObsoleteMessage);
return; return;
} }
ryzenControllerAppContext.addPreset(props.presetName, props.preset); ryzenControllerAppContext.addPreset(props.presetName, props.preset);
const presetDownloadedMessage = getTranslation( const presetDownloadedTextVariable = variablesInTranslation(presetDownloadedText, {
"presetOnlineBtn.presetDownloaded", presetName: props.presetName,
'Preset "{presetName}" has been downloaded', });
{ presetName: props.presetName } NotificationContext.talk(presetDownloadedTextVariable);
);
NotificationContext.talk(presetDownloadedMessage);
return; return;
}} }}
> >
{getTranslation("presetOnlineBtn.download", "Download")} {downloadText}
</button> </button>
<button <button
className="uk-button uk-button-small uk-button-default" className="uk-button uk-button-small uk-button-default"
uk-tooltip={`title: ${getTranslation( uk-tooltip={`title: ${loadTooltipText}`}
"presetOnlineBtn.loadTooltip",
"Without saving the preset, it will be loaded in RyzenAdj's tabs but not applied."
)}`}
onClick={() => { onClick={() => {
ryzenControllerAppContext.updateCurrentSettings(props.preset); ryzenControllerAppContext.updateCurrentSettings(props.preset);
const presetloadedMessage = getTranslation( const presetloadedTextVariable = variablesInTranslation(presetLoadedText, {
"presetOnlineBtn.presetDownloaded", presetName: props.presetName,
'Preset "{presetName}" has been loaded', });
{ presetName: props.presetName } NotificationContext.talk(presetloadedTextVariable);
);
NotificationContext.talk(presetloadedMessage);
}} }}
> >
{getTranslation("presetOnlineBtn.load", "Load")} {loadText}
</button> </button>
</div> </div>
)} )}

View File

@ -4,10 +4,22 @@ import Notification from "../contexts/NotificationContext";
import { createRyzenAdjCommandLine, executeRyzenAdj } from "../contexts/RyzenAdjContext"; import { createRyzenAdjCommandLine, executeRyzenAdj } from "../contexts/RyzenAdjContext";
import LightModeContext from "../contexts/LightModeContext"; import LightModeContext from "../contexts/LightModeContext";
import NotificationContext from "../contexts/NotificationContext"; import NotificationContext from "../contexts/NotificationContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation, variablesInTranslation } from "../contexts/LocaleContext";
const UIkit = require("uikit"); const UIkit = require("uikit");
const applyText = getTranslation("ryzenAdjBottomBar.apply", "Apply");
const createPresetText = getTranslation("ryzenAdjBottomBar.createPreset", "Create preset");
const resetText = getTranslation("ryzenAdjBottomBar.reset", "Reset");
const promptText = getTranslation("ryzenAdjBottomBar.prompt", "New preset name");
const mustProvideNameText = getTranslation("ryzenAdjBottomBar.mustProvideName", "You must provide a name");
const presetCreatedText = getTranslation("ryzenAdjBottomBar.presetCreated", 'Preset "{newPresetName}" created');
const invalidPresetText = getTranslation("ryzenAdjBottomBar.invalidPreset", "Unable to apply invalid preset");
const presetWithSameNameExistText = getTranslation(
"ryzenAdjBottomBar.presetWithSameNameExist",
'A preset with the name "{newPresetName}" already exist'
);
class RyzenAdjBottomBar extends React.PureComponent { class RyzenAdjBottomBar extends React.PureComponent {
render() { render() {
return ( return (
@ -23,19 +35,19 @@ class RyzenAdjBottomBar extends React.PureComponent {
return ( return (
<div className={`uk-padding-small uk-position-fixed uk-position-bottom-right ${classes}`}> <div className={`uk-padding-small uk-position-fixed uk-position-bottom-right ${classes}`}>
<button className="uk-button uk-button-primary" onClick={this.apply(ryzenControllerAppContext)}> <button className="uk-button uk-button-primary" onClick={this.apply(ryzenControllerAppContext)}>
{getTranslation("ryzenAdjBottomBar.apply", "Apply")} {applyText}
</button> </button>
<button <button
className="uk-button uk-button-default uk-margin-left" className="uk-button uk-button-default uk-margin-left"
onClick={this.createNewPreset(ryzenControllerAppContext)} onClick={this.createNewPreset(ryzenControllerAppContext)}
> >
{getTranslation("ryzenAdjBottomBar.createPreset", "Create preset")} {createPresetText}
</button> </button>
<button <button
className="uk-button uk-button-default uk-margin-left" className="uk-button uk-button-default uk-margin-left"
onClick={this.reset(ryzenControllerAppContext)} onClick={this.reset(ryzenControllerAppContext)}
> >
{getTranslation("ryzenAdjBottomBar.reset", "Reset")} {resetText}
</button> </button>
</div> </div>
); );
@ -51,8 +63,8 @@ class RyzenAdjBottomBar extends React.PureComponent {
ryzenControllerAppContext: RyzenControllerAppContextType ryzenControllerAppContext: RyzenControllerAppContextType
): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void { ): (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void {
return function() { return function() {
const promptMessage = getTranslation("ryzenAdjBottomBar.prompt", "New preset name"); const promptMessage = promptText;
const mustProvideNameMessage = getTranslation("ryzenAdjBottomBar.mustProvideName", "You must provide a name"); const mustProvideNameMessage = mustProvideNameText;
UIkit.modal.prompt(promptMessage, "").then((newPresetName: string | null) => { UIkit.modal.prompt(promptMessage, "").then((newPresetName: string | null) => {
if (newPresetName === null) { if (newPresetName === null) {
@ -60,20 +72,15 @@ class RyzenAdjBottomBar extends React.PureComponent {
} else if (newPresetName.length <= 0) { } else if (newPresetName.length <= 0) {
Notification.warning(mustProvideNameMessage); Notification.warning(mustProvideNameMessage);
} else if (ryzenControllerAppContext.presets.hasOwnProperty(newPresetName)) { } else if (ryzenControllerAppContext.presets.hasOwnProperty(newPresetName)) {
const presetWithSameNameExistMessage = getTranslation( const presetWithSameNameExistTextVariable = variablesInTranslation(presetWithSameNameExistText, {
"ryzenAdjBottomBar.presetWithSameNameExist", newPresetName: newPresetName,
'A preset with the name "{newPresetName}" already exist', });
{ newPresetName: newPresetName } Notification.warning(presetWithSameNameExistTextVariable);
);
Notification.warning(presetWithSameNameExistMessage);
} else { } else {
ryzenControllerAppContext.addPreset(newPresetName, ryzenControllerAppContext.currentSettings); ryzenControllerAppContext.addPreset(newPresetName, ryzenControllerAppContext.currentSettings);
const presetCreatedMessage = getTranslation(
"ryzenAdjBottomBar.presetCreated", const presetCreatedTextVariable = variablesInTranslation(presetCreatedText, { newPresetName: newPresetName });
'Preset "{newPresetName}" created', Notification.success(presetCreatedTextVariable);
{ newPresetName: newPresetName }
);
Notification.success(presetCreatedMessage);
} }
}); });
}; };
@ -82,9 +89,7 @@ class RyzenAdjBottomBar extends React.PureComponent {
apply(ryzenControllerAppContext: RyzenControllerAppContextType) { apply(ryzenControllerAppContext: RyzenControllerAppContextType) {
return function() { return function() {
if (!isPresetValid(ryzenControllerAppContext.currentSettings)) { if (!isPresetValid(ryzenControllerAppContext.currentSettings)) {
NotificationContext.warning( NotificationContext.warning(invalidPresetText);
getTranslation("ryzenAdjBottomBar.invalidPreset", "Unable to apply invalid preset")
);
return; return;
} }
ryzenControllerAppContext.updateLatestSettings(); ryzenControllerAppContext.updateLatestSettings();

View File

@ -3,6 +3,13 @@ import { Link, Switch, Route } from "react-router-dom";
import LightModeContext from "../contexts/LightModeContext"; import LightModeContext from "../contexts/LightModeContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
const cpuTitleText = getTranslation("sceneSelector.cpuTitle", "CPU");
const gpuTitleText = getTranslation("sceneSelector.gpuTitle", "GPU");
const powerTitleText = getTranslation("sceneSelector.powerTitle", "Power");
const presetsTitleText = getTranslation("sceneSelector.presetsTitle", "Presets");
const settingsTitleText = getTranslation("sceneSelector.settingsTitle", "Settings");
const releasesTitleText = getTranslation("sceneSelector.releasesTitle", "Releases");
function Tabs(props: { tabName: string; tabLocation: string; currentLocation: string }) { function Tabs(props: { tabName: string; tabLocation: string; currentLocation: string }) {
let isActive = ""; let isActive = "";
if (props.currentLocation === props.tabLocation) { if (props.currentLocation === props.tabLocation) {
@ -30,38 +37,18 @@ function SceneSelector() {
return ( return (
<ul className="uk-tab uk-margin-remove-bottom"> <ul className="uk-tab uk-margin-remove-bottom">
<Tabs <Tabs tabName={cpuTitleText} tabLocation="/cpu" currentLocation={currentLocation} />
tabName={getTranslation("sceneSelector.cpuTitle", "CPU")} <Tabs tabName={gpuTitleText} tabLocation="/gpu" currentLocation={currentLocation} />
tabLocation="/cpu" <Tabs tabName={powerTitleText} tabLocation="/power" currentLocation={currentLocation} />
currentLocation={currentLocation} <Tabs tabName={presetsTitleText} tabLocation="/presets" currentLocation={currentLocation} />
/> <Tabs tabName={settingsTitleText} tabLocation="/settings" currentLocation={currentLocation} />
<Tabs
tabName={getTranslation("sceneSelector.gpuTitle", "GPU")}
tabLocation="/gpu"
currentLocation={currentLocation}
/>
<Tabs
tabName={getTranslation("sceneSelector.powerTitle", "Power")}
tabLocation="/power"
currentLocation={currentLocation}
/>
<Tabs
tabName={getTranslation("sceneSelector.presetsTitle", "Presets")}
tabLocation="/presets"
currentLocation={currentLocation}
/>
<Tabs
tabName={getTranslation("sceneSelector.settingsTitle", "Settings")}
tabLocation="/settings"
currentLocation={currentLocation}
/>
<li> <li>
<a <a
href="https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases" href="https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases"
onClick={openExternal("https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases")} onClick={openExternal("https://gitlab.com/ryzen-controller-team/ryzen-controller/-/releases")}
> >
<span uk-icon="link"></span> <span uk-icon="link"></span>
{getTranslation("sceneSelector.releasesTitle", "Releases")} {releasesTitleText}
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -1,6 +1,10 @@
import * as React from "react"; import * as React from "react";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
const currentPathText = getTranslation("settingForm.currentPath", "Current path:");
const browseBtnText = getTranslation("settingForm.browseBtn", "Browse");
const windowsBinFileTypeText = getTranslation("settingForm.windowsBinFileType", "Windows Binary");
type ElectronFileDialogType = { type ElectronFileDialogType = {
filePaths?: Array<string>; filePaths?: Array<string>;
}; };
@ -47,10 +51,10 @@ class SettingForm extends React.PureComponent<SettingFormProps> {
return ( return (
<React.Fragment> <React.Fragment>
<p className="uk-margin-small-bottom"> <p className="uk-margin-small-bottom">
{getTranslation("settingForm.currentPath", "Current path:")} <code>{path || "default"}</code> {currentPathText} <code>{path || "default"}</code>
</p> </p>
<button onClick={this.findFile.bind(this)} className="uk-button uk-button-default"> <button onClick={this.findFile.bind(this)} className="uk-button uk-button-default">
{getTranslation("settingForm.browseBtn", "Browse")} {browseBtnText}
</button> </button>
</React.Fragment> </React.Fragment>
); );
@ -67,7 +71,7 @@ class SettingForm extends React.PureComponent<SettingFormProps> {
? [] ? []
: [ : [
{ {
name: getTranslation("settingForm.windowsBinFileType", "Windows Binary"), name: windowsBinFileTypeText,
extensions: ["exe"], extensions: ["exe"],
}, },
]; ];

View File

@ -1,7 +1,18 @@
import * as React from "react"; import * as React from "react";
import Card from "./Card"; import Card from "./Card";
import SysInfoContext from "../contexts/SysInfoContext"; import SysInfoContext from "../contexts/SysInfoContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation, variablesInTranslation } from "../contexts/LocaleContext";
const unableToGetSysInfoText = getTranslation("sysInfoCards.unableToGetSysInfo", "Unable to get system info:");
const basicInfoTitleText = getTranslation("sysInfoCards.basicInfoTitle", "Basic Information");
const biosVersionText = getTranslation("sysInfoCards.biosVersion", "Bios version:");
const CPUInfoTitleText = getTranslation("sysInfoCards.CPUInfoTitle", "CPU Information");
const cpuPerfDescText = getTranslation(
"sysInfoCards.cpuPerfDesc",
"{speedmax}Ghz on {physicalCores} cores, {cores} threads."
);
const GPUInfoTitleText = getTranslation("sysInfoCards.GPUInfoTitle", "GPU #{index} Information");
const gpuPerfDescText = getTranslation("sysInfoCards.gpuPerfDesc", "{ram}Mb (Dynamic vram: {dyn}).");
function SysInfoCards() { function SysInfoCards() {
return ( return (
@ -10,7 +21,7 @@ function SysInfoCards() {
if (sysInfoContext.error) { if (sysInfoContext.error) {
return ( return (
<Card title="Error"> <Card title="Error">
{getTranslation("sysInfoCards.unableToGetSysInfo", "Unable to get system info:")} {unableToGetSysInfoText}
<br /> <br />
{sysInfoContext.error} {sysInfoContext.error}
</Card> </Card>
@ -24,41 +35,36 @@ function SysInfoCards() {
const bios = sysInfoContext.bios; const bios = sysInfoContext.bios;
return ( return (
<div className="uk-margin-small-right uk-margin-small-left uk-flex uk-flex-center uk-flex-around uk-flex-wrap"> <div className="uk-margin-small-right uk-margin-small-left uk-flex uk-flex-center uk-flex-around uk-flex-wrap">
<Card title={getTranslation("sysInfoCards.basicInfoTitle", "Basic Information")}> <Card title={basicInfoTitleText}>
{system === false || mem === false || bios === false ? ( {system === false || mem === false || bios === false ? (
<div uk-spinner=""></div> <div uk-spinner=""></div>
) : ( ) : (
<React.Fragment> <React.Fragment>
{system.manufacturer} {system.model} {system.version} {system.manufacturer} {system.model} {system.version}
<br /> <br />
{getTranslation("sysInfoCards.biosVersion", "Bios version:")} {bios.version}{" "} {biosVersionText} {bios.version} {bios.revision ? `rev${bios.revision}` : ""}
{bios.revision ? `rev${bios.revision}` : ""}
<br /> <br />
{(mem.total / (1024 * 1024 * 1024)).toFixed(2)}Gb Ram {(mem.total / (1024 * 1024 * 1024)).toFixed(2)}Gb Ram
</React.Fragment> </React.Fragment>
)} )}
</Card> </Card>
<Card title={getTranslation("sysInfoCards.CPUInfoTitle", "CPU Information")}> <Card title={CPUInfoTitleText}>
{cpu === false ? ( {cpu === false ? (
<div uk-spinner=""></div> <div uk-spinner=""></div>
) : ( ) : (
<React.Fragment> <React.Fragment>
{cpu.manufacturer} {cpu.brand} {cpu.manufacturer} {cpu.brand}
<br /> <br />
{getTranslation( {variablesInTranslation(cpuPerfDescText, {
"sysInfoCards.cpuPerfDesc",
"{speedmax}Ghz on {physicalCores} cores, {cores} threads.",
{
speedmax: cpu.speedmax, speedmax: cpu.speedmax,
physicalCores: cpu.physicalCores.toString(), physicalCores: cpu.physicalCores.toString(),
cores: cpu.cores.toString(), cores: cpu.cores.toString(),
} })}
)}
</React.Fragment> </React.Fragment>
)} )}
</Card> </Card>
{gpu === false ? ( {gpu === false ? (
<Card title={getTranslation("sysInfoCards.GPUInfoTitle", "GPU #{index} Information", { index: "0" })}> <Card title={variablesInTranslation(GPUInfoTitleText, { index: "0" })}>
<div uk-spinner=""></div> <div uk-spinner=""></div>
</Card> </Card>
) : ( ) : (
@ -66,13 +72,13 @@ function SysInfoCards() {
{gpu.controllers.map((controller, index) => ( {gpu.controllers.map((controller, index) => (
<Card <Card
key={`gpu-${index}`} key={`gpu-${index}`}
title={getTranslation("sysInfoCards.GPUInfoTitle", "GPU #{index} Information", { title={variablesInTranslation(GPUInfoTitleText, {
index: `${index}`, index: `${index}`,
})} })}
> >
{controller.vendor} {controller.model} {controller.vendor} {controller.model}
<br /> <br />
{getTranslation("sysInfoCards.gpuPerfDesc", "{ram}Mb (Dynamic vram: {dyn}).", { {variablesInTranslation(gpuPerfDescText, {
ram: controller.vram.toString(), ram: controller.vram.toString(),
dyn: controller.vramDynamic.toString(), dyn: controller.vramDynamic.toString(),
})} })}

View File

@ -5,6 +5,9 @@ import LightModeContext from "../contexts/LightModeContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
import AppVersion from "../contexts/AppVersion"; import AppVersion from "../contexts/AppVersion";
const beerText = getTranslation("topbar.beer", "Buy us some beers ❤️");
const discordText = getTranslation("topbar.discord", "Join us on discord");
function TopBar() { function TopBar() {
return ( return (
<header> <header>
@ -25,13 +28,13 @@ function TopBar() {
/> />
<Badge <Badge
className="uk-margin-left" className="uk-margin-left"
value={getTranslation("topbar.beer", "Buy us some beers ❤️")} value={beerText}
onClick={openExternal("https://www.patreon.com/ryzencontrollerteam")} onClick={openExternal("https://www.patreon.com/ryzencontrollerteam")}
background="#888888" background="#888888"
/> />
<Badge <Badge
className="uk-margin-left" className="uk-margin-left"
value={getTranslation("topbar.discord", "Join us on discord")} value={discordText}
onClick={openExternal("https://discord.gg/EahayUv")} onClick={openExternal("https://discord.gg/EahayUv")}
background="#7289da" background="#7289da"
/> />

View File

@ -3,4 +3,3 @@ export default {
semver: process.env?.REACT_APP_VERSION || "dev", semver: process.env?.REACT_APP_VERSION || "dev",
isDev: process.env?.REACT_APP_VERSION?.indexOf("dev") !== -1, isDev: process.env?.REACT_APP_VERSION?.indexOf("dev") !== -1,
}; };

View File

@ -9,7 +9,6 @@ const localeSettingsKey = `${AppVersion.string}.locale`;
const LocaleContext = createContext({ const LocaleContext = createContext({
is: "en", is: "en",
change: (to: AvailableLanguages): void => {}, change: (to: AvailableLanguages): void => {},
getTranslation: (id: string, fallback?: string, variables?: Record<string, string>): string => "",
}); });
LocaleContext.displayName = "LocaleContext"; LocaleContext.displayName = "LocaleContext";
@ -45,7 +44,7 @@ function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string |
if (currentLocale === "en" && fallback) { if (currentLocale === "en" && fallback) {
localeTranslation[id] = fallback; localeTranslation[id] = fallback;
} }
fs.writeFile(localeFile, JSON.stringify(localeTranslation, null, 4), function(err: string | null) { fs.writeFile(localeFile, JSON.stringify(localeTranslation, null, 2), function(err: string | null) {
electronSettings.delete("lock"); electronSettings.delete("lock");
if (err) { if (err) {
console.log("error", err); console.log("error", err);
@ -57,6 +56,22 @@ function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string |
}, 1000); }, 1000);
} }
/**
* Will replace the variables in translated sentences.
*
* @param sentence The return value of getTranslation() below.
* @param variables The variables to be replaced.
*/
function variablesInTranslation(sentence: string, variables: Record<string, string>): string {
for (const variable in variables) {
if (variables.hasOwnProperty(variable)) {
const value = variables[variable];
sentence = sentence.replace(new RegExp(`{${variable}}`, "g"), value);
}
}
return sentence;
}
/** /**
* Will return the translated message. * Will return the translated message.
* *
@ -75,7 +90,9 @@ function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string |
* @param variables Variables to replace in the sentence * @param variables Variables to replace in the sentence
*/ */
function getTranslation(id: string, fallback?: string, variables?: Record<string, string>): string { function getTranslation(id: string, fallback?: string, variables?: Record<string, string>): string {
const currentLocale = electronSettings.get(localeSettingsKey) ? (electronSettings.get(localeSettingsKey) as AvailableLanguages) : "en"; const currentLocale = electronSettings.get(localeSettingsKey)
? (electronSettings.get(localeSettingsKey) as AvailableLanguages)
: "en";
var sentence: string | undefined = LocaleTranslations[currentLocale][id]; var sentence: string | undefined = LocaleTranslations[currentLocale][id];
if (!sentence && sentence !== "") { if (!sentence && sentence !== "") {
@ -91,16 +108,11 @@ function getTranslation(id: string, fallback?: string, variables?: Record<string
} }
if (variables) { if (variables) {
for (const variable in variables) { variablesInTranslation(sentence, variables);
if (variables.hasOwnProperty(variable)) {
const value = variables[variable];
sentence = sentence.replace(new RegExp(`{${variable}}`, "g"), value);
}
}
} }
return sentence; return sentence;
} }
export { localeSettingsKey, getTranslation }; export { localeSettingsKey, getTranslation, variablesInTranslation };
export default LocaleContext; export default LocaleContext;

View File

@ -4,7 +4,7 @@ import { isNumber } from "util";
import compareVersions from "compare-versions"; import compareVersions from "compare-versions";
import NotificationContext from "./NotificationContext"; import NotificationContext from "./NotificationContext";
import { getTranslation } from "./LocaleContext"; import { getTranslation } from "./LocaleContext";
import AppVersion from './AppVersion'; import AppVersion from "./AppVersion";
const isDev = window.require("electron-is-dev"); const isDev = window.require("electron-is-dev");
const electronSettings = window.require("electron-settings"); const electronSettings = window.require("electron-settings");
@ -12,6 +12,16 @@ const reApplyPeriodicallySettingsKey = `${AppVersion.string}.reApplyPeriodically
const appContextSettingsKey = `${AppVersion.string}.appContext`; const appContextSettingsKey = `${AppVersion.string}.appContext`;
const fileSystem = window.require("fs"); const fileSystem = window.require("fs");
const invalidPresetText = getTranslation("appContext.invalidPreset", "Unable to apply invalid preset");
const newReleaseAvailableText = getTranslation(
"appContext.newReleaseAvailable",
"A new release is available, please check the release tab."
);
const ryzenAdjPathWrongPathText = getTranslation(
"appContext.ryzenAdjPath.wrongPath",
"Path to ryzenadj.exe is wrong, please fix it in settings tab."
);
const defaultPreset = { const defaultPreset = {
"--slow-time=": { enabled: false, value: getOptionDefinition("--slow-time=").default }, "--slow-time=": { enabled: false, value: getOptionDefinition("--slow-time=").default },
"--psi0-current=": { enabled: false, value: getOptionDefinition("--psi0-current=").default }, "--psi0-current=": { enabled: false, value: getOptionDefinition("--psi0-current=").default },
@ -208,12 +218,7 @@ const RyzenControllerSettingsDefinitions: RyzenControllerSettingDefinitionList =
path = getRyzenAdjExecutablePath(); path = getRyzenAdjExecutablePath();
} }
if (!fileSystem.existsSync(path)) { if (!fileSystem.existsSync(path)) {
reject( reject(ryzenAdjPathWrongPathText);
getTranslation(
"appContext.ryzenAdjPath.wrongPath",
"Path to ryzenadj.exe is wrong, please fix it in settings tab."
)
);
} }
resolve(true); resolve(true);
}); });
@ -365,7 +370,7 @@ const executeRyzenAdjUsingPreset = function(presetName: string): boolean {
return false; return false;
} }
if (!isPresetValid(presets[presetName])) { if (!isPresetValid(presets[presetName])) {
NotificationContext.warning(getTranslation("appContext.invalidPreset", "Unable to apply invalid preset")); NotificationContext.warning(invalidPresetText);
return false; return false;
} }
executeRyzenAdj(createRyzenAdjCommandLine(presets[presetName])); executeRyzenAdj(createRyzenAdjCommandLine(presets[presetName]));
@ -392,9 +397,7 @@ const checkIfNewerReleaseExist = function(): void {
}) })
.then((isNewReleaseExist: boolean) => { .then((isNewReleaseExist: boolean) => {
if (isNewReleaseExist) { if (isNewReleaseExist) {
NotificationContext.talk( NotificationContext.talk(newReleaseAvailableText);
getTranslation("appContext.newReleaseAvailable", "A new release is available, please check the release tab.")
);
} }
}) })
.catch(err => { .catch(err => {

View File

@ -106,5 +106,14 @@
"PresetOnline.retryLoadingPresetListBtn": "重试", "PresetOnline.retryLoadingPresetListBtn": "重试",
"PresetOnline.pleaseCheckInternetConnection": "请检查您的互联网连接。", "PresetOnline.pleaseCheckInternetConnection": "请检查您的互联网连接。",
"presetButtons.uploadError": "上载预设时发生错误", "presetButtons.uploadError": "上载预设时发生错误",
"presetLine.compatibility": "兼容性:" "presetLine.compatibility": "兼容性:",
"appContext.invalidPreset": "无法套用无效的预设",
"appContext.ryzenAdjPath.wrongPath": "ryzenadj.exe的路径错误请在“设置”选项卡中对其进行修复。",
"presetButtons.mustWaitForSignatureGen": "您必须等待生成笔记本电脑签名",
"presetOnlineBtn.presetLoaded": "预设 {presetName} 已加载",
"PresetsScene.errorWhileSendingVote": "发送投票时出错",
"ryzenAdjBottomBar.invalidPreset": "无法套用无效的预设",
"settingForm.windowsBinFileType": "Windows可执行文件",
"presetOnlineBtn.presetInvalidOrObsolete": "预设 {presetName} 无效或已过时",
"sysInfoCards.unableToGetSysInfo": "无法获取系统信息:"
} }

View File

@ -106,5 +106,14 @@
"PresetOnline.retryLoadingPresetListBtn": "Wiederholen", "PresetOnline.retryLoadingPresetListBtn": "Wiederholen",
"PresetOnline.pleaseCheckInternetConnection": "Bitte überprüfe deine Internetverbindung.", "PresetOnline.pleaseCheckInternetConnection": "Bitte überprüfe deine Internetverbindung.",
"presetButtons.uploadError": "Beim Hochladen der Voreinstellung ist ein Fehler aufgetreten", "presetButtons.uploadError": "Beim Hochladen der Voreinstellung ist ein Fehler aufgetreten",
"presetLine.compatibility": "Kompatibilität:" "presetLine.compatibility": "Kompatibilität:",
"appContext.invalidPreset": "Ungültig voreingestellte Anwendung nicht möglich",
"appContext.ryzenAdjPath.wrongPath": "Der Pfad zu ryzenadj.exe ist falsch. Bitte korrigieren Sie ihn auf der Registerkarte Einstellungen.",
"ryzenAdjBottomBar.invalidPreset": "Ungültig voreingestellte Anwendung nicht möglich",
"presetButtons.mustWaitForSignatureGen": "Sie müssen warten, bis die Laptop-Signatur generiert wurde",
"sysInfoCards.unableToGetSysInfo": "Systeminformationen können nicht abgerufen werden:",
"settingForm.windowsBinFileType": "Windows ausführbare Datei",
"presetOnlineBtn.presetLoaded": "Preset {presetName} wurde geladen",
"presetOnlineBtn.presetInvalidOrObsolete": "Preset \"{presetName}\" ist ungültig oder veraltet",
"PresetsScene.errorWhileSendingVote": "Fehler beim Senden der Abstimmung"
} }

View File

@ -106,5 +106,14 @@
"PresetOnline.retryLoadingPresetListBtn": "Retry", "PresetOnline.retryLoadingPresetListBtn": "Retry",
"PresetOnline.pleaseCheckInternetConnection": "Please check your internet connection.", "PresetOnline.pleaseCheckInternetConnection": "Please check your internet connection.",
"presetButtons.uploadError": "An error occured while uploading the preset", "presetButtons.uploadError": "An error occured while uploading the preset",
"presetLine.compatibility": "Compatibility:" "presetLine.compatibility": "Compatibility:",
"appContext.invalidPreset": "Unable to apply invalid preset",
"presetOnlineBtn.presetInvalidOrObsolete": "Preset \"{presetName}\" is invalid or obsolete",
"PresetsScene.errorWhileSendingVote": "Error while sending vote",
"sysInfoCards.unableToGetSysInfo": "Unable to get system info:",
"settingForm.windowsBinFileType": "Windows Binary",
"appContext.ryzenAdjPath.wrongPath": "Path to ryzenadj.exe is wrong, please fix it in settings tab.",
"ryzenAdjBottomBar.invalidPreset": "Unable to apply invalid preset",
"presetOnlineBtn.presetLoaded": "Preset \"{presetName}\" has been loaded",
"presetButtons.mustWaitForSignatureGen": "You must wait for laptop signature to be generated"
} }

View File

@ -105,5 +105,16 @@
"PresetOnline.retryLoadingPresetListBtn": "Reintentar", "PresetOnline.retryLoadingPresetListBtn": "Reintentar",
"PresetOnline.pleaseCheckInternetConnection": "Verifique su conexión a Internet", "PresetOnline.pleaseCheckInternetConnection": "Verifique su conexión a Internet",
"presetButtons.uploadError": "Se produjo un error al cargar el preset", "presetButtons.uploadError": "Se produjo un error al cargar el preset",
"presetLine.compatibility": "Compatibilidad:" "presetLine.compatibility": "Compatibilidad:",
"ryzenAdj.minFclkFrequency.desc": "Infinity Fabric es el término de marketing de AMD para la conexión de bus que conecta los troqueles del procesador (GPU / CPU). Esto define el límite mínimo de reloj del bus.",
"notification.settingsSaveSuccess": "La configuración se ha guardado correctamente",
"appContext.invalidPreset": "No se puede aplicar el preajuste no válido",
"appContext.ryzenAdjPath.wrongPath": "La ruta a ryzenadj.exe es incorrecta, corríjala en la pestaña de configuración.",
"ryzenAdjBottomBar.invalidPreset": "No se puede aplicar el preajuste no válido",
"presetOnlineBtn.presetLoaded": "Se ha cargado el preajuste {presetName}",
"presetOnlineBtn.presetInvalidOrObsolete": "El preajuste \"{presetName}\" es inválido u obsoleto",
"PresetsScene.errorWhileSendingVote": "Error al enviar voto",
"sysInfoCards.unableToGetSysInfo": "No se puede obtener información del sistema:",
"presetButtons.mustWaitForSignatureGen": "Debe esperar a que se genere la firma de la computadora portátil",
"settingForm.windowsBinFileType": "Ejecutable de Windows"
} }

View File

@ -106,5 +106,14 @@
"PresetOnline.retryLoadingPresetListBtn": "Réessayer", "PresetOnline.retryLoadingPresetListBtn": "Réessayer",
"PresetOnline.pleaseCheckInternetConnection": "Veuillez vérifier votre connexion internet.", "PresetOnline.pleaseCheckInternetConnection": "Veuillez vérifier votre connexion internet.",
"presetButtons.uploadError": "Une erreur est survenu lors de l'upload du preset", "presetButtons.uploadError": "Une erreur est survenu lors de l'upload du preset",
"presetLine.compatibility": "Compatibilité :" "presetLine.compatibility": "Compatibilité :",
"appContext.invalidPreset": "Preset corrompu, impossible de l'appliquer",
"presetOnlineBtn.presetInvalidOrObsolete": "Le preset \"{presetName}\" est invalide ou obsolète",
"PresetsScene.errorWhileSendingVote": "Erreur lors de l'envoi du vote",
"sysInfoCards.unableToGetSysInfo": "Impossible d'obtenir les informations système :",
"settingForm.windowsBinFileType": "Executable Windows",
"appContext.ryzenAdjPath.wrongPath": "Le chemin vers ryzenadj.exe est invalide, veuillez le corriger dans l'onglet paramètres.",
"ryzenAdjBottomBar.invalidPreset": "Preset corrompu, impossible de l'appliquer",
"presetOnlineBtn.presetLoaded": "Le preset \"{presetName}\" est chargé",
"presetButtons.mustWaitForSignatureGen": "Vous devez attendre que la signature de votre PC ai été généré."
} }

View File

@ -106,5 +106,14 @@
"PresetOnline.retryLoadingPresetListBtn": "Yeniden Dene", "PresetOnline.retryLoadingPresetListBtn": "Yeniden Dene",
"PresetOnline.pleaseCheckInternetConnection": "Lütfen internet bağlantınızı kontrol edin.", "PresetOnline.pleaseCheckInternetConnection": "Lütfen internet bağlantınızı kontrol edin.",
"presetButtons.uploadError": "Ön ayar yüklenirken bir hata oluştu", "presetButtons.uploadError": "Ön ayar yüklenirken bir hata oluştu",
"presetLine.compatibility": "bağdaşma:" "presetLine.compatibility": "bağdaşma:",
"appContext.invalidPreset": "Geçersiz önayar uygulanamıyor",
"appContext.ryzenAdjPath.wrongPath": "Ryzenadj.exe yolu yanlış, lütfen ayarlar sekmesinde düzeltin.",
"ryzenAdjBottomBar.invalidPreset": "Geçersiz önayar uygulanamıyor",
"presetButtons.mustWaitForSignatureGen": "Dizüstü bilgisayar imzasının oluşturulmasını beklemelisiniz",
"presetOnlineBtn.presetLoaded": "{PresetName} hazır ayarı yüklendi",
"PresetsScene.errorWhileSendingVote": "Oy gönderilirken hata oluştu",
"sysInfoCards.unableToGetSysInfo": "Sistem bilgisi alınamıyor:",
"settingForm.windowsBinFileType": "Windows yürütülebilir",
"presetOnlineBtn.presetInvalidOrObsolete": "Önceden ayarlanmış \"{presetName}\" geçersiz veya eski"
} }

View File

@ -7,11 +7,22 @@ import PresetAutoApplyCards from "../components/PresetAutoApplyCards";
import PresetOnline from "../components/PresetOnline"; import PresetOnline from "../components/PresetOnline";
import NotificationContext from "../contexts/NotificationContext"; import NotificationContext from "../contexts/NotificationContext";
import PresetsOnlineContext from "../contexts/PresetsOnline"; import PresetsOnlineContext from "../contexts/PresetsOnline";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation, variablesInTranslation } from "../contexts/LocaleContext";
import AppVersion from "../contexts/AppVersion"; import AppVersion from "../contexts/AppVersion";
const uikit = window.require("uikit"); const uikit = window.require("uikit");
const electronSettings = window.require("electron-settings"); const electronSettings = window.require("electron-settings");
const votedPresetsSettingsKey = `${AppVersion.string}.votedPresets`; const votedPresetsSettingsKey = `${AppVersion.string}.votedPresets`;
const confirmVoteText = getTranslation("PresetsScene.confirmVote", "Are you sure to {vote} this preset?");
const errorWhileSendingVoteText = getTranslation("PresetsScene.errorWhileSendingVote", "Error while sending vote");
const onlinePresetTitleText = getTranslation("PresetsScene.onlinePresetTitle", "Online Presets");
const localPresetTitleText = getTranslation("PresetsScene.localPresetTitle", "Local Presets");
const autoApplyTitleText = getTranslation("PresetsScene.autoApplyTitle", "Auto apply preset");
const updatingVotesText = getTranslation("PresetsScene.updatingVotes", "Updating votes...");
const cantVoteTwiceSamePresetText = getTranslation(
"PresetsScene.cantVoteTwiceSamePreset",
"You can't vote twice for the same preset"
);
class PresetsScene extends React.Component<{}, PresetsOnlineContextType> { class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
_isMounted = false; _isMounted = false;
@ -94,33 +105,31 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
} }
downvote(presetId: number): Promise<ApiPreset> { downvote(presetId: number): Promise<ApiPreset> {
if (this.isUserAlreadyVotedForThisPreset(presetId)) { const confirmVoteTextVariable = variablesInTranslation(confirmVoteText, {
let message = getTranslation("PresetsScene.cantVoteTwiceSamePreset", "You can't vote twice for the same preset");
NotificationContext.warning(message);
return new Promise((res, rej) => {
rej(message);
});
}
let confirmMessage = getTranslation("PresetsScene.confirmVote", "Are you sure to {vote} this preset?", {
vote: "👎", vote: "👎",
}); });
return uikit.modal.confirm(confirmMessage).then(() => { if (this.isUserAlreadyVotedForThisPreset(presetId)) {
NotificationContext.warning(cantVoteTwiceSamePresetText);
return new Promise((res, rej) => {
rej(cantVoteTwiceSamePresetText);
});
}
return uikit.modal.confirm(confirmVoteTextVariable).then(() => {
return this.vote(presetId, "down"); return this.vote(presetId, "down");
}); });
} }
upvote(presetId: number): Promise<ApiPreset> { upvote(presetId: number): Promise<ApiPreset> {
if (this.isUserAlreadyVotedForThisPreset(presetId)) { const confirmVoteTextVariable = variablesInTranslation(confirmVoteText, {
let message = getTranslation("PresetsScene.cantVoteTwiceSamePreset", "You can't vote twice for the same preset");
NotificationContext.warning(message);
return new Promise((res, rej) => {
rej(message);
});
}
let confirmMessage = getTranslation("PresetsScene.confirmVote", "Are you sure to {vote} this preset?", {
vote: "👍", vote: "👍",
}); });
return uikit.modal.confirm(confirmMessage).then(() => { if (this.isUserAlreadyVotedForThisPreset(presetId)) {
NotificationContext.warning(cantVoteTwiceSamePresetText);
return new Promise((res, rej) => {
rej(cantVoteTwiceSamePresetText);
});
}
return uikit.modal.confirm(confirmVoteTextVariable).then(() => {
return this.vote(presetId, "up"); return this.vote(presetId, "up");
}); });
} }
@ -141,7 +150,7 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
"Content-Type": "application/merge-patch+json", "Content-Type": "application/merge-patch+json",
}, },
}; };
NotificationContext.talk(getTranslation("PresetsScene.updatingVotes", "Updating votes...")); NotificationContext.talk(updatingVotesText);
return fetch(url, requestOptionGet) return fetch(url, requestOptionGet)
.then(response => response.json()) .then(response => response.json())
.then((data: ApiPreset) => { .then((data: ApiPreset) => {
@ -162,7 +171,7 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
}); });
}) })
.catch(error => { .catch(error => {
NotificationContext.error(getTranslation("PresetsScene.errorWhileSendingVote", "Error while sending vote")); NotificationContext.error(errorWhileSendingVoteText);
throw new Error(error); throw new Error(error);
}); });
} }
@ -193,9 +202,6 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
} }
render() { render() {
const onlinePresetTitle = getTranslation("PresetsScene.onlinePresetTitle", "Online Presets");
const localPresetTitle = getTranslation("PresetsScene.localPresetTitle", "Local Presets");
const autoApplyTitle = getTranslation("PresetsScene.autoApplyTitle", "Auto apply preset");
return ( return (
<PresetsOnlineContext.Provider value={this.state}> <PresetsOnlineContext.Provider value={this.state}>
<RyzenControllerAppContext.Consumer> <RyzenControllerAppContext.Consumer>
@ -204,7 +210,7 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
return ( return (
<React.Fragment> <React.Fragment>
<PresetListEmpty /> <PresetListEmpty />
<SceneTitle title={onlinePresetTitle} /> <SceneTitle title={onlinePresetTitleText} />
<PresetOnline /> <PresetOnline />
</React.Fragment> </React.Fragment>
); );
@ -213,16 +219,16 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
const presetNames = Object.keys(ryzenControllerAppContext.presets); const presetNames = Object.keys(ryzenControllerAppContext.presets);
return ( return (
<React.Fragment> <React.Fragment>
<SceneTitle title={localPresetTitle} /> <SceneTitle title={localPresetTitleText} />
<ul className="uk-margin uk-list uk-list-large uk-list-striped"> <ul className="uk-margin uk-list uk-list-large uk-list-striped">
{presetNames.map(presetName => { {presetNames.map(presetName => {
const preset = ryzenControllerAppContext.presets[presetName]; const preset = ryzenControllerAppContext.presets[presetName];
return <PresetLine key={`0_${presetName}`} presetName={presetName} preset={preset} />; return <PresetLine key={`0_${presetName}`} presetName={presetName} preset={preset} />;
})} })}
</ul> </ul>
<SceneTitle title={onlinePresetTitle} /> <SceneTitle title={onlinePresetTitleText} />
<PresetOnline /> <PresetOnline />
<SceneTitle title={autoApplyTitle} /> <SceneTitle title={autoApplyTitleText} />
<PresetAutoApplyCards /> <PresetAutoApplyCards />
</React.Fragment> </React.Fragment>
); );

View File

@ -16,6 +16,11 @@ import AppVersion from "../contexts/AppVersion";
const electronSettings = window.require("electron-settings"); const electronSettings = window.require("electron-settings");
const powerMonitor = window.require("electron").remote.powerMonitor; const powerMonitor = window.require("electron").remote.powerMonitor;
const settingsSaveSuccessText = getTranslation(
"notification.settingsSaveSuccess",
"Settings has been saved successfully"
);
class Scene extends React.Component<{}, RyzenControllerAppContextType> { class Scene extends React.Component<{}, RyzenControllerAppContextType> {
state: RyzenControllerAppContextType = { state: RyzenControllerAppContextType = {
...defaultRyzenControllerAppContext, ...defaultRyzenControllerAppContext,
@ -158,10 +163,7 @@ class Scene extends React.Component<{}, RyzenControllerAppContextType> {
if (newSettingsPromises.length > 0) { if (newSettingsPromises.length > 0) {
Promise.all(newSettingsPromises) Promise.all(newSettingsPromises)
.then(results => { .then(results => {
NotificationContext.success( NotificationContext.success(settingsSaveSuccessText, "settings_applied");
getTranslation("notification.settingsSaveSuccess", "Settings has been saved successfully"),
"settings_applied"
);
this.setState({ settings: newSettings }); this.setState({ settings: newSettings });
}) })
.catch((error: string) => { .catch((error: string) => {

View File

@ -5,30 +5,32 @@ import SettingsList from "../components/SettingsList";
import SysInfoContext from "../contexts/SysInfoContext"; import SysInfoContext from "../contexts/SysInfoContext";
import { getTranslation } from "../contexts/LocaleContext"; import { getTranslation } from "../contexts/LocaleContext";
const settingsTitleText = getTranslation("SettingsScene.settingsTitle", "Settings");
const sysInfoTitleText = getTranslation("SettingsScene.sysInfoTitle", "System Info");
const loadingSysHashText = getTranslation("SettingsScene.loadingSysHash", "Loading...");
const systemHashDescText = getTranslation(
"SettingsScene.systemHashDesc",
"This will be used to ensure downloaded presets compatibility."
);
class SettingsScene extends React.PureComponent<{}, {}> { class SettingsScene extends React.PureComponent<{}, {}> {
render() { render() {
return ( return (
<React.Fragment> <React.Fragment>
<SceneTitle title={getTranslation("SettingsScene.settingsTitle", "Settings")} /> <SceneTitle title={settingsTitleText} />
<SettingsList /> <SettingsList />
<SceneTitle <SceneTitle title={sysInfoTitleText} className="uk-margin-remove-bottom" />
title={getTranslation("SettingsScene.sysInfoTitle", "System Info")}
className="uk-margin-remove-bottom"
/>
<SysInfoCards /> <SysInfoCards />
<SysInfoContext.Consumer> <SysInfoContext.Consumer>
{sysInfoContext => ( {sysInfoContext => (
<p <p
className="uk-text-small uk-text-italic uk-margin-left uk-margin-remove-bottom" className="uk-text-small uk-text-italic uk-margin-left uk-margin-remove-bottom"
uk-tooltip={`pos: top-left; title: ${getTranslation( uk-tooltip={`pos: top-left; title: ${systemHashDescText}`}
"SettingsScene.systemHashDesc",
"This will be used to ensure downloaded presets compatibility."
)}`}
> >
System hash:{" "} System hash:{" "}
{sysInfoContext.signature && sysInfoContext.permissiveSignature {sysInfoContext.signature && sysInfoContext.permissiveSignature
? `${sysInfoContext.signature} || ${sysInfoContext.permissiveSignature}` ? `${sysInfoContext.signature} || ${sysInfoContext.permissiveSignature}`
: getTranslation("SettingsScene.loadingSysHash", "Loading...")} : loadingSysHashText}
</p> </p>
)} )}
</SysInfoContext.Consumer> </SysInfoContext.Consumer>