Skip to content

Commit 52dac32

Browse files
committed
add i18next and config for i18n
Signed-off-by: Michael Robinson <merobi@gmail.com>
1 parent 0f857c5 commit 52dac32

9 files changed

Lines changed: 166 additions & 16 deletions

File tree

web/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@
5959
"redux": "^4.0.1",
6060
"redux-logger": "^3.0.6",
6161
"redux-saga": "^1.0.2",
62-
"webpack-merge": "^4.2.1"
62+
"webpack-merge": "^4.2.1",
63+
"i18next": "^22.0.6",
64+
"i18next-browser-languagedetector": "^7.0.1",
65+
"react-i18next": "^12.0.0"
6366
},
6467
"devDependencies": {
6568
"@types/classnames": "^2.2.10",

web/src/components/header/Header.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import MqText from '../core/text/MqText'
1010
import NamespaceSelect from '../namespace-select/NamespaceSelect'
1111
import React, { ReactElement } from 'react'
1212
import Search from '../search/Search'
13+
import { useTranslation } from 'react-i18next'
1314

1415
const styles = (theme: Theme) => {
1516
return createStyles({
@@ -40,6 +41,7 @@ const styles = (theme: Theme) => {
4041
type HeaderProps = WithStyles<typeof styles>
4142

4243
const Header = (props: HeaderProps): ReactElement => {
44+
const { t } = useTranslation();
4345
const { classes } = props
4446
return (
4547
<AppBar position='fixed' elevation={0} className={classes.appBar}>
@@ -53,7 +55,7 @@ const Header = (props: HeaderProps): ReactElement => {
5355
<NamespaceSelect />
5456
<Box ml={2}>
5557
<MqText link href={API_DOCS_URL}>
56-
API Docs
58+
{t('header.docs_link')}
5759
</MqText>
5860
</Box>
5961
</Box>

web/src/components/jobs/JobDetailPage.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import MqText from '../core/text/MqText'
2424
import RunInfo from './RunInfo'
2525
import RunStatus from './RunStatus'
2626
import Runs from './Runs'
27+
import { useTranslation } from 'react-i18next'
2728

2829
const styles = ({ spacing }: ITheme) => {
2930
return createStyles({
@@ -52,6 +53,7 @@ const JobDetailPage: FunctionComponent<IProps> = props => {
5253
const handleChange = (event: ChangeEvent, newValue: SetStateAction<number>) => {
5354
setTab(newValue)
5455
}
56+
const { t } = useTranslation()
5557

5658
useEffect(() => {
5759
fetchRuns(job.name, job.namespace)
@@ -82,13 +84,13 @@ const JobDetailPage: FunctionComponent<IProps> = props => {
8284
>
8385
<Box mb={2} display={'flex'} justifyContent={'space-between'} alignItems={'center'}>
8486
<Tabs value={tab} onChange={handleChange} textColor='primary' indicatorColor='primary'>
85-
<Tab label='LATEST RUN' disableRipple={true} />
86-
<Tab label='RUN HISTORY' disableRipple={true} />
87+
<Tab label={t('jobs.latest_tab')} disableRipple={true} />
88+
<Tab label={t('jobs.history_tab')} disableRipple={true} />
8789
</Tabs>
8890
<Box display={'flex'} alignItems={'center'}>
8991
<Box mr={1}>
9092
<Button variant='outlined' color='primary' target={'_blank'} href={job.location}>
91-
Location
93+
{t('jobs.location')}
9294
</Button>
9395
</Box>
9496
<IconButton onClick={() => history.push('/')}>
@@ -116,8 +118,8 @@ const JobDetailPage: FunctionComponent<IProps> = props => {
116118
) : (
117119
!job.latestRun && (
118120
<MqEmpty
119-
title={'No Run Information Available'}
120-
body={'Try adding some runs for this job.'}
121+
title={t('jobs.empty_title')}
122+
body={t('jobs.empty_body')}
121123
/>
122124
)
123125
)

web/src/components/jobs/RunInfo.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import MqCode from '../core/code/MqCode'
77
import MqJson from '../core/code/MqJson'
88
import MqText from '../core/text/MqText'
99
import React, { FunctionComponent } from 'react'
10+
import { useTranslation } from 'react-i18next'
1011

1112
interface RunInfoProps {
1213
run: Run
1314
}
1415

1516
const RunInfo: FunctionComponent<RunInfoProps> = props => {
1617
const { run } = props
18+
const { t } = useTranslation()
1719

1820
return (
1921
<Box mt={2}>
@@ -26,7 +28,7 @@ const RunInfo: FunctionComponent<RunInfoProps> = props => {
2628
{run.facets && (
2729
<Box mt={2}>
2830
<Box mb={1}>
29-
<MqText subheading>FACETS</MqText>
31+
<MqText subheading>{t('jobs.runinfo_subhead')}</MqText>
3032
</Box>
3133
<MqJson code={run.facets} />
3234
</Box>

web/src/components/jobs/Runs.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import React, { FunctionComponent, SetStateAction } from 'react'
2626
import RunInfo from './RunInfo'
2727
import RunStatus from './RunStatus'
2828
import transitions from '@material-ui/core/styles/transitions'
29+
import { useTranslation } from 'react-i18next'
2930

3031
const RUN_COLUMNS = ['ID', 'STATE', 'CREATED AT', 'STARTED AT', 'ENDED AT', 'DURATION']
3132

@@ -54,8 +55,9 @@ interface RunsProps {
5455

5556
const Runs: FunctionComponent<RunsProps & WithStyles<typeof styles>> = props => {
5657
const { runs, facets, classes } = props
58+
const { t } = useTranslation()
5759
if (runs.length === 0) {
58-
return <MqEmpty title={'No Runs Found'} body={'Try adding some runs for this job.'} />
60+
return <MqEmpty title={'jobs.empty_title'} body={'jobs.empty_body'} />
5961
}
6062

6163
const [infoView, setInfoView] = React.useState<Run | null>(null)
@@ -116,7 +118,7 @@ const Runs: FunctionComponent<RunsProps & WithStyles<typeof styles>> = props =>
116118
{facets && (
117119
<Box mt={2}>
118120
<Box mb={1}>
119-
<MqText subheading>FACETS</MqText>
121+
<MqText subheading>{t('jobs.runs_subhead')}</MqText>
120122
</Box>
121123
<MqCode code={JSON.stringify(facets, null, '\t')} />
122124
</Box>

web/src/components/lineage/Lineage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import MqEmpty from '../core/empty/MqEmpty'
2323
import MqText from '../core/text/MqText'
2424
import Node from './components/node/Node'
2525
import ParentSize from '@visx/responsive/lib/components/ParentSize'
26+
import { useTranslation } from 'react-i18next'
2627

2728
const BOTTOM_OFFSET = 8
2829

@@ -155,13 +156,14 @@ class Lineage extends React.Component<LineageProps, LineageState> {
155156

156157
render() {
157158
const { classes } = this.props
159+
const { t } = useTranslation()
158160
return (
159161
<Box className={classes.lineageContainer}>
160162
{this.props.selectedNode === null && (
161163
<Box display={'flex'} justifyContent={'center'} alignItems={'center'} pt={2}>
162-
<MqEmpty title={'No node selected'}>
164+
<MqEmpty title={t('lineage.empty_title')}>
163165
<MqText subdued>
164-
Try selecting a node through search or the jobs or datasets page.
166+
{t('lineage.empty_body')}
165167
</MqText>
166168
</MqEmpty>
167169
</Box>

web/src/components/search/SearchPlaceholder.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { theme } from '../../helpers/theme'
55
import MqText from '../core/text/MqText'
66
import React from 'react'
77
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles'
8+
import { useTranslation } from 'react-i18next';
89

910
const styles = (theme: Theme) =>
1011
createStyles({
@@ -20,24 +21,25 @@ const styles = (theme: Theme) =>
2021
})
2122

2223
const SearchPlaceholder: React.FC<WithStyles<typeof styles>> = ({ classes }) => {
24+
const { t } = useTranslation();
2325
return (
2426
<Box className={classes.root}>
2527
<Box display={'inline'}>
2628
<MqText disabled inline>
2729
{' '}
28-
Search
30+
{t('search.search')}
2931
</MqText>{' '}
3032
<MqText bold inline font={'mono'} color={theme.palette.common.white}>
3133
{' '}
32-
Jobs
34+
{t('search.jobs')}
3335
</MqText>{' '}
3436
<MqText disabled inline>
3537
{' '}
36-
and
38+
{t('search.and')}
3739
</MqText>{' '}
3840
<MqText bold inline font={'mono'} color={theme.palette.common.white}>
3941
{' '}
40-
Datasets
42+
{t('search.datasets')}
4143
</MqText>
4244
</Box>
4345
</Box>

web/src/i18n.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import i18n from 'i18next';
2+
import { initReactI18next } from 'react-i18next';
3+
// import Backend from 'i18next-http-backend';
4+
import LanguageDetector from 'i18next-browser-languagedetector';
5+
6+
7+
i18n
8+
// .use(Backend)
9+
// detect user language
10+
// learn more: https://github.com/i18next/i18next-browser-languageDetector
11+
.use(LanguageDetector)
12+
// pass the i18n instance to react-i18next.
13+
.use(initReactI18next)
14+
// init i18next
15+
// for all options read: https://www.i18next.com/overview/configuration-options
16+
.init({
17+
debug: true,
18+
fallbackLng: 'en',
19+
interpolation: {
20+
escapeValue: false, // not needed for react as it escapes by default
21+
},
22+
resources: {
23+
en: {
24+
translation: {
25+
// here we will place our translations...
26+
header: {
27+
docs_link: 'API Docs'
28+
},
29+
jobs: {
30+
latest_tab: 'LATEST RUN',
31+
history_tab: 'RUN HISTORY',
32+
location: 'LOCATION',
33+
empty_title: 'No Run Information Available',
34+
empty_body: 'Try adding some runs for this job.',
35+
runinfo_subhead: 'FACETS',
36+
runs_subhead: 'FACETS'
37+
},
38+
search: {
39+
search: 'Search',
40+
jobs: 'Jobs',
41+
and: 'and',
42+
datasets: 'Datasets'
43+
},
44+
lineage: {
45+
empty_title: 'No node selected',
46+
empty_body: 'Try selecting a node through search or the jobs or datasets page.'
47+
}
48+
}
49+
},
50+
fr: {
51+
translation: {
52+
header: {
53+
docs_link: 'API Docs'
54+
},
55+
jobs: {
56+
latest_tab: 'DERNIÈRE COURSE',
57+
history_tab: "HISTORIQUE D'EXECUTION",
58+
location: 'EMPLACEMENT',
59+
empty_title: 'Pas de Course les Informations Disponibles',
60+
empty_body: "Essayez d'ajouter quelques exécutions pour ce travail.",
61+
runinfo_subhead: 'FACETTES',
62+
runs_subhead: 'FACETTES',
63+
},
64+
search: {
65+
search: 'Recherche',
66+
jobs: "d'Emplois",
67+
and: 'et',
68+
datasets: "d'Ensembles de Données"
69+
},
70+
lineage: {
71+
empty_title: 'Aucun nœud sélectionné',
72+
empty_body: 'Essayez de sélectionner un nœud via la recherche ou la page des travaux ou des ensembles de données.'
73+
}
74+
}
75+
},
76+
es: {
77+
translation: {
78+
header: {
79+
docs_link: 'API Docs'
80+
},
81+
jobs: {
82+
latest_tab: 'ÚLTIMA EJECUCIÓN',
83+
history_tab: 'HISTORIAL DE EJECUCIONES',
84+
location: 'UBICACIÓN',
85+
empty_title: 'No hay Información de Ejecución Disponible',
86+
empty_body: 'Intente agregar algunas ejecuciones para este trabajo.',
87+
runinfo_subhead: 'FACETAS',
88+
runs_subhead: 'FACETAS'
89+
},
90+
search: {
91+
search: 'Buscar',
92+
jobs: 'Trabajos',
93+
and: 'y',
94+
datasets: 'Conjuntos de Datos'
95+
},
96+
lineage: {
97+
empty_title: 'Ningún nodo seleccionado',
98+
empty_body: 'Intente seleccionar un nodo mediante la búsqueda o la página de trabajos o conjuntos de datos.'
99+
}
100+
}
101+
},
102+
pl: {
103+
translation: {
104+
header: {
105+
docs_link: 'API Dokumenty'
106+
},
107+
jobs: {
108+
latest_tab: 'OSTATNI BIEG',
109+
history_tab: 'BIEGAĆ HISTORIA',
110+
location: 'LOKALIZACJA',
111+
empty_title: 'Brak dostępnych informacji o przebiegu',
112+
empty_body: 'Spróbuj dodać kilka przebiegów dla tego zadania.',
113+
runinfo_subhead: 'ASPECTY',
114+
runs_subhead: 'ASPECTY'
115+
},
116+
search: {
117+
search: 'Wyszukaj Oferty',
118+
jobs: 'Pracy',
119+
and: 'i',
120+
datasets: 'Zestawy Danych'
121+
},
122+
lineage: {
123+
empty_title: 'Nie wybrano węzła',
124+
empty_body: 'Spróbuj wybrać węzeł za pomocą wyszukiwania lub strony zadań lub zestawów danych.'
125+
}
126+
}
127+
}
128+
129+
}
130+
});
131+
132+
export default i18n;

web/src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ import App from './components/App'
55
// fonts
66
import './index.css'
77

8+
// i18n
9+
import './i18n.js';
10+
811
ReactDOM.render(<App />, document.getElementById('root'))

0 commit comments

Comments
 (0)