Complete i18n support

This commit is contained in:
Paulo Gustavo Veiga 2021-02-14 17:53:37 -08:00
parent 77e3d24eb1
commit ca833d60de
7 changed files with 116 additions and 98 deletions

View File

@ -5,8 +5,8 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="<%=PUBLIC_URL%>/favicon.ico" /> <link rel="icon" href="<%=PUBLIC_URL%>/favicon.ico" />
<link rel="preconnect" href="https://fonts.gstatic.com"> <link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" rel="stylesheet" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />

View File

@ -25,8 +25,8 @@ const queryClient = new QueryClient({
}); });
const App = () => { const App = () => {
const [appi18n, setAppi18n] = useState<AppI18n>(new AppI18n('es')); const appi18n = new AppI18n();
const locale = appi18n.getLocale(); const locale = appi18n.getBrowserLocale();
return locale.message ? ( return locale.message ? (
<Provider store={store}> <Provider store={store}>

View File

@ -1,3 +1,5 @@
import { fetchAccount } from './../../redux/clientSlice';
export class Locale { export class Locale {
code: LocaleCode; code: LocaleCode;
label: string; label: string;
@ -11,21 +13,17 @@ export class Locale {
} }
export default class AppI18n { export default class AppI18n {
private locale: Locale = Locales.EN;
constructor(localeCode: string) { constructor() {
try {
this.locale = localeFromStr(localeCode)
} catch {
this.locale = this.browserLocale();
}
} }
getLocale(): Locale { public getUserLocale(): Locale {
return this.locale; const account = fetchAccount();
return account ? account.locale : this.getBrowserLocale();
} }
private browserLocale(): Locale { public getBrowserLocale(): Locale {
let localeCode = (navigator.languages && navigator.languages[0]) let localeCode = (navigator.languages && navigator.languages[0])
|| navigator.language; || navigator.language;
@ -49,8 +47,8 @@ export const Locales =
{ {
EN: new Locale('en', 'English', require('./../../compiled-lang/en.json')), EN: new Locale('en', 'English', require('./../../compiled-lang/en.json')),
ES: new Locale('es', 'Español', require('./../../compiled-lang/es.json')), ES: new Locale('es', 'Español', require('./../../compiled-lang/es.json')),
DE: new Locale('de', 'Français', require('./../../compiled-lang/de.json')), DE: new Locale('fr', 'Français', require('./../../compiled-lang/fr.json')),
FR: new Locale('fr', 'Deutsch', require('./../../compiled-lang/fr.json')), FR: new Locale('de', 'Deutsch', require('./../../compiled-lang/de.json')),
} }
export const localeFromStr = (code: string): Locale => { export const localeFromStr = (code: string): Locale => {

View File

@ -45,6 +45,7 @@ class MockClient implements Client {
} }
fetchAccountInfo(): Promise<AccountInfo> { fetchAccountInfo(): Promise<AccountInfo> {
console.log('Fetch account info ...')
const locale: LocaleCode | null = localStorage.getItem('locale') as LocaleCode; const locale: LocaleCode | null = localStorage.getItem('locale') as LocaleCode;
return Promise.resolve({ return Promise.resolve({
firstName: 'Costme', firstName: 'Costme',

View File

@ -12,7 +12,19 @@ export default class RestClient implements Client {
} }
updateAccountLanguage(locale: LocaleCode): Promise<void> { updateAccountLanguage(locale: LocaleCode): Promise<void> {
throw "Method not implemented"; const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
axios.put(`${this.baseUrl}/c/restful/account/locale`,
locale,
{ headers: { 'Content-Type': 'text/plain' } }
).then(() => {
// All was ok, let's sent to success page ...;
success();
}).catch(error => {
const errorInfo = this.parseResponseOnError(error.response);
reject(errorInfo);
});
}
return new Promise(handler);
} }
importMap(model: ImportMapInfo): Promise<number> { importMap(model: ImportMapInfo): Promise<number> {

View File

@ -80,7 +80,7 @@
"action.new": [ "action.new": [
{ {
"type": 0, "type": 0,
"value": "New Map" "value": "New Map German"
} }
], ],
"action.open": [ "action.open": [

View File

@ -22,6 +22,7 @@ import AccountMenu from './account-menu';
import ClientHealthSentinel from '../../classes/client/client-health-sentinel'; import ClientHealthSentinel from '../../classes/client/client-health-sentinel';
import HelpMenu from './help-menu'; import HelpMenu from './help-menu';
import LanguageMenu from './language-menu'; import LanguageMenu from './language-menu';
import AppI18n, { Locales } from '../../classes/app-i18n';
const logoIcon = require('../../images/logo-small.svg'); const logoIcon = require('../../images/logo-small.svg');
const poweredByIcon = require('../../images/pwrdby-white.svg'); const poweredByIcon = require('../../images/pwrdby-white.svg');
@ -107,94 +108,100 @@ const MapsPage = () => {
})) }))
// Configure using user settings ...
const appi18n = new AppI18n();
const userLocale = appi18n.getUserLocale();
return ( return (
<div className={classes.root}> <IntlProvider locale={userLocale.code} defaultLocale={Locales.EN.code} messages={userLocale.message} >
<ClientHealthSentinel /> <div className={classes.root}>
<AppBar <ClientHealthSentinel />
position="fixed" <AppBar
className={clsx(classes.appBar, { position="fixed"
[classes.appBarShift]: open, className={clsx(classes.appBar, {
})} [classes.appBarShift]: open,
variant='outlined' })}
elevation={0}> variant='outlined'
elevation={0}>
<Toolbar> <Toolbar>
<Tooltip title={intl.formatMessage({ id: 'maps.create-tooltip', defaultMessage: 'Create a New Map' })}> <Tooltip title={intl.formatMessage({ id: 'maps.create-tooltip', defaultMessage: 'Create a New Map' })}>
<Button color="primary" <Button color="primary"
size="medium" size="medium"
variant="contained" variant="contained"
type="button" type="button"
disableElevation={true} disableElevation={true}
startIcon={<AddCircleTwoTone />} startIcon={<AddCircleTwoTone />}
className={classes.newMapButton} className={classes.newMapButton}
onClick={() => setActiveDialog('create')}> onClick={() => setActiveDialog('create')}>
<FormattedMessage id="action.new" defaultMessage="New Map" /> <FormattedMessage id="action.new" defaultMessage="New Map" />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={intl.formatMessage({ id: 'maps.import-desc', defaultMessage: 'Import from other tools' })}> <Tooltip title={intl.formatMessage({ id: 'maps.import-desc', defaultMessage: 'Import from other tools' })}>
<Button <Button
color="primary" color="primary"
size="medium" size="medium"
variant="outlined" variant="outlined"
type="button" type="button"
disableElevation={true} disableElevation={true}
startIcon={<CloudUploadTwoTone />} startIcon={<CloudUploadTwoTone />}
className={classes.importButton} className={classes.importButton}
onClick={() => setActiveDialog('import')}> onClick={() => setActiveDialog('import')}>
<FormattedMessage id="action.import" defaultMessage="Import" /> <FormattedMessage id="action.import" defaultMessage="Import" />
</Button> </Button>
</Tooltip> </Tooltip>
<ActionDispatcher action={activeDialog} onClose={() => setActiveDialog(undefined)} mapsId={[]} /> <ActionDispatcher action={activeDialog} onClose={() => setActiveDialog(undefined)} mapsId={[]} />
<div className={classes.rightButtonGroup}> <div className={classes.rightButtonGroup}>
<LanguageMenu /> <LanguageMenu />
<HelpMenu /> <HelpMenu />
<AccountMenu /> <AccountMenu />
</div> </div>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<Drawer <Drawer
variant="permanent" variant="permanent"
className={clsx(classes.drawer, { className={clsx(classes.drawer, {
[classes.drawerOpen]: open
})}
classes={{
paper: clsx({
[classes.drawerOpen]: open [classes.drawerOpen]: open
}), })}
}}> classes={{
paper: clsx({
[classes.drawerOpen]: open
}),
}}>
<div style={{ padding: "20px 0 20px 15px" }}> <div style={{ padding: "20px 0 20px 15px" }}>
<img src={logoIcon} alt="logo" /> <img src={logoIcon} alt="logo" />
</div> </div>
<List component="nav"> <List component="nav">
{filterButtons.map(buttonInfo => { {filterButtons.map(buttonInfo => {
return (<StyleListItem return (<StyleListItem
icon={buttonInfo.icon} icon={buttonInfo.icon}
label={buttonInfo.label} label={buttonInfo.label}
filter={buttonInfo.filter} filter={buttonInfo.filter}
active={filter} active={filter}
onClick={handleMenuClick} onClick={handleMenuClick}
onDelete={handleLabelDelete} onDelete={handleLabelDelete}
key={`${buttonInfo.filter.type}:${(buttonInfo.filter as LabelFilter).label}`} key={`${buttonInfo.filter.type}:${(buttonInfo.filter as LabelFilter).label}`}
/>) />)
} }
)} )}
</List> </List>
<div style={{ position: 'absolute', bottom: '10px', left: '20px' }}> <div style={{ position: 'absolute', bottom: '10px', left: '20px' }}>
<Link href="http://www.wisemapping.org/"> <Link href="http://www.wisemapping.org/">
<img src={poweredByIcon} alt="Powered By WiseMapping" /> <img src={poweredByIcon} alt="Powered By WiseMapping" />
</Link> </Link>
</div> </div>
</Drawer> </Drawer>
<main className={classes.content}> <main className={classes.content}>
<div className={classes.toolbar} /> <div className={classes.toolbar} />
<MapsList filter={filter} /> <MapsList filter={filter} />
</main> </main>
</div> </div>
</IntlProvider>
); );
} }