[MM-14058] Add support for i18n (#2190)
* Add language files * Add react-intl, mmjstool, setup for adding translations * Translated main module * Translations for renderer * A few minor fixes * More fixes * Add CI, add missing menu translations, other cleanup * Added setting to manually select the language of the app * Force English for E2e * Unit tests * Fix mmjstool * Move set language to before update menu * PR feedback
This commit is contained in:
@@ -8,6 +8,7 @@ import 'renderer/css/settings.css';
|
||||
|
||||
import React from 'react';
|
||||
import {FormCheck, Col, FormGroup, FormText, Container, Row, Button, FormControl} from 'react-bootstrap';
|
||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl';
|
||||
import ReactSelect, {ActionMeta, MultiValue} from 'react-select';
|
||||
|
||||
import {CombinedConfig, LocalConfiguration} from 'types/config';
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
RELOAD_CONFIGURATION,
|
||||
GET_AVAILABLE_SPELL_CHECKER_LANGUAGES,
|
||||
CHECK_FOR_UPDATES,
|
||||
GET_AVAILABLE_LANGUAGES,
|
||||
} from 'common/communication';
|
||||
|
||||
import AutoSaveIndicator, {SavingState} from './AutoSaveIndicator';
|
||||
@@ -32,6 +34,10 @@ const CONFIG_TYPE_APP_OPTIONS = 'appOptions';
|
||||
|
||||
type ConfigType = typeof CONFIG_TYPE_UPDATES | typeof CONFIG_TYPE_APP_OPTIONS;
|
||||
|
||||
type Props = {
|
||||
intl: IntlShape;
|
||||
}
|
||||
|
||||
type State = DeepPartial<CombinedConfig> & {
|
||||
ready: boolean;
|
||||
maximized?: boolean;
|
||||
@@ -39,13 +45,14 @@ type State = DeepPartial<CombinedConfig> & {
|
||||
userOpenedDownloadDialog: boolean;
|
||||
allowSaveSpellCheckerURL: boolean;
|
||||
availableLanguages: Array<{label: string; value: string}>;
|
||||
availableSpellcheckerLanguages: Array<{label: string; value: string}>;
|
||||
canUpgrade?: boolean;
|
||||
}
|
||||
|
||||
type SavingStateItems = {
|
||||
appOptions: SavingState;
|
||||
updates: SavingState;
|
||||
};
|
||||
}
|
||||
|
||||
type SaveQueueItem = {
|
||||
configType: ConfigType;
|
||||
@@ -53,7 +60,7 @@ type SaveQueueItem = {
|
||||
data: CombinedConfig[keyof CombinedConfig];
|
||||
}
|
||||
|
||||
export default class SettingsPage extends React.PureComponent<Record<string, never>, State> {
|
||||
class SettingsPage extends React.PureComponent<Props, State> {
|
||||
trayIconThemeRef: React.RefObject<HTMLDivElement>;
|
||||
downloadLocationRef: React.RefObject<HTMLInputElement>;
|
||||
showTrayIconRef: React.RefObject<HTMLInputElement>;
|
||||
@@ -69,6 +76,7 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
startInFullscreenRef: React.RefObject<HTMLInputElement>;
|
||||
autoCheckForUpdatesRef: React.RefObject<HTMLInputElement>;
|
||||
logLevelRef: React.RefObject<HTMLSelectElement>;
|
||||
appLanguageRef: React.RefObject<HTMLSelectElement>;
|
||||
|
||||
saveQueue: SaveQueueItem[];
|
||||
|
||||
@@ -77,7 +85,7 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
savingIsDebounced: boolean;
|
||||
resetSaveStateIsDebounced: boolean;
|
||||
|
||||
constructor(props: Record<string, never>) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
ready: false,
|
||||
@@ -88,6 +96,7 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
userOpenedDownloadDialog: false,
|
||||
allowSaveSpellCheckerURL: false,
|
||||
availableLanguages: [],
|
||||
availableSpellcheckerLanguages: [],
|
||||
};
|
||||
|
||||
this.getConfig();
|
||||
@@ -106,6 +115,7 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
this.spellCheckerURLRef = React.createRef();
|
||||
this.autoCheckForUpdatesRef = React.createRef();
|
||||
this.logLevelRef = React.createRef();
|
||||
this.appLanguageRef = React.createRef();
|
||||
|
||||
this.saveQueue = [];
|
||||
this.selectedSpellCheckerLocales = [];
|
||||
@@ -121,6 +131,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
});
|
||||
|
||||
window.ipcRenderer.invoke(GET_AVAILABLE_SPELL_CHECKER_LANGUAGES).then((languages: string[]) => {
|
||||
const availableSpellcheckerLanguages = languages.filter((language) => localeTranslations[language]).map((language) => ({label: localeTranslations[language], value: language}));
|
||||
availableSpellcheckerLanguages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
this.setState({availableSpellcheckerLanguages});
|
||||
});
|
||||
|
||||
window.ipcRenderer.invoke(GET_AVAILABLE_LANGUAGES).then((languages: string[]) => {
|
||||
const availableLanguages = languages.filter((language) => localeTranslations[language]).map((language) => ({label: localeTranslations[language], value: language}));
|
||||
availableLanguages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
this.setState({availableLanguages});
|
||||
@@ -318,6 +334,13 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeAppLanguage = () => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'appLanguage', data: this.appLanguageRef.current?.value});
|
||||
this.setState({
|
||||
appLanguage: this.appLanguageRef.current?.value,
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeAutoCheckForUpdates = () => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_UPDATES, {key: 'autoCheckForUpdates', data: this.autoCheckForUpdatesRef.current?.checked});
|
||||
this.setState({
|
||||
@@ -415,6 +438,8 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
}
|
||||
|
||||
render() {
|
||||
const {intl} = this.props;
|
||||
|
||||
const settingsPage = {
|
||||
close: {
|
||||
textDecoration: 'none',
|
||||
@@ -471,6 +496,17 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
fontWeight: 500,
|
||||
},
|
||||
|
||||
appLanguageInput: {
|
||||
marginRight: '3px',
|
||||
marginTop: '8px',
|
||||
width: '320px',
|
||||
height: '34px',
|
||||
padding: '0 12px',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #ccc',
|
||||
fontWeight: 500,
|
||||
},
|
||||
|
||||
container: {
|
||||
paddingBottom: '40px',
|
||||
},
|
||||
@@ -496,9 +532,15 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.autostart}
|
||||
onChange={this.handleChangeAutoStart}
|
||||
/>
|
||||
{'Start app on login'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.startAppOnLogin'
|
||||
defaultMessage='Start app on login'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, the app starts automatically when you log in to your machine.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.startAppOnLogin.description'
|
||||
defaultMessage='If enabled, the app starts automatically when you log in to your machine.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
|
||||
@@ -512,9 +554,15 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.hideOnStart}
|
||||
onChange={this.handleChangeHideOnStart}
|
||||
/>
|
||||
{'Launch app minimized'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.launchAppMinimized'
|
||||
defaultMessage='Launch app minimized'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, the app will start in system tray, and will not show the window on launch.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.launchAppMinimized.description'
|
||||
defaultMessage='If enabled, the app will start in system tray, and will not show the window on launch.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
@@ -530,10 +578,20 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.useSpellChecker}
|
||||
onChange={this.handleChangeUseSpellChecker}
|
||||
/>
|
||||
{'Check spelling'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.checkSpelling'
|
||||
defaultMessage='Check spelling'
|
||||
/>
|
||||
<FormText>
|
||||
{'Highlight misspelled words in your messages based on your system language or language preference. '}
|
||||
{'Setting takes effect after restarting the app.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.checkSpelling.description'
|
||||
defaultMessage='Highlight misspelled words in your messages based on your system language or language preference.'
|
||||
/>
|
||||
{' '}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.afterRestart'
|
||||
defaultMessage='Setting takes effect after restarting the app.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>
|
||||
{this.state.useSpellChecker &&
|
||||
@@ -541,12 +599,17 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
inputId='inputSpellCheckerLocalesDropdown'
|
||||
className='SettingsPage__spellCheckerLocalesDropdown'
|
||||
classNamePrefix='SettingsPage__spellCheckerLocalesDropdown'
|
||||
options={this.state.availableLanguages}
|
||||
options={this.state.availableSpellcheckerLanguages}
|
||||
isMulti={true}
|
||||
isClearable={false}
|
||||
onChange={this.handleChangeSpellCheckerLocales}
|
||||
value={this.selectedSpellCheckerLocales}
|
||||
placeholder={'Select preferred language(s)'}
|
||||
placeholder={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.checkSpelling.preferredLanguages'
|
||||
defaultMessage='Select preferred language(s)'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
</>,
|
||||
@@ -559,7 +622,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
key='editSpellcheckerURL'
|
||||
onClick={() => this.setState({spellCheckerURL: '', allowSaveSpellCheckerURL: false})}
|
||||
variant='link'
|
||||
>{'Use an alternative dictionary URL'}</Button>,
|
||||
>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.checkSpelling.editSpellcheckUrl'
|
||||
defaultMessage='Use an alternative dictionary URL'
|
||||
/>
|
||||
</Button>,
|
||||
);
|
||||
} else {
|
||||
options.push(
|
||||
@@ -583,22 +651,33 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
id='saveSpellCheckerURL'
|
||||
onClick={this.saveSpellCheckerURL}
|
||||
>
|
||||
<span>{'Save'}</span>
|
||||
<FormattedMessage
|
||||
id='label.save'
|
||||
defaultMessage='Save'
|
||||
/>
|
||||
</Button>
|
||||
<FormText>
|
||||
{'Specify the url where dictionary definitions can be retrieved'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.checkSpelling.specifyURL'
|
||||
defaultMessage='Specify the url where dictionary definitions can be retrieved'
|
||||
/>
|
||||
</FormText>
|
||||
<Button
|
||||
id='revertSpellcheckerURL'
|
||||
key='revertSpellcheckerURL'
|
||||
onClick={this.resetSpellCheckerURL}
|
||||
variant='link'
|
||||
>{'Revert to default'}</Button>
|
||||
>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.checkSpelling.revertToDefault'
|
||||
defaultMessage='Revert to default'
|
||||
/>
|
||||
</Button>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
if (window.process.platform === 'darwin' || window.process.platform === 'win32') {
|
||||
const TASKBAR = window.process.platform === 'win32' ? 'taskbar' : 'Dock';
|
||||
const taskbar = window.process.platform === 'win32' ? 'taskbar' : 'Dock';
|
||||
options.push(
|
||||
<FormCheck
|
||||
key='showunreadbadge'
|
||||
@@ -611,9 +690,17 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.showUnreadBadge}
|
||||
onChange={this.handleShowUnreadBadge}
|
||||
/>
|
||||
{`Show red badge on ${TASKBAR} icon to indicate unread messages`}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.showUnreadBadge'
|
||||
defaultMessage='Show red badge on {taskbar} icon to indicate unread messages'
|
||||
values={{taskbar}}
|
||||
/>
|
||||
<FormText>
|
||||
{`Regardless of this setting, mentions are always indicated with a red badge and item count on the ${TASKBAR} icon.`}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.showUnreadBadge.description'
|
||||
defaultMessage='Regardless of this setting, mentions are always indicated with a red badge and item count on the {taskbar} icon.'
|
||||
values={{taskbar}}
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
@@ -629,13 +716,30 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={!this.state.notifications || this.state.notifications.flashWindow === 2}
|
||||
onChange={this.handleFlashWindow}
|
||||
/>
|
||||
{'Flash taskbar icon when a new message is received'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.flashWindow'
|
||||
defaultMessage='Flash taskbar icon when a new message is received'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, the taskbar icon will flash for a few seconds when a new message is received.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.flashWindow.description'
|
||||
defaultMessage='If enabled, the taskbar icon will flash for a few seconds when a new message is received.'
|
||||
/>
|
||||
{window.process.platform === 'linux' && (
|
||||
<>
|
||||
<br/>
|
||||
<em><strong>{'NOTE: '}</strong>{'This functionality may not work with all Linux window managers.'}</em>
|
||||
<em>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.flashWindow.description.note'
|
||||
defaultMessage='NOTE: '
|
||||
/>
|
||||
</strong>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.flashWindow.description.linuxFunctionality'
|
||||
defaultMessage='This functionality may not work with all Linux window managers.'
|
||||
/>
|
||||
</em>
|
||||
</>
|
||||
)}
|
||||
</FormText>
|
||||
@@ -656,7 +760,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.notifications ? this.state.notifications.bounceIcon : false}
|
||||
onChange={this.handleBounceIcon}
|
||||
style={{marginRight: '10px'}}
|
||||
label='Bounce the Dock icon'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.bounceIcon'
|
||||
defaultMessage='Bounce the Dock icon'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormCheck
|
||||
type='radio'
|
||||
@@ -670,7 +779,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
this.state.notifications.bounceIconType === 'informational'
|
||||
}
|
||||
onChange={this.handleBounceIconType}
|
||||
label='once'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.bounceIcon.once'
|
||||
defaultMessage='once'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{' '}
|
||||
<FormCheck
|
||||
@@ -681,12 +795,20 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
disabled={!this.state.notifications || !this.state.notifications.bounceIcon}
|
||||
defaultChecked={this.state.notifications && this.state.notifications.bounceIconType === 'critical'}
|
||||
onChange={this.handleBounceIconType}
|
||||
label={'until I open the app'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.bounceIcon.untilOpenApp'
|
||||
defaultMessage='until I open the app'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormText
|
||||
style={{marginLeft: '20px'}}
|
||||
>
|
||||
{'If enabled, the Dock icon bounces once or until the user opens the app when a new notification is received.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.bounceIcon.description'
|
||||
defaultMessage='If enabled, the Dock icon bounces once or until the user opens the app when a new notification is received.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormGroup>,
|
||||
);
|
||||
@@ -704,9 +826,22 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.showTrayIcon}
|
||||
onChange={this.handleChangeShowTrayIcon}
|
||||
/>
|
||||
{window.process.platform === 'darwin' ? `Show ${this.state.appName} icon in the menu bar` : 'Show icon in the notification area'}
|
||||
{window.process.platform === 'darwin' ?
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.trayIcon.show.darwin'
|
||||
defaultMessage='Show {appName} icon in the menu bar'
|
||||
values={{appName: this.state.appName}}
|
||||
/> :
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.trayIcon.show'
|
||||
defaultMessage='Show icon in the notification area'
|
||||
/>
|
||||
}
|
||||
<FormText>
|
||||
{'Setting takes effect after restarting the app.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.afterRestart'
|
||||
defaultMessage='Setting takes effect after restarting the app.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
@@ -719,7 +854,10 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
ref={this.trayIconThemeRef}
|
||||
style={{marginLeft: '20px'}}
|
||||
>
|
||||
{'Icon theme: '}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.trayIcon.theme'
|
||||
defaultMessage='Icon theme: '
|
||||
/>
|
||||
{window.process.platform === 'win32' &&
|
||||
<>
|
||||
<FormCheck
|
||||
@@ -729,7 +867,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
value='use_system'
|
||||
defaultChecked={this.state.trayIconTheme === 'use_system' || !this.state.trayIconTheme}
|
||||
onChange={() => this.handleChangeTrayIconTheme('use_system')}
|
||||
label={'Use system default'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.trayIcon.theme.systemDefault'
|
||||
defaultMessage='Use system default'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{' '}
|
||||
</>
|
||||
@@ -741,7 +884,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
value='light'
|
||||
defaultChecked={this.state.trayIconTheme === 'light' || !this.state.trayIconTheme}
|
||||
onChange={() => this.handleChangeTrayIconTheme('light')}
|
||||
label={'Light'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.trayIcon.theme.light'
|
||||
defaultMessage='Light'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{' '}
|
||||
<FormCheck
|
||||
@@ -751,7 +899,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
value='dark'
|
||||
defaultChecked={this.state.trayIconTheme === 'dark'}
|
||||
onChange={() => this.handleChangeTrayIconTheme('dark')}
|
||||
label={'Dark'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.trayIcon.theme.dark'
|
||||
defaultMessage='Dark'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</FormGroup>,
|
||||
);
|
||||
@@ -771,10 +924,24 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.minimizeToTray}
|
||||
onChange={this.handleChangeMinimizeToTray}
|
||||
/>
|
||||
{'Leave app running in notification area when application window is closed'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.minimizeToTray'
|
||||
defaultMessage='Leave app running in notification area when application window is closed'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, the app stays running in the notification area after app window is closed.'}
|
||||
{this.state.showTrayIcon ? ' Setting takes effect after restarting the app.' : ''}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.minimizeToTray.description'
|
||||
defaultMessage='If enabled, the app stays running in the notification area after app window is closed.'
|
||||
/>
|
||||
{this.state.showTrayIcon &&
|
||||
<>
|
||||
{' '}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.afterRestart'
|
||||
defaultMessage='Setting takes effect after restarting the app.'
|
||||
/>
|
||||
</>
|
||||
}
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
@@ -790,10 +957,20 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.enableHardwareAcceleration}
|
||||
onChange={this.handleChangeEnableHardwareAcceleration}
|
||||
/>
|
||||
{'Use GPU hardware acceleration'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.enableHardwareAcceleration'
|
||||
defaultMessage='Use GPU hardware acceleration'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, Mattermost UI is rendered more efficiently but can lead to decreased stability for some systems.'}
|
||||
{' Setting takes effect after restarting the app.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.enableHardwareAcceleration.description'
|
||||
defaultMessage='If enabled, Mattermost UI is rendered more efficiently but can lead to decreased stability for some systems.'
|
||||
/>
|
||||
{' '}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.afterRestart'
|
||||
defaultMessage='Setting takes effect after restarting the app.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>,
|
||||
);
|
||||
@@ -809,9 +986,15 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.startInFullscreen}
|
||||
onChange={this.handleChangeStartInFullscreen}
|
||||
/>
|
||||
{'Open app in fullscreen'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.fullscreen'
|
||||
defaultMessage='Open app in fullscreen'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, the Mattermost application will always open in full screen'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.fullscreen.description'
|
||||
defaultMessage='If enabled, the Mattermost application will always open in full screen'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>,
|
||||
);
|
||||
@@ -822,7 +1005,50 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
key='containerDownloadLocation'
|
||||
>
|
||||
<hr/>
|
||||
<div>{'Download Location'}</div>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.appLanguage'
|
||||
defaultMessage='Set app language (beta)'
|
||||
/>
|
||||
<FormControl
|
||||
style={settingsPage.appLanguageInput}
|
||||
as='select'
|
||||
id='inputAppLanguage'
|
||||
ref={this.appLanguageRef}
|
||||
value={this.state.appLanguage}
|
||||
onChange={this.handleChangeAppLanguage}
|
||||
>
|
||||
<option value=''>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.appLanguage.useSystemDefault', defaultMessage: 'Use system default'})}
|
||||
</option>
|
||||
{this.state.availableLanguages.map((language) => {
|
||||
return (
|
||||
<option
|
||||
key={language.value}
|
||||
value={language.value}
|
||||
>
|
||||
{language.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</FormControl>
|
||||
<FormText>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.appLanguage.description'
|
||||
defaultMessage='Chooses the language that the Desktop App will use for menu items and popups. Still in beta, some languages will be missing translation strings.'
|
||||
/>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.afterRestart'
|
||||
defaultMessage='Setting takes effect after restarting the app.'
|
||||
/>
|
||||
</FormText>
|
||||
<br/>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.downloadLocation'
|
||||
defaultMessage='Download Location'
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
disabled={true}
|
||||
style={settingsPage.downloadLocationInput}
|
||||
@@ -837,13 +1063,22 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
id='saveDownloadLocation'
|
||||
onClick={this.selectDownloadLocation}
|
||||
>
|
||||
<span>{'Change'}</span>
|
||||
<FormattedMessage
|
||||
id='label.change'
|
||||
defaultMessage='Change'
|
||||
/>
|
||||
</Button>
|
||||
<FormText>
|
||||
{'Specify the folder where files will download.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.downloadLocation.description'
|
||||
defaultMessage='Specify the folder where files will download.'
|
||||
/>
|
||||
</FormText>
|
||||
<br/>
|
||||
{'Logging level'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.loggingLevel'
|
||||
defaultMessage='Logging level'
|
||||
/>
|
||||
<FormControl
|
||||
style={settingsPage.logLevelInput}
|
||||
as='select'
|
||||
@@ -852,16 +1087,35 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
value={this.state.logLevel}
|
||||
onChange={this.handleChangeLogLevel}
|
||||
>
|
||||
<option value='error'>{'Errors (error)'}</option>
|
||||
<option value='warn'>{'Errors and Warnings (warn)'}</option>
|
||||
<option value='info'>{'Info (info)'}</option>
|
||||
<option value='verbose'>{'Verbose (verbose)'}</option>
|
||||
<option value='debug'>{'Debug (debug)'}</option>
|
||||
<option value='silly'>{'Finest (silly)'}</option>
|
||||
<option value='error'>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.loggingLevel.level.error', defaultMessage: 'Errors (error)'})}
|
||||
</option>
|
||||
<option value='warn'>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.loggingLevel.level.warn', defaultMessage: 'Errors and Warnings (warn)'})}
|
||||
</option>
|
||||
<option value='info'>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.loggingLevel.level.info', defaultMessage: 'Info (info)'})}
|
||||
</option>
|
||||
<option value='verbose'>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.loggingLevel.level.verbose', defaultMessage: 'Verbose (verbose)'})}
|
||||
</option>
|
||||
<option value='debug'>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.loggingLevel.level.debug', defaultMessage: 'Debug (debug)'})}
|
||||
</option>
|
||||
<option value='silly'>
|
||||
{intl.formatMessage({id: 'renderer.components.settingsPage.loggingLevel.level.silly', defaultMessage: 'Finest (silly)'})}
|
||||
</option>
|
||||
</FormControl>
|
||||
<FormText>
|
||||
{'Logging is helpful for developers and support to isolate issues you may be encountering with the desktop app.'}
|
||||
<br/>{'Increasing the log level increases disk space usage and can impact performance. We recommend only increasing the log level if you are having issues.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.loggingLevel.description'
|
||||
defaultMessage='Logging is helpful for developers and support to isolate issues you may be encountering with the desktop app.'
|
||||
/>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.loggingLevel.description.subtitle'
|
||||
defaultMessage='Increasing the log level increases disk space usage and can impact performance. We recommend only increasing the log level if you are having issues.'
|
||||
/>
|
||||
</FormText>
|
||||
</div>,
|
||||
);
|
||||
@@ -871,12 +1125,22 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
optionsRow = (
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
<h2 style={settingsPage.sectionHeading}>{'App Options'}</h2>
|
||||
<h2 style={settingsPage.sectionHeading}>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.appOptions'
|
||||
defaultMessage='App Options'
|
||||
/>
|
||||
</h2>
|
||||
<div className='IndicatorContainer appOptionsSaveIndicator'>
|
||||
<AutoSaveIndicator
|
||||
id='appOptionsSaveIndicator'
|
||||
savingState={this.state.savingState.appOptions}
|
||||
errorMessage={'Can\'t save your changes. Please try again.'}
|
||||
errorMessage={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.saving.error'
|
||||
defaultMessage={'Can\'t save your changes. Please try again.'}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{ options.map((opt) => (
|
||||
@@ -895,12 +1159,22 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
<>
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
<h2 style={settingsPage.sectionHeading}>{'Updates'}</h2>
|
||||
<h2 style={settingsPage.sectionHeading}>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.updates'
|
||||
defaultMessage='Updates'
|
||||
/>
|
||||
</h2>
|
||||
<div className='IndicatorContainer updatesSaveIndicator'>
|
||||
<AutoSaveIndicator
|
||||
id='updatesSaveIndicator'
|
||||
savingState={this.state.savingState.updates}
|
||||
errorMessage={'Can\'t save your changes. Please try again.'}
|
||||
errorMessage={
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.saving.error'
|
||||
defaultMessage={'Can\'t save your changes. Please try again.'}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<FormGroup
|
||||
@@ -915,9 +1189,15 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.autoCheckForUpdates}
|
||||
onChange={this.handleChangeAutoCheckForUpdates}
|
||||
/>
|
||||
{'Automatically check for updates'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.updates.automatic'
|
||||
defaultMessage='Automatically check for updates'
|
||||
/>
|
||||
<FormText>
|
||||
{'If enabled, updates to the Desktop App will download automatically and you will be notified when ready to install.'}
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.updates.automatic.description'
|
||||
defaultMessage='If enabled, updates to the Desktop App will download automatically and you will be notified when ready to install.'
|
||||
/>
|
||||
</FormText>
|
||||
</FormCheck>
|
||||
<Button
|
||||
@@ -925,7 +1205,10 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
id='checkForUpdatesNow'
|
||||
onClick={this.checkForUpdates}
|
||||
>
|
||||
<span>{'Check for Updates Now'}</span>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.updates.checkNow'
|
||||
defaultMessage='Check for Updates Now'
|
||||
/>
|
||||
</Button>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
@@ -944,7 +1227,14 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
waitForIpc = (<p>{'Loading configuration...'}</p>);
|
||||
waitForIpc = (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.loadingConfig'
|
||||
defaultMessage='Loading configuration...'
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -962,7 +1252,12 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
}}
|
||||
>
|
||||
<div style={{position: 'relative'}}>
|
||||
<h1 style={settingsPage.heading}>{'Settings'}</h1>
|
||||
<h1 style={settingsPage.heading}>
|
||||
<FormattedMessage
|
||||
id='renderer.components.settingsPage.header'
|
||||
defaultMessage='Settings'
|
||||
/>
|
||||
</h1>
|
||||
<hr/>
|
||||
</div>
|
||||
<Container
|
||||
@@ -975,3 +1270,5 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(SettingsPage);
|
||||
|
Reference in New Issue
Block a user