Skip to content

Commit

Permalink
Merge pull request #1330 from emlys/feature/1168
Browse files Browse the repository at this point in the history
Restart the app on change of language setting
  • Loading branch information
davemfish authored Jul 31, 2023
2 parents 8045806 + 9a08bdb commit ebd8385
Show file tree
Hide file tree
Showing 21 changed files with 214 additions and 129 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Unreleased Changes
* Fixed a bug where sampledata downloads failed silently (and progress bar
became innacurate) if the Workbench did not have write permission to
the download location. https://github.com/natcap/invest/issues/1070
* Changing the language setting will now cause the app to relaunch
(`#1168 <https://github.com/natcap/invest/issues/1168>`_),
* Forest Carbon
* The biophysical table is now case-insensitive.
* HRA
Expand Down
7 changes: 7 additions & 0 deletions workbench/__mocks__/electron-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default class Store {
constructor() {
this.store = {};
}
get() {}
set() {}
}
1 change: 1 addition & 0 deletions workbench/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"dependencies": {
"@babel/runtime": "^7.13.10",
"electron-log": "^4.3.5",
"electron-store": "^8.1.0",
"i18next": "^22.4.9",
"localforage": "^1.9.0",
"node-fetch": "^2.6.7",
Expand Down
1 change: 1 addition & 0 deletions workbench/src/main/ipcMainChannels.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const ipcMainChannels = {
DOWNLOAD_URL: 'download-url',
GET_N_CPUS: 'get-n-cpus',
GET_ELECTRON_PATHS: 'get-electron-paths',
GET_LANGUAGE: 'get-language',
INVEST_KILL: 'invest-kill',
INVEST_READ_LOG: 'invest-read-log',
INVEST_RUN: 'invest-run',
Expand Down
16 changes: 7 additions & 9 deletions workbench/src/main/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import path from 'path';
import {
app,
BrowserWindow,
screen,
nativeTheme,
Menu,
ipcMain
} from 'electron';

import Store from 'electron-store';

import {
createPythonFlaskProcess,
getFlaskIsReady,
Expand Down Expand Up @@ -71,6 +72,11 @@ export const createWindow = async () => {
logger.info(`Running invest-workbench version ${pkg.version}`);
nativeTheme.themeSource = 'light'; // override OS/browser setting

// read language setting from storage and switch to that language
// default to en if no language setting exists
const store = new Store();
i18n.changeLanguage(store.get('language', 'en'));

splashScreen = new BrowserWindow({
width: 574, // dims set to match the image in splash.html
height: 500,
Expand Down Expand Up @@ -110,14 +116,6 @@ export const createWindow = async () => {
menuTemplate(mainWindow, ELECTRON_DEV_MODE, i18n)
)
);
// when language changes, rebuild the menu bar in new language
i18n.on('languageChanged', (lng) => {
Menu.setApplicationMenu(
Menu.buildFromTemplate(
menuTemplate(mainWindow, ELECTRON_DEV_MODE, i18n)
)
);
});
mainWindow.loadURL(path.join(BASE_URL, 'index.html'));

mainWindow.once('ready-to-show', () => {
Expand Down
15 changes: 12 additions & 3 deletions workbench/src/main/setupChangeLanguage.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import i18n from 'i18next';
import { ipcMain } from 'electron';
import Store from 'electron-store';
import { app, ipcMain } from 'electron';
import { getLogger } from './logger';
import { ipcMainChannels } from './ipcMainChannels';

const logger = getLogger(__filename.split('/').slice(-1)[0]);

const store = new Store();

export default function setupChangeLanguage() {
ipcMain.on(ipcMainChannels.GET_LANGUAGE, (event) => {
// default to en if no language setting exists
event.returnValue = store.get('language', 'en');
});

ipcMain.handle(
ipcMainChannels.CHANGE_LANGUAGE,
(e, languageCode) => {
logger.debug('changing language to', languageCode);
i18n.changeLanguage(languageCode);
store.set('language', languageCode);
app.relaunch();
app.quit();
}
);
}
1 change: 1 addition & 0 deletions workbench/src/preload/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default {
PORT: PORT, // where the flask app is running
ELECTRON_LOG_PATH: electronLogPath,
USERGUIDE_PATH: userguidePath,
LANGUAGE: ipcRenderer.sendSync(ipcMainChannels.GET_LANGUAGE),
logger: {
debug: (message) => ipcRenderer.send(ipcMainChannels.LOGGER, 'debug', message),
info: (message) => ipcRenderer.send(ipcMainChannels.LOGGER, 'info', message),
Expand Down
17 changes: 1 addition & 16 deletions workbench/src/renderer/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ export default class App extends React.Component {
investSettings: investSettings,
showDownloadModal: this.props.isFirstRun,
});
await i18n.changeLanguage(investSettings.language);
await ipcRenderer.invoke(
ipcMainChannels.CHANGE_LANGUAGE, investSettings.language
);
await i18n.changeLanguage(window.Workbench.LANGUAGE);
ipcRenderer.on('download-status', (downloadedNofN) => {
this.setState({
downloadedNofN: downloadedNofN,
Expand All @@ -95,20 +92,8 @@ export default class App extends React.Component {
}

async saveSettings(settings) {
const { investSettings } = this.state;
await saveSettingsStore(settings);
this.setState({ investSettings: settings });
// if language has changed, refresh the app
if (settings.language !== investSettings.language) {
// change language in the renderer process
await i18n.changeLanguage(settings.language);
// change language in the main process
await ipcRenderer.invoke(
ipcMainChannels.CHANGE_LANGUAGE, settings.language
);
// rerender for changes to take effect
window.location.reload();
}
}

/** Store a sampledata filepath in localforage.
Expand Down
3 changes: 1 addition & 2 deletions workbench/src/renderer/components/InvestTab/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class InvestTab extends React.Component {
args,
investSettings.loggingLevel,
investSettings.taskgraphLoggingLevel,
investSettings.language,
window.Workbench.LANGUAGE,
tabID
);
this.switchTabs('log');
Expand Down Expand Up @@ -317,7 +317,6 @@ InvestTab.propTypes = {
nWorkers: PropTypes.string,
taskgraphLoggingLevel: PropTypes.string,
loggingLevel: PropTypes.string,
language: PropTypes.string,
}).isRequired,
saveJob: PropTypes.func.isRequired,
updateJobProperties: PropTypes.func.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion workbench/src/renderer/components/ResourcesLinks/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function ResourcesTab(props) {
}

const { t, i18n } = useTranslation();
const userGuideURL = `${window.Workbench.USERGUIDE_PATH}/${i18n.language}/${docs}`;
const userGuideURL = `${window.Workbench.USERGUIDE_PATH}/${window.Workbench.LANGUAGE}/${docs}`;

return (
<React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export function getDefaultSettings() {
taskgraphLoggingLevel: 'INFO',
loggingLevel: 'INFO',
sampleDataDir: '',
language: 'en'
};
return defaultSettings;
}
Expand Down
63 changes: 49 additions & 14 deletions workbench/src/renderer/components/SettingsModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ class SettingsModal extends React.Component {
this.state = {
show: false,
languageOptions: null,
language: window.Workbench.LANGUAGE,
showConfirmLanguageChange: false,
};
this.handleShow = this.handleShow.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleChangeLanguage = this.handleChangeLanguage.bind(this);
this.handleReset = this.handleReset.bind(this);
this.switchToDownloadModal = this.switchToDownloadModal.bind(this);
}
Expand Down Expand Up @@ -67,27 +70,36 @@ class SettingsModal extends React.Component {
this.props.saveSettings(newSettings);
}

handleChangeLanguage() {
// if language has changed, refresh the app
if (this.state.language !== window.Workbench.LANGUAGE) {
// tell the main process to update the language setting in storage
// and then relaunch the app
ipcRenderer.invoke(ipcMainChannels.CHANGE_LANGUAGE, this.state.language);
}
}

switchToDownloadModal() {
this.props.showDownloadModal();
this.handleClose();
}

render() {
const { show, languageOptions } = this.state;
const { show, languageOptions, language, showConfirmLanguageChange } = this.state;
const { investSettings, clearJobsStorage, nCPU, t } = this.props;

const nWorkersOptions = [
[-1, `${t('Synchronous')} (-1)`],
[0, `${t('Threaded task management')} (0)`]
[0, `${t('Threaded task management')} (0)`],
];
for (let i = 1; i <= nCPU; i += 1) {
nWorkersOptions.push([i, `${i} ${t('CPUs')}`]);
}
const logLevelOptions = { // map value to display name
'DEBUG': t('DEBUG'),
'INFO': t('INFO'),
'WARNING': t('WARNING'),
'ERROR': t('ERROR')
const logLevelOptions = { // map value to display name
DEBUG: t('DEBUG'),
INFO: t('INFO'),
WARNING: t('WARNING'),
ERROR: t('ERROR'),
};
return (
<React.Fragment>
Expand Down Expand Up @@ -124,18 +136,18 @@ class SettingsModal extends React.Component {
<Form.Label column sm="8" htmlFor="language-select">
<MdTranslate className="language-icon" />
{t('Language')}
<Form.Text className="text-nowrap" muted>
<MdWarningAmber className="align-text-bottom ml-3" />
{t('Changing this setting will refresh the app and close all tabs')}
</Form.Text>
</Form.Label>
<Col sm="4">
<Form.Control
id="language-select"
as="select"
name="language"
value={investSettings.language}
onChange={this.handleChange}
value={window.Workbench.LANGUAGE}
onChange={
(event) => this.setState({
showConfirmLanguageChange: true,
language: event.target.value
})}
>
{Object.entries(languageOptions).map((entry) => {
const [value, displayName] = entry;
Expand Down Expand Up @@ -264,6 +276,30 @@ class SettingsModal extends React.Component {
<span>{t('no invest workspaces will be deleted')}</span>
</Modal.Body>
</Modal>
{
(languageOptions) ? (
<Modal show={showConfirmLanguageChange} className="confirm-modal" >
<Modal.Header>
<Modal.Title as="h5" >{t('Warning')}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
{t('Changing this setting will close your tabs and relaunch the app.')}
</p>
</Modal.Body>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => this.setState({ showConfirmLanguageChange: false })}
>{t('Cancel')}</Button>
<Button
variant="primary"
onClick={this.handleChangeLanguage}
>{t('Change to ') + languageOptions[language]}</Button>
</Modal.Footer>
</Modal>
) : <React.Fragment />
}
</React.Fragment>
);
}
Expand All @@ -277,7 +313,6 @@ SettingsModal.propTypes = {
taskgraphLoggingLevel: PropTypes.string,
loggingLevel: PropTypes.string,
sampleDataDir: PropTypes.string,
language: PropTypes.string,
}).isRequired,
showDownloadModal: PropTypes.func.isRequired,
nCPU: PropTypes.number.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ function AboutModal(props) {
// create link to users guide entry for this arg
// anchor name is the arg name, with underscores replaced with hyphens
const userguideURL = `
${window.Workbench.USERGUIDE_PATH}/${i18n.language}/${userguide}#${argkey.replace(/_/g, '-')}`;
${window.Workbench.USERGUIDE_PATH}/${window.Workbench.LANGUAGE}/${userguide}#${argkey.replace(/_/g, '-')}`;
return (
<React.Fragment>
<Button
Expand Down
4 changes: 1 addition & 3 deletions workbench/src/renderer/menubar/about.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Translation } from 'react-i18next';

import i18n from '../i18n/i18n';
import { handleClickExternalURL } from './handlers';
import { getSettingsValue } from '../components/SettingsModal/SettingsStorage';
import { ipcMainChannels } from '../../main/ipcMainChannels';
import investLogo from '../static/invest-logo.png';

Expand All @@ -15,8 +14,7 @@ async function getInvestVersion() {
return investVersion;
}

const language = await getSettingsValue('language');
await i18n.changeLanguage(language);
await i18n.changeLanguage(window.Workbench.LANGUAGE);
const investVersion = await getInvestVersion();
ReactDom.render(
<Translation>
Expand Down
4 changes: 1 addition & 3 deletions workbench/src/renderer/menubar/report.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import {
handleClickExternalURL,
handleClickFindLogfiles
} from './handlers';
import { getSettingsValue } from '../components/SettingsModal/SettingsStorage';
import investLogo from '../static/invest-logo.png';
import natcapLogo from '../static/NatCapLogo.jpg';

const language = await getSettingsValue('language');
await i18n.changeLanguage(language);
await i18n.changeLanguage(window.Workbench.LANGUAGE);
ReactDom.render(
<Translation>
{(t, { i18n }) => (
Expand Down
Loading

0 comments on commit ebd8385

Please sign in to comment.