Skip to content

Commit 3745a7e

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

11 files changed

Lines changed: 167 additions & 17 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,8 @@ 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'
27+
import i18next from '../../i18n'
2628

2729
const BOTTOM_OFFSET = 8
2830

@@ -159,9 +161,9 @@ class Lineage extends React.Component<LineageProps, LineageState> {
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={i18next.t('lineage.empty_title')}>
163165
<MqText subdued>
164-
Try selecting a node through search or the jobs or datasets page.
166+
{i18next.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/for_i18n.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module 'i18next';

web/src/i18n.js

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