Commit 907b96e9 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov

several i18n fixes

parent 9bfa23e9
......@@ -10,7 +10,7 @@ import { checkAccessTokens, refreshAuthTokenAuto } from 'actions/login';
import rootReducer from 'reducers/index';
import thunk from 'redux-thunk';
import {log} from 'utilities/debug';
import { I18nextProvider } from 'react-i18next';
import { I18nextProvider, withSSR } from 'react-i18next';
import routes from 'ui/routes';
import renderRoutes from 'ui/renderRoutes';
import i18nClient from '../i18n/i18n-client';
......@@ -41,9 +41,12 @@ const direction = getDir(detectedLang);
const initialState = __PRELOADED_STATE__ === undefined ? {} : __PRELOADED_STATE__;
const store = composeEnhancers(applyMiddleware(thunk, routerMiddleware(history)))(createStore)(rootReducer, initialState);
const SsrI18nProvider = withSSR()(I18nextProvider) as any;
// Configure axios for client
configureBackendApi({ apiUrl: store.getState().applicationConfig.apiUrl || process.env.API_URL || 'http://localhost:8080' });
if (__PRELOADED_STATE__ === undefined) {
document.getElementsByTagName('html')[0].setAttribute('lang', detectedLang);
document.getElementsByTagName('html')[0].setAttribute('dir', direction);
......@@ -55,9 +58,11 @@ if (__PRELOADED_STATE__ === undefined) {
ReactDOM.render(
<Provider store={ store }>
<ConnectedRouter history={ history }>
<I18nextProvider i18n={ i18nClient } initialLanguage={ initialLanguage } initialI18nStore={ initialI18nStore }>
<I18nextProvider i18n={ i18nClient }>
<MuiThemeProvider theme={ theme(direction) }>
<React.Suspense fallback="loading">
{ renderRoutes(routes) }
</React.Suspense>
</MuiThemeProvider>
</I18nextProvider>
</ConnectedRouter>
......@@ -74,11 +79,11 @@ if (__PRELOADED_STATE__ === undefined) {
ReactDOM.hydrate(
<Provider store={ store }>
<ConnectedRouter history={ history }>
<I18nextProvider i18n={ i18nClient } initialLanguage={ initialLanguage } initialI18nStore={ initialI18nStore }>
<SsrI18nProvider i18n={ i18nClient } initialLanguage={ initialLanguage } initialI18nStore={ initialI18nStore }>
<MuiThemeProvider theme={ theme(direction) }>
{ renderRoutes(routes) }
</MuiThemeProvider>
</I18nextProvider>
</SsrI18nProvider>
</ConnectedRouter>
</Provider>,
document.getElementById('the-app'),
......
import * as i18n from 'i18next';
// @ts-ignore
import i18n from 'i18next';
import axios from 'axios';
import optionsBase from './options-base';
import langDetector from './langDetector';
const XHR = require('i18next-xhr-backend');
const LanguageDetector = require('i18next-browser-languagedetector');
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
const lngDetector = new LanguageDetector();
lngDetector.addDetector(langDetector);
......@@ -31,12 +32,18 @@ optionsBase[backendKey] = backend;
const i18nClient = i18n
.use(XHR)
.use(lngDetector)
.use(initReactI18next)
.init({
detection: {
order: ['catalogLangDetector'],
lookupFromPathIndex: 0,
},
react: {
useSuspense: false,
},
...optionsBase,
});
}).then((res) => res);
export default i18nClient;
......@@ -5,7 +5,6 @@ const optionsBase = {
// have a common namespace used around the full app
ns: ['translations', 'common'],
defaultNS: 'translations',
nsSeparator: ':', // namespace separator
keySeparator: '.', // key separator
saveMissing: false,
......
......@@ -15047,6 +15047,39 @@
}
}
},
"react-event-listener": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz",
"integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==",
"requires": {
"@babel/runtime": "^7.2.0",
"prop-types": "^15.6.0",
"warning": "^4.0.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz",
"integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"react-fontawesome": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/react-fontawesome/-/react-fontawesome-1.6.1.tgz",
......
......@@ -14,6 +14,7 @@ import StringFilter from 'ui/common/filter/StringFilter';
import StringArrFilter from 'ui/common/filter/StringArrFilter';
import AutocompleteFilter from 'ui/common/filter/AutocompleteFilter';
import Accession from 'model/accession/Accession';
import Crop from 'model/genesys/Crop';
import DateFilter from 'ui/common/filter/DateFilter';
import BooleanArrFilter from 'ui/common/filter/BooleanArrFilter';
import { autocomplete } from 'accessions/actions/public';
......@@ -22,7 +23,6 @@ import IconButton from '@material-ui/core/IconButton';
import InputAdornment from '@material-ui/core/InputAdornment';
import HelpOutlineIcon from '@material-ui/icons/HelpOutline';
import Validators from 'utilities/Validators';
import Crop from '../../../model/genesys/Crop';
const AccessionFilters = ({handleSubmit, initialValues, initialize, filterCode, autocomplete, terms, crops, countryCodes, t, ...other}:
WithTranslation & {handleSubmit?: any, initialValues?: Accession, initialize?: any, filterCode?: string, autocomplete: any, terms: any, crops: Crop[], countryCodes: string[]} & any) => {
......
......@@ -2,7 +2,7 @@ import * as React from 'react';
import { WithTranslation, withTranslation } from 'react-i18next';
import Grid from '@material-ui/core/Grid';
import withStyles from '@material-ui/core/styles/withStyles';
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
import { ExternalLink } from 'ui/common/Links';
const styles = (theme) => ({
......@@ -111,10 +111,9 @@ const styles = (theme) => ({
/*tslint:enable*/
});
interface IArticleSectionProps extends React.ClassAttributes<any>, WithTranslation {
classes: any;
interface IArticleSectionProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
className?: string;
title?: string;
title?: any;
t: any;
body: string;
}
......
import * as React from 'react';
import { reduxForm } from 'redux-form';
import { withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import { WithTranslation, withTranslation } from 'react-i18next';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import { SEARCHSUGGESTIONS_FORM } from 'constants/search';
......@@ -76,7 +76,8 @@ const SuggestionsGroup = ({ classes, t, groupTitle, group }) => {
);
};
const SuggestionsForm = ({ classes, t, suggestions, onReset, handleSubmit, initialize, onSubmit, ...other }) => {
const SuggestionsForm = ({ classes, t, suggestions, onReset, handleSubmit, initialize, onSubmit, ...other }:
WithTranslation & WithStyles & { suggestions: any, onReset: any, handleSubmit: any, initialize: any, onSubmit: any }) => {
const processSubmit = handleSubmit((data) => {
// console.log('Submitting', data);
return onSubmit(data);
......
......@@ -9,7 +9,7 @@ import Card, { CardHeader, CardContent } from 'ui/common/Card';
import Markdown from 'ui/common/markdown';
import CropChips from 'crop/ui/c/CropChips';
const DescriptorListCard = ({ descriptorList, className, t }: WithTranslation & { descriptorList: DescriptorList, className: string}) => {
const DescriptorListCard = ({ descriptorList, className, t }: WithTranslation & { descriptorList: DescriptorList, className?: string}) => {
if (descriptorList) {
return (
<Card className={ className }>
......
import * as React from 'react';
import { WithTranslation, withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import { log } from 'utilities/debug';
import { fixDate } from 'utilities';
......@@ -41,8 +41,7 @@ const styles = (theme) => ({
},
});
interface IDetailInfoProps extends React.ClassAttributes<any>, WithTranslation {
classes: any;
interface IDetailInfoProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
// loadDescriptor: (uuid: string) => void;
publishDescriptor?: (descriptor: Descriptor, needToRedirect?: boolean) => void;
approveDescriptor?: (descriptor: Descriptor) => void;
......@@ -51,9 +50,8 @@ interface IDetailInfoProps extends React.ClassAttributes<any>, WithTranslation {
editDescriptor: (descriptor: Descriptor) => void;
descriptor: Descriptor;
// error: any;
descriptorExtra: any;
descriptorExtra?: any;
repositoryDownloadUrl: (file: RepositoryFile, first?: boolean) => any;
t: any;
}
class DetailInfo extends React.Component<IDetailInfoProps, any> {
......
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withTranslation } from 'react-i18next';
import { WithTranslation, withTranslation } from 'react-i18next';
// actions
import {
......@@ -21,14 +21,13 @@ import DialogTitle from '@material-ui/core/DialogTitle';
import DimensionForm from './c/dimensionForm';
import confirm from 'utilities/confirmAlert';
interface IDimensionDialogProps extends React.ClassAttributes<any> {
interface IDimensionDialogProps extends React.ClassAttributes<any>, WithTranslation {
loadDimension: (name: string) => void;
saveDimension: (dimension: Dimension<any>) => void;
clearDimDetails: () => void;
deleteDimension: (dimension: Dimension<any>) => void;
toggleDimensionModal: (open: boolean) => void;
dimension: Dimension<any>;
t: any;
isOpen: boolean;
}
......
......@@ -101,7 +101,7 @@ class ChangesSection extends React.Component<IChangesSectionProps> {
Object.keys(changes.data).sort((a, b) => (new Date(b)).getTime() - (new Date(a).getTime())).map((date, index) => (
<div key={ date } className={ classes.changesItem }>
<div className={ classes.changesItemHeader }>
<h3><PrettyDate value={ date }/></h3>
<h3><PrettyDate value={ new Date(date) }/></h3>
<Button id={ `copy-button-${index}` } onClick={ (e) => this.copyRunTable(e, index) }>
{ t('common:action.copyToClipboard') }&nbsp;&nbsp; <FileCopyIcon fontSize="small"/>
</Button>
......
......@@ -2,7 +2,6 @@ import * as React from 'react';
import { WithTranslation, withTranslation } from 'react-i18next';
import {Field} from 'redux-form';
import ExecutionGroup from 'model/kpi/ExecutionGroup';
import Grid from '@material-ui/core/Grid';
import { TextField } from 'ui/common/text-field';
import Validators from 'utilities/Validators';
......@@ -52,7 +51,7 @@ const execGroupField = (member, index, fields, itemLabel) => (
interface IExecutionGroupsFieldProps extends React.ClassAttributes<any>, WithTranslation {
label?: string;
groups: ExecutionGroup[];
name: string;
}
class ExecutionGroupsField extends React.Component<IExecutionGroupsFieldProps> {
......@@ -66,9 +65,9 @@ class ExecutionGroupsField extends React.Component<IExecutionGroupsFieldProps> {
}
public render() {
const { label, t } = this.props;
const { label, name, t } = this.props;
return(
<ItemsEditor name="groups" header={ t('kpi.admin.c.executionForm.groupListField.header') } itemLabel={ label } addItem={ this.onAddGroup } removeItem={ this.onDeleteGroup } component={ execGroupField } />
<ItemsEditor name={ name } header={ t('kpi.admin.c.executionForm.groupListField.header') } itemLabel={ label } addItem={ this.onAddGroup } removeItem={ this.onDeleteGroup } component={ execGroupField } />
);
}
}
......
import * as React from 'react';
import { withTranslation } from 'react-i18next';
import { WithTranslation, withTranslation } from 'react-i18next';
import {Field, reduxForm} from 'redux-form';
// Actions
import {receiveRequestInfo} from 'requests/actions/public';
......@@ -15,10 +15,9 @@ import PurposeTypeRadioGroup from './PurposeTypeRadioGroup';
import Switch from '@material-ui/core/Switch';
import CountryCodePicker from 'datasets/ui/dashboard/dataset-stepper/steps/location/CountryCodePicker';
interface IPersonalInfoFormProps extends React.ClassAttributes<any> {
interface IPersonalInfoFormProps extends React.ClassAttributes<any>, WithTranslation {
handleSubmit: any;
initialValues: any;
t: any;
}
class RequestInfoForm extends React.Component<IPersonalInfoFormProps, any> {
......@@ -151,8 +150,8 @@ class RequestInfoForm extends React.Component<IPersonalInfoFormProps, any> {
}
export default withTranslation()(reduxForm({
export default reduxForm({
form: REQUEST_INFO_FORM,
onSubmit: (values, dispatch) => dispatch(receiveRequestInfo(values)),
enableReinitialize: true,
})(RequestInfoForm));
})(withTranslation()(RequestInfoForm));
......@@ -24,7 +24,8 @@ const SubsetFilters = ({handleSubmit, initialValues, initialize, t, terms, crops
<PartnerFilter name="owner" label="subsets.public.f.partner"/>
</CollapsibleComponentSearch>
<CollapsibleComponentSearch sectionIndex={ sectionIndex++ } title={ t('subsets.public.f.textSearch') }>
<TextFilter name="_text"
<TextFilter
name="_text"
label={ t('public.f._text') }
placeholder={ t('subsets.public.f._textPlaceholder') }
hint={ t('public.f._textHint') }
......
import * as React from 'react';
import { withTranslation } from 'react-i18next';
import { WithTranslation, withTranslation } from 'react-i18next';
import {Field, reduxForm, FieldArray} from 'redux-form';
import * as _ from 'lodash';
......@@ -15,7 +15,7 @@ import Subset from 'model/subset/Subset';
import SubsetCreator, {CreatorRole} from 'model/subset/SubsetCreator';
// ui
import {withStyles} from '@material-ui/core/styles';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import IconButton from '@material-ui/core/IconButton';
......@@ -26,11 +26,9 @@ import SubsetCreatorAutocompleteField from './SubsetCreatorAutocompleteField';
import RadioSelection from 'ui/common/forms/RadioSelection';
interface ISubsetCreatorFormProps extends React.ClassAttributes<any> {
t: any;
interface ISubsetCreatorFormProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
initialValues: any;
fields: any;
classes: any;
subset: Subset;
isInvalidForm: boolean;
createSubsetCreator: () => Promise<SubsetCreator>;
......@@ -195,7 +193,7 @@ class SubsetCreatorForm extends React.Component<ISubsetCreatorFormProps, any> {
}
}
export default withTranslation()(reduxForm({
export default reduxForm({
form: SUBSET_CREATOR_FORM,
enableReinitialize: true,
})(withTranslation()(((withStyles as any)(styles)(SubsetCreatorForm)))));
})((withStyles as any)(styles)(withTranslation()(SubsetCreatorForm)));
import * as React from 'react';
import { withTranslation } from 'react-i18next';
import { WithTranslation, withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
......@@ -21,7 +21,7 @@ import PageTitle from 'ui/common/PageTitle';
import confirm from 'utilities/confirmAlert';
import { ScrollToTopOnMount } from 'ui/common/page/scrollers';
interface IDataPublishedContainerProps<T> extends React.ClassAttributes<any> {
interface IDataPublishedContainerProps<T> extends React.ClassAttributes<any>, WithTranslation {
// basic
title: string;
tab?: string;
......@@ -65,9 +65,6 @@ interface IDataPublishedContainerProps<T> extends React.ClassAttributes<any> {
selectAll: () => void;
unselectAll: () => void;
onPageChange: () => void;
// Util
t: any;
}
class BaseMyDataPage<T> extends React.Component<IDataPublishedContainerProps<T>, any> {
......@@ -253,4 +250,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
filterCodeToUrl,
}, dispatch);
export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(BaseMyDataPage));
export default connect(mapStateToProps, mapDispatchToProps)(withTranslation()(BaseMyDataPage));
......@@ -99,7 +99,7 @@ class AuditedInfo extends React.Component<IAuditedInfo> {
t={ t }
/>
</code>
<PrettyDate value={ auditLog.logDate } t={ t }/>
<PrettyDate value={ auditLog.logDate }/>
</div>
)) }
</div>
......
......@@ -6,7 +6,7 @@ import { WithTranslation, withTranslation } from 'react-i18next';
interface IBundledProps extends React.ClassAttributes<any>, WithTranslation {
children?: any;
propertiesList: Array<{title: string, value: any, customProps?: any}>;
propertiesList: Array<{title?: string, value?: any, customProps?: any}>;
title: string;
small?: boolean;
topSection?: any;
......
import * as React from 'react';
import { withTranslation } from 'react-i18next';
import { WithTranslation, withTranslation } from 'react-i18next';
import {ConnectDropTarget, DropTarget, DropTargetConnector, DropTargetMonitor} from 'react-dnd';
import {withStyles} from '@material-ui/core/styles';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import UploadButton from 'ui/common/buttons/UploadButton';
const styles = () => ({
......@@ -15,16 +15,14 @@ const styles = () => ({
},
});
export interface ITargetBoxProps extends React.ClassAttributes<any> {
export interface ITargetBoxProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
id: any;
accepts: string[];
connectDropTarget?: ConnectDropTarget;
isOver?: boolean;
canDrop?: boolean;
onDrop: (props: ITargetBoxProps, monitor: DropTargetMonitor) => void;
classes: any;
handleUploading: (files: File[]) => void;
t: any;
multiple?: boolean;
}
......@@ -76,4 +74,4 @@ const dropTargetWrap = DropTarget((props: ITargetBoxProps) => props.accepts, box
canDrop: monitor.canDrop(),
}));
export default withTranslation()((withStyles as any)(styles)(dropTargetWrap(TargetBox)));
export default (withStyles as any)(styles)(dropTargetWrap(withTranslation()(TargetBox)));
......@@ -102,18 +102,20 @@ class LicenceFilterInternal extends React.Component<ILicenceFilterInternalProps>
interface ILicenceFilterProps extends React.ClassAttributes<any>, WithTranslation {
name: string;
className?: string;
}
class LicenceFilter extends React.Component<ILicenceFilterProps> {
public render() {
const { name, t } = this.props;
const { name, className, t } = this.props;
return (
<Field
name={ name }
component={ LicenceFilterInternal }
t={ t }
className={ className }
/>
);
}
......
......@@ -67,7 +67,7 @@ interface ITextFilter extends React.ClassAttributes<any>, WithTranslation {
endAdornment?: any;
}
class TextFilter extends React.Component<ITextFilter, any> {
class TextFilter extends React.Component<ITextFilter & any, any> {
public render() {
const { name , label, className, t, endAdornment, ...other } = this.props;
......
......@@ -73,4 +73,4 @@ const TopSection = ({classes, pageTitle, subTitle, backTarget = 'dashboard', t}:
</Grid>
);
export default withTranslation()((withStyles as any)(styles)(TopSection));
export default (withStyles as any)(styles)(withTranslation()(TopSection));
import * as React from 'react';
import { withTranslation } from 'react-i18next';
import {withStyles} from '@material-ui/core/styles';
import { WithTranslation, withTranslation } from 'react-i18next';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import StepsListItem from './StepsListItem';
interface IProgressMenuProps extends React.ClassAttributes<any> {
interface IProgressMenuProps extends React.ClassAttributes<any>, WithStyles, WithTranslation {
disabled: boolean;
stepperTitle: string;
classes: any;
location: any;
steps: any;
onGotoStep: (i: number) => void;
t: any;
}
const styleSheet = (theme) => ({
......@@ -70,4 +68,4 @@ class ProgressMenu extends React.Component<IProgressMenuProps, any> {
}
}
export default withTranslation()(withStyles(styleSheet)<any>(ProgressMenu));
export default withStyles(styleSheet)<any>(withTranslation()(ProgressMenu));
......@@ -56,7 +56,7 @@ const DASHBOARD_MENUS = [
},
];
const Layout = ({route, match, t, location}: WithTranslation &{ route: any, match: any, t: any, location: any}) => (
const Layout = ({route, match, t, location}: WithTranslation & { route: any, match: any, t: any, location: any}) => (
<div>
<TopNavigation menuItems={ DASHBOARD_MENUS } location={ location } root={ route.path } title={ 'dashboard.p.dashboard.dashboard' }/>
{ renderRoutes(route.routes, match.path) }
......
......@@ -37,7 +37,7 @@ const styles = (theme) => ({
/*tslint:enable*/
});
const MyListCount = ({count, classes, classname = '', style = {}, t}: WithStyles & WithTranslation &{count: number, classname?: string, style?: any}) => (
const MyListCount = ({count, classes, classname = '', style = {}, t}: WithStyles & WithTranslation & {count: number, classname?: string, style?: any}) => (
<span>
{ t('list.common.menu') }
<span style={ style } className={ `${classes.root} ${classname}` }>{ count }</span>
......
import * as React from 'react';
import { withStyles } from '@material-ui/core/styles';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import Card, {CardHeader, CardContent, CardActions} from 'ui/common/Card';
// import * as classnames from 'classnames';
......@@ -59,8 +59,7 @@ const styles = (theme) => ({
},
});
interface ILayoutProps extends React.Props<any> {
classes?: any;
interface ILayoutProps extends React.ClassAttributes<any>, WithStyles {
children?: any;
sidebar?: any;
withFooter?: any;
......
import * as React from 'react';
import {bindActionCreators, compose} from 'redux';
import {connect} from 'react-redux';
import {withStyles} from '@material-ui/core/styles';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import SidebarDrawer from './SidebarDrawer';
import ChevronRight from '@material-ui/icons/ChevronRight';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import { withTranslation } from 'react-i18next';
import { WithTranslation, withTranslation } from 'react-i18next';
import { collapseSidebar } from 'actions/layout';
import withWidth from '@material-ui/core/withWidth/withWidth';
import withWidth, { WithWidth } from '@material-ui/core/withWidth/withWidth';
import {Breakpoint} from '@material-ui/core/styles/createBreakpoints';
interface ISidebarProps extends React.ClassAttributes<any> {
classes: any;
interface ISidebarProps extends React.ClassAttributes<any>, WithTranslation, WithWidth, WithStyles {