Skip to content

Commit

Permalink
Merge pull request #122 from vnma0/l10n
Browse files Browse the repository at this point in the history
Supporting people who don't even know an international language like English
  • Loading branch information
minhducsun2002 committed Apr 17, 2019
2 parents 3ca66ae + ae83941 commit a12424d
Show file tree
Hide file tree
Showing 39 changed files with 842 additions and 127 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
"@material-ui/core": "^3.7.0",
"@material-ui/icons": "^3.0.1",
"dayjs": "^1.8.8",
"js-cookie": "^2.2.0",
"react": "^16.6.3",
"react-ace": "^6.4.0",
"react-dom": "^16.6.3",
"react-l10n": "^0.3.0",
"react-router-dom": "^4.3.1",
"react-scripts": "^2.1.5",
"typeface-roboto": "0.0.54"
"react-scripts": "^2.1.5"
},
"scripts": {
"start": "react-scripts start",
Expand Down
15 changes: 12 additions & 3 deletions src/app/globalStatusBar/clock/clock.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import AccessTime from '@material-ui/icons/AccessTime'
import timeAgo from '../../../external/timeAgo.js';
import './clock.css'

import LocalizedMessage from 'react-l10n';

/**
* @name CountdownClock
* @description A simple clock that shows time left and the duration of the contest
Expand Down Expand Up @@ -57,9 +59,16 @@ class CountdownClock extends Component {
<Button disabled id="clock" className={this.state.clockStyleClass}>
<AccessTime style={{ marginRight: '10px' }} />
{this.state.ended
? 'ENDED' : (this.state.started
? `TIME LEFT : ${this.state.current} / ${this.state.duration}`
: `${timeAgo(new Date(), this.props.time.start)} BEFORE START`)}
? <LocalizedMessage id="globalStatusBar.clock.ended"/>
: (this.state.started
? <>
<LocalizedMessage id="globalStatusBar.clock.timeLeft"/>
{` : ${this.state.current} / ${this.state.duration}`}
</>
: <>
{`${timeAgo(new Date(), this.props.time.start)} `}
<LocalizedMessage id="globalStatusBar.clock.beforeStart"/>
</>)}
</Button>
</span>
</Tooltip>
Expand Down
4 changes: 3 additions & 1 deletion src/app/globalStatusBar/globalStatusBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { Component } from 'react'
import { Toolbar, AppBar, IconButton } from '@material-ui/core'

import Menu from '@material-ui/icons/Menu'
import LocalizedMessage from 'react-l10n';

import ContestSignature from './contestSignature/contestSignature.js'
import CountdownClock from './clock/clock.js'
import LoginButton from './login/loginButton.js'
Expand Down Expand Up @@ -135,7 +137,7 @@ class GlobalStatusBar extends Component {
})
}
>
{'Log in here'}
<LocalizedMessage id="globalStatusBar.login.invokingButton"/>
</LoginButton>
) : (
<UserSettingButton
Expand Down
23 changes: 14 additions & 9 deletions src/app/globalStatusBar/login/loginDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { fade } from '../lib/libTransition.js'
import login from './stub/login.js'
import { pushNotification } from '../../notifier/notify.js'

import LocalizedMessage from 'react-l10n';

/**
* @name LoginDialog
* @description The login dialog.
Expand Down Expand Up @@ -92,11 +94,13 @@ class LoginDialog extends Component {
: fade
}
>
<DialogTitle>Log in</DialogTitle>
<DialogTitle>
<LocalizedMessage id="globalStatusBar.login.dialog.title"/>
</DialogTitle>

<DialogContent>
<DialogContentText>
Log in to submit solutions and view problems.
<LocalizedMessage id="globalStatusBar.login.dialog.greeting"/>
{/* TODO : configurable language */}
</DialogContentText>

Expand All @@ -107,7 +111,7 @@ class LoginDialog extends Component {
<Grid item>
<TextField
autoFocus={true}
label="ID"
label={<LocalizedMessage id="globalStatusBar.login.dialog.usernameHint" />}
value={this.state.id}
onChange={this.handleUserIDChange}
fullWidth={true}
Expand All @@ -128,7 +132,7 @@ class LoginDialog extends Component {
</Grid>
<Grid item>
<TextField
label="Authentication key"
label={<LocalizedMessage id="globalStatusBar.login.dialog.passkeyHint" />}
ref={this.state.passkeyRef}
type="password"
onChange={this.handleKeyChange}
Expand All @@ -144,9 +148,8 @@ class LoginDialog extends Component {
<DialogActions>
<Button
disabled={this.state.loginInProgress}
onClick={this.props.onClose}
>
Cancel
onClick={this.props.onClose}>
<LocalizedMessage id="globalStatusBar.login.dialog.options.cancel" />
</Button>
<Button
disabled={this.state.loginInProgress}
Expand All @@ -159,7 +162,9 @@ class LoginDialog extends Component {
if (success) window.location.reload();
else
if (typeof pushNotification === 'function')
pushNotification('Failed to log in.')
pushNotification(
<LocalizedMessage id="globalStatusBar.login.dialog.errorText" />
)
})
// if login finished, hide the loading circle
this.setState({
Expand All @@ -171,7 +176,7 @@ class LoginDialog extends Component {
{this.state.loginInProgress ? (
<CircularProgress size={20} />
) : (
'Log in'
<LocalizedMessage id="globalStatusBar.login.dialog.options.login" />
)}
</Button>
</DialogActions>
Expand Down
4 changes: 0 additions & 4 deletions src/app/globalStatusBar/login/stub/login.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { pushNotification } from '../../../notifier/notify.js'

/**
* @name constructRequestBody
* @desc Create an URLSearchParam object suitable to be sent as log in request
Expand Down Expand Up @@ -40,8 +38,6 @@ async function login(username, password) {
)
.then(res => res.ok)
.catch(err => {
if (typeof pushNotification === 'function')
pushNotification('Failed to log in.')
if (process.env.NODE_ENV === 'development')
console.log(err);
return false;
Expand Down
21 changes: 21 additions & 0 deletions src/app/globalStatusBar/userSetting/localeChange/localeChange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { RadioGroup, FormControlLabel, Radio, FormControl } from '@material-ui/core';

export default class LocaleChange extends React.PureComponent {
render() {
let out = [];
for (let lang_key in this.props.languages) {
out.push(<FormControlLabel value={lang_key} key={lang_key}
label={this.props.languages[lang_key]} control={<Radio />}/>)
}
return (
<div style={{ marginTop: 10 }}>
<FormControl component="fieldset">
<RadioGroup value={this.props.choice} onChange={this.props.onChange}>
{out}
</RadioGroup>
</FormControl>
</div>
)
}
}
79 changes: 65 additions & 14 deletions src/app/globalStatusBar/userSetting/userSettingDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ import {
Tab,
} from '@material-ui/core'
import { Button } from '@material-ui/core'

import * as Cookies from 'js-cookie';

import PasswordChangeDialog from './passwordChange/passwordChangeDialog.js'
import LocaleChange from './localeChange/localeChange.js';
import { fade } from '../lib/libTransition.js'

import { supportedLanguages } from '../../../l10n-loader.js';

import LocalizedMessage from 'react-l10n';

/**
* @name UserSettingDialog
* @description Dialog to change user settings.
Expand All @@ -26,20 +34,35 @@ import { fade } from '../lib/libTransition.js'

class UserSettingDialog extends Component {
constructor(props) {
super(props)
super(props);

// default to en_US
if (!Cookies.get('language'))
Cookies.set('language', 'en_US')

this.state = {
currentTab: 0,
pwdChangeDialogOpen: false,

language: Cookies.get('language')
}
this.handleChange = this.handleChange.bind(this)

this.requireReload = false;
// some settings require reloading

this.submitOptions = this.submitOptions.bind(this);
}

handleChange(event, value) {
this.setState({
currentTab: value,
})
submitOptions() {
Cookies.set('language', this.state.language);

if (this.requireReload)
window.location.reload();
else
this.props.onClose()
}


render() {
return (
<>
Expand All @@ -52,17 +75,26 @@ class UserSettingDialog extends Component {
}
>
<DialogTitle>
User settings{' '}
{this.props.user ? `for ${this.props.user}` : ''}
{this.props.user
? (
<>
<LocalizedMessage id="globalStatusBar.userSetting.dialog.title.userPresent"/>
{this.props.user}
</>
)
: (
<LocalizedMessage id="globalStatusBar.userSetting.dialog.title.userAbsent"/>
)}
</DialogTitle>
<DialogContent>
<AppBar position="static">
<Tabs
value={this.state.currentTab}
fullWidth
onChange={this.handleChange}
>
<Tab label="Password" />
onChange={(e, v) => this.setState({ currentTab: v })}>
{/* we only care about the target value, ignore the event passed */}
<Tab label={<LocalizedMessage id="globalStatusBar.userSetting.dialog.entry.password.title"/>} />
<Tab label={<LocalizedMessage id="globalStatusBar.userSetting.dialog.entry.language.title"/>} />
</Tabs>
</AppBar>
{this.state.currentTab === 0 && (
Expand All @@ -78,14 +110,33 @@ class UserSettingDialog extends Component {
})
}
>
Change your password
<LocalizedMessage id="globalStatusBar.userSetting.dialog.entry.password.invokingButton"/>
</Button>
</div>
)}
{this.state.currentTab === 1 && (
<>
<LocaleChange
languages={supportedLanguages}
choice={this.state.language}
onChange={(event, arg) => {
this.requireReload = arg !== Cookies.get('language');
this.setState({ language: arg })
}}/>
{this.requireReload
&& <div style={{ color: 'red', marginTop: 10 }} >
<LocalizedMessage id="globalStatusBar.userSetting.dialog.entry.language.notice" />
</div>}
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={this.props.onClose}>Cancel</Button>
<Button onClick={this.submitOptions}>Save</Button>
<Button onClick={this.props.onClose}>
<LocalizedMessage id="globalStatusBar.userSetting.dialog.options.cancel"/>
</Button>
<Button onClick={this.submitOptions}>
<LocalizedMessage id="globalStatusBar.userSetting.dialog.options.save"/>
</Button>
</DialogActions>
</Dialog>
<PasswordChangeDialog
Expand Down
13 changes: 10 additions & 3 deletions src/app/globalStatusBar/userSetting/userSettingMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Menu, MenuItem } from '@material-ui/core'

import PropTypes from 'prop-types'

import LocalizedMessage from 'react-l10n';

/**
* @name UserSettingMenu
* @description A drop-down menu that allows (for now) changing profile settings & logging out
Expand All @@ -21,11 +23,16 @@ class UserSettingMenu extends Component {
return (
<Menu open={this.props.open} anchorEl={this.props.anchorEl}
onClose={this.props.onClose}>
<MenuItem disabled>Welcome back, {this.props.user}</MenuItem>
<MenuItem disabled>
<LocalizedMessage id="globalStatusBar.userSetting.menu.greeting"/>
{this.props.user}
</MenuItem>
<MenuItem onClick={this.props.showProfileAction}>
Change user settings
<LocalizedMessage id="globalStatusBar.userSetting.menu.changeUserSettings"/>
</MenuItem>
<MenuItem onClick={this.props.logoutAction}>
<LocalizedMessage id="globalStatusBar.userSetting.menu.logout"/>
</MenuItem>
<MenuItem onClick={this.props.logoutAction}>Log out</MenuItem>
</Menu>
)
}
Expand Down
6 changes: 5 additions & 1 deletion src/app/home/homepageLauncher.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from 'react'
import { ListItemIcon, ListItemText } from '@material-ui/core'
import Home from '@material-ui/icons/Home'

import LocalizedMessage from 'react-l10n';

/**
* @name HomepageLauncher
* @description Element to be rendered in the sidenav, responsible for launching home page
Expand All @@ -14,7 +16,9 @@ class HomepageLauncher extends React.Component {
<ListItemIcon>
<Home />
</ListItemIcon>
<ListItemText>Home</ListItemText>
<ListItemText>
<LocalizedMessage id="homepage.launcher"/>
</ListItemText>
</>
)
}
Expand Down
9 changes: 6 additions & 3 deletions src/app/problemList/codeEditor/codeBox.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { AppBar, Grid, Divider } from "@material-ui/core";
import React from 'react'
import { AppBar, Grid, Divider } from '@material-ui/core'
import LocalizedMessage from 'react-l10n';

import CodeEditor from "./codeEditor.js";
import SubmitButton from "./submitButton.js";
Expand All @@ -22,6 +23,7 @@ class CodeBox extends React.PureComponent {
fileLoading: false,
editorHeight: window.innerHeight - 180
};

this.catcherRef = React.createRef();
this.updateEditorHeight = this.updateEditorHeight.bind(this);
this.processFile = this.processFile.bind(this);
Expand Down Expand Up @@ -119,7 +121,7 @@ class CodeBox extends React.PureComponent {
})
}
>
Submit
<LocalizedMessage id="problems.codeEditor.control.submitButton"/>
</SubmitButton>
</Grid>
</Grid>
Expand All @@ -138,6 +140,7 @@ class CodeBox extends React.PureComponent {
// enforce source limit even for editor
}}
code={this.state.code}
editorHeight={this.state.editorHeight}
/>
</div>
<input type="file" onChange={(event) => this.processFile(event.target.files[0])}
Expand Down
Loading

0 comments on commit a12424d

Please sign in to comment.