From 4ed042b7434f41ea0b839634d755e5554fab1634 Mon Sep 17 00:00:00 2001
From: Jessie Keck <jessie.keck@gmail.com>
Date: Fri, 1 Feb 2019 16:10:25 -0800
Subject: [PATCH] Internationalize strings using react-i18next

---
 __tests__/src/components/WindowList.test.js   |  2 +-
 .../components/WindowSideBarButtons.test.js   |  2 +-
 .../components/WindowSideBarInfoPanel.test.js |  2 +-
 .../src/components/WindowSideBarPanel.test.js |  5 ++--
 __tests__/src/components/WindowTopBar.test.js |  1 +
 .../WorkspaceControlPanelButtons.test.js      |  3 ++-
 .../WorkspaceFullScreenButton.test.js         |  5 +++-
 .../src/components/WorkspaceMenu.test.js      |  6 ++---
 locales/en/translation.json                   | 27 ++++++++++++++++++-
 src/components/ManifestForm.js                |  8 +++++-
 src/components/WindowList.js                  | 12 +++++----
 src/components/WindowSideBarButtons.js        | 10 +++++--
 src/components/WindowSideBarInfoPanel.js      |  6 +++--
 src/components/WindowSideBarPanel.js          |  2 +-
 src/components/WindowThumbnailSettings.js     | 16 ++++++-----
 src/components/WindowTopBar.js                | 11 +++++---
 src/components/WindowTopMenuButton.js         | 18 ++++++-------
 src/components/WorkspaceAddButton.js          |  9 +++++--
 .../WorkspaceControlPanelButtons.js           |  3 ++-
 src/components/WorkspaceExport.js             |  6 +++--
 src/components/WorkspaceFullScreenButton.js   |  9 +++++--
 src/components/WorkspaceMenu.js               | 10 ++++---
 src/components/WorkspaceMenuButton.js         | 11 +++++---
 src/components/WorkspaceSettings.js           | 12 +++++----
 src/containers/ManifestForm.js                |  9 ++++++-
 src/containers/WindowList.js                  | 10 ++++++-
 src/containers/WindowSideBarButtons.js        |  2 ++
 src/containers/WindowSideBarInfoPanel.js      | 12 +++++++++
 src/containers/WindowThumbnailSettings.js     |  4 +++
 src/containers/WindowTopBar.js                |  2 ++
 src/containers/WindowTopMenuButton.js         | 12 +++++++++
 src/containers/WorkspaceAddButton.js          | 11 +++++++-
 src/containers/WorkspaceExport.js             |  4 +++
 src/containers/WorkspaceFullScreenButton.js   | 12 ++++++++-
 src/containers/WorkspaceMenu.js               | 12 +++++++++
 src/containers/WorkspaceMenuButton.js         | 12 +++++++++
 src/containers/WorkspaceSettings.js           |  2 ++
 37 files changed, 235 insertions(+), 65 deletions(-)
 create mode 100644 src/containers/WindowSideBarInfoPanel.js
 create mode 100644 src/containers/WindowTopMenuButton.js
 create mode 100644 src/containers/WorkspaceMenu.js
 create mode 100644 src/containers/WorkspaceMenuButton.js

diff --git a/__tests__/src/components/WindowList.test.js b/__tests__/src/components/WindowList.test.js
index 896dd2aa6..0bc7a7e14 100644
--- a/__tests__/src/components/WindowList.test.js
+++ b/__tests__/src/components/WindowList.test.js
@@ -49,7 +49,7 @@ describe('WindowList', () => {
       expect(wrapper.find('WithStyles(MenuItem)').length).toBe(1);
       expect(wrapper.find('WithStyles(MenuItem)').key()).toBe('xyz');
       expect(
-        wrapper.find('WithStyles(MenuItem)').matchesElement(<MenuItem>[Untitled]</MenuItem>),
+        wrapper.find('WithStyles(MenuItem)').matchesElement(<MenuItem>untitled</MenuItem>),
       ).toBe(true);
       wrapper.find('WithStyles(MenuItem)').simulate('click', {});
       expect(handleClose).toBeCalled();
diff --git a/__tests__/src/components/WindowSideBarButtons.test.js b/__tests__/src/components/WindowSideBarButtons.test.js
index 0ffde2da5..b57978af8 100644
--- a/__tests__/src/components/WindowSideBarButtons.test.js
+++ b/__tests__/src/components/WindowSideBarButtons.test.js
@@ -18,7 +18,7 @@ describe('WindowSideBarButtons', () => {
       <WindowSideBarButtons toggleWindowSideBarPanel={toggleWindowSideBarPanel} />,
     );
 
-    const iconButton = wrapper.find('WithStyles(IconButton)[aria-label="Open information companion window"]');
+    const iconButton = wrapper.find('WithStyles(IconButton)[aria-label="openInfoCompanionWindow"]');
     expect(iconButton.simulate('click'));
     expect(toggleWindowSideBarPanel).toHaveBeenCalledTimes(1);
     expect(toggleWindowSideBarPanel).toHaveBeenCalledWith('info');
diff --git a/__tests__/src/components/WindowSideBarInfoPanel.test.js b/__tests__/src/components/WindowSideBarInfoPanel.test.js
index 5180d0f50..21c6fabc3 100644
--- a/__tests__/src/components/WindowSideBarInfoPanel.test.js
+++ b/__tests__/src/components/WindowSideBarInfoPanel.test.js
@@ -22,7 +22,7 @@ describe('WindowSideBarInfoPanel', () => {
   it('renders without an error', () => {
     expect(
       wrapper.find('WithStyles(Typography)[variant="h2"]').first().matchesElement(
-        <Typography>About this item</Typography>,
+        <Typography>aboutThisItem</Typography>,
       ),
     ).toBe(true);
 
diff --git a/__tests__/src/components/WindowSideBarPanel.test.js b/__tests__/src/components/WindowSideBarPanel.test.js
index d262ecd0f..f31c05019 100644
--- a/__tests__/src/components/WindowSideBarPanel.test.js
+++ b/__tests__/src/components/WindowSideBarPanel.test.js
@@ -3,6 +3,7 @@ import { shallow } from 'enzyme';
 import createStore from '../../../src/state/createStore';
 import * as actions from '../../../src/state/actions';
 import WindowSideBarPanel from '../../../src/components/WindowSideBarPanel';
+import WindowSideBarInfoPanel from '../../../src/containers/WindowSideBarInfoPanel';
 import fixture from '../../fixtures/version-2/001.json';
 
 describe('WindowSideBarPanel', () => {
@@ -21,7 +22,7 @@ describe('WindowSideBarPanel', () => {
     });
 
     it('renders the WindowSideBarInfoPanel', () => {
-      expect(wrapper.find('WithStyles(WindowSideBarInfoPanel)').length).toBe(1);
+      expect(wrapper.find(WindowSideBarInfoPanel).length).toBe(1);
     });
   });
 
@@ -31,7 +32,7 @@ describe('WindowSideBarPanel', () => {
     });
 
     it('does not render any panel component', () => {
-      expect(wrapper.find('WithStyes(WindowSideBarInfoPanel)').length).toBe(0);
+      expect(wrapper.find(WindowSideBarInfoPanel).length).toBe(0);
     });
   });
 });
diff --git a/__tests__/src/components/WindowTopBar.test.js b/__tests__/src/components/WindowTopBar.test.js
index e3002a0ce..86cc56729 100644
--- a/__tests__/src/components/WindowTopBar.test.js
+++ b/__tests__/src/components/WindowTopBar.test.js
@@ -24,6 +24,7 @@ describe('WindowTopBar', () => {
         removeWindow={mockRemoveWindow}
         toggleWindowSideBar={mockToggleWindowSideBar}
         classes={{}}
+        t={key => key}
       />,
     ).dive();
   });
diff --git a/__tests__/src/components/WorkspaceControlPanelButtons.test.js b/__tests__/src/components/WorkspaceControlPanelButtons.test.js
index c4c28fbf1..a66c8dcb5 100644
--- a/__tests__/src/components/WorkspaceControlPanelButtons.test.js
+++ b/__tests__/src/components/WorkspaceControlPanelButtons.test.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import { shallow } from 'enzyme';
+import WorkspaceFullScreenButton from '../../../src/containers/WorkspaceFullScreenButton';
 import WorkspaceControlPanelButtons
   from '../../../src/components/WorkspaceControlPanelButtons';
 
@@ -11,6 +12,6 @@ describe('WorkspaceControlPanelButtons', () => {
 
   it('renders without an error', () => {
     expect(wrapper.find('WithStyles(List)').length).toBe(1);
-    expect(wrapper.find('Connect(WithStyles(WorkspaceFullScreenButton))').length).toBe(1);
+    expect(wrapper.find(WorkspaceFullScreenButton).length).toBe(1);
   });
 });
diff --git a/__tests__/src/components/WorkspaceFullScreenButton.test.js b/__tests__/src/components/WorkspaceFullScreenButton.test.js
index 97e1612c6..4fcb48a6b 100644
--- a/__tests__/src/components/WorkspaceFullScreenButton.test.js
+++ b/__tests__/src/components/WorkspaceFullScreenButton.test.js
@@ -8,7 +8,10 @@ describe('WorkspaceFullScreenButton', () => {
   beforeEach(() => {
     setWorkspaceFullscreen = jest.fn();
     wrapper = shallow(
-      <WorkspaceFullScreenButton classes={{}} setWorkspaceFullscreen={setWorkspaceFullscreen} />,
+      <WorkspaceFullScreenButton
+        classes={{}}
+        setWorkspaceFullscreen={setWorkspaceFullscreen}
+      />,
     ).dive();
   });
 
diff --git a/__tests__/src/components/WorkspaceMenu.test.js b/__tests__/src/components/WorkspaceMenu.test.js
index bc9117a59..27c11729c 100644
--- a/__tests__/src/components/WorkspaceMenu.test.js
+++ b/__tests__/src/components/WorkspaceMenu.test.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import WorkspaceMenu from '../../../src/components/WorkspaceMenu';
+import WindowList from '../../../src/containers/WindowList';
 
 describe('WorkspaceMenu', () => {
   let wrapper;
@@ -23,15 +24,14 @@ describe('WorkspaceMenu', () => {
     it('sets the anchor state', () => {
       wrapper.instance().handleMenuItemClick('windowList', { currentTarget: true });
 
-      expect(wrapper.find('Connect(WindowList)').props().open).toBe(true);
+      expect(wrapper.find(WindowList).props().open).toBe(true);
     });
   });
 
   describe('handleMenuItemClose', () => {
     it('resets the anchor state', () => {
       wrapper.instance().handleMenuItemClose('windowList')();
-
-      expect(wrapper.find('Connect(WindowList)').props().open).toBe(false);
+      expect(wrapper.find(WindowList).props().open).toBe(false);
     });
   });
 });
diff --git a/locales/en/translation.json b/locales/en/translation.json
index d67e188df..3ca92e213 100644
--- a/locales/en/translation.json
+++ b/locales/en/translation.json
@@ -1,3 +1,28 @@
 {
-  "translation": {}
+  "translation": {
+    "aboutThisItem": "About this item",
+    "add": "Add",
+    "bottom": "Bottom",
+    "closeInfoCompanionWindow": "Close information companion window",
+    "closeMenu": "Close Menu",
+    "closeWindow": "Close window",
+    "dark": "Dark",
+    "downloadExport": "Download/Export",
+    "downloadExportWorkspace": "Download/export workspace",
+    "fetchManifest": "Fetch Manifest",
+    "fullScreen": "Full Screen",
+    "light": "Light",
+    "listAllOpenWindows": "List all open windows",
+    "menu": "Menu",
+    "off": "Off",
+    "openInfoCompanionWindow": "Open information companion window",
+    "openWindows": "Open windows",
+    "position": "Position",
+    "right": "Right",
+    "settings": "Settings",
+    "theme": "Theme",
+    "thumbnails": "Thumbnails",
+    "toggleWindowSideBar": "Toggle window sidebar",
+    "untitled": "[Untitled]"
+  }
 }
diff --git a/src/components/ManifestForm.js b/src/components/ManifestForm.js
index 4ea3eeee3..bc9e32513 100644
--- a/src/components/ManifestForm.js
+++ b/src/components/ManifestForm.js
@@ -52,6 +52,7 @@ class ManifestForm extends Component {
    */
   render() {
     const { formValue } = this.state;
+    const { t } = this.props;
     return (
       <form onSubmit={this.formSubmit}>
         <input
@@ -60,7 +61,7 @@ class ManifestForm extends Component {
           type="text"
           onChange={this.handleInputChange}
         />
-        <button id="fetchBtn" type="submit">FetchManifest</button>
+        <button id="fetchBtn" type="submit">{t('fetchManifest')}</button>
       </form>
     );
   }
@@ -69,6 +70,11 @@ class ManifestForm extends Component {
 ManifestForm.propTypes = {
   fetchManifest: PropTypes.func.isRequired,
   setLastRequested: PropTypes.func.isRequired,
+  t: PropTypes.func,
+};
+
+ManifestForm.defaultProps = {
+  t: key => key,
 };
 
 export default ManifestForm;
diff --git a/src/components/WindowList.js b/src/components/WindowList.js
index df0ffdf70..99d5c8fc7 100644
--- a/src/components/WindowList.js
+++ b/src/components/WindowList.js
@@ -13,14 +13,14 @@ class WindowList extends Component {
    * @private
    */
   titleContent(window) {
-    const { manifests } = this.props;
+    const { manifests, t } = this.props;
 
     if (window.manifestId
         && manifests[window.manifestId]
         && manifests[window.manifestId].manifestation) {
       return manifests[window.manifestId].manifestation.getLabel().map(label => label.value)[0];
     }
-    return '[Untitled]';
+    return t('untitled');
   }
 
   /**
@@ -29,13 +29,13 @@ class WindowList extends Component {
    */
   render() {
     const {
-      handleClose, anchorEl, windows, focusWindow,
+      handleClose, anchorEl, windows, focusWindow, t,
     } = this.props;
     return (
       <Menu id="window-list-menu" anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
         <ListSubheader>
-          <Button color="inherit" aria-label="Close Menu" onClick={handleClose} align="right" style={{ float: 'right' }}>&times;</Button>
-          Open windows
+          <Button color="inherit" aria-label={t('closeMenu')} onClick={handleClose} align="right" style={{ float: 'right' }}>&times;</Button>
+          {t('openWindows')}
         </ListSubheader>
         {
           Object.values(windows).map(window => (
@@ -60,10 +60,12 @@ WindowList.propTypes = {
   anchorEl: PropTypes.object, // eslint-disable-line react/forbid-prop-types
   windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
   manifests: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
 };
 
 WindowList.defaultProps = {
   anchorEl: null,
+  t: key => key,
 };
 
 export default WindowList;
diff --git a/src/components/WindowSideBarButtons.js b/src/components/WindowSideBarButtons.js
index bcc05b6cf..122c22dc6 100644
--- a/src/components/WindowSideBarButtons.js
+++ b/src/components/WindowSideBarButtons.js
@@ -23,11 +23,15 @@ class WindowSideBarButtons extends Component {
    * @return {type}  description
    */
   render() {
-    const { toggleWindowSideBarPanel } = this.props;
+    const { toggleWindowSideBarPanel, t } = this.props;
     return (
       <>
         <IconButton
-          aria-label="Open information companion window"
+          aria-label={
+            this.sideBarPanelCurrentlySelected('info')
+              ? t('closeInfoCompanionWindow')
+              : t('openInfoCompanionWindow')
+          }
           onClick={() => (toggleWindowSideBarPanel('info'))}
         >
           <InfoIcon
@@ -42,11 +46,13 @@ class WindowSideBarButtons extends Component {
 WindowSideBarButtons.propTypes = {
   toggleWindowSideBarPanel: PropTypes.func,
   sideBarPanel: PropTypes.string,
+  t: PropTypes.func,
 };
 
 WindowSideBarButtons.defaultProps = {
   toggleWindowSideBarPanel: () => {},
   sideBarPanel: 'closed',
+  t: key => key,
 };
 
 export default WindowSideBarButtons;
diff --git a/src/components/WindowSideBarInfoPanel.js b/src/components/WindowSideBarInfoPanel.js
index 80904da99..aa6467868 100644
--- a/src/components/WindowSideBarInfoPanel.js
+++ b/src/components/WindowSideBarInfoPanel.js
@@ -39,10 +39,10 @@ class WindowSideBarInfoPanel extends Component {
    * @return
    */
   render() {
-    const { classes } = this.props;
+    const { classes, t } = this.props;
     return (
       <div className={ns('window-sidebar-info-panel')}>
-        <Typography variant="h2" className={classes.windowSideBarH2}>About this item</Typography>
+        <Typography variant="h2" className={classes.windowSideBarH2}>{t('aboutThisItem')}</Typography>
         <Typography variant="h3" className={classes.windowSideBarH3}>{this.manifestLabel()}</Typography>
         <Typography variant="body2">{this.manifestDescription()}</Typography>
       </div>
@@ -53,12 +53,14 @@ class WindowSideBarInfoPanel extends Component {
 WindowSideBarInfoPanel.propTypes = {
   classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types
   manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
 };
 
 
 WindowSideBarInfoPanel.defaultProps = {
   classes: {},
   manifest: {},
+  t: key => key,
 };
 
 /**
diff --git a/src/components/WindowSideBarPanel.js b/src/components/WindowSideBarPanel.js
index a34d1622a..2d647b701 100644
--- a/src/components/WindowSideBarPanel.js
+++ b/src/components/WindowSideBarPanel.js
@@ -1,6 +1,6 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import WindowSideBarInfoPanel from './WindowSideBarInfoPanel';
+import WindowSideBarInfoPanel from '../containers/WindowSideBarInfoPanel';
 
 /**
  * WindowSideBarPanel - the panel that pops out from the sidebar
diff --git a/src/components/WindowThumbnailSettings.js b/src/components/WindowThumbnailSettings.js
index e30663e4f..518a74cd2 100644
--- a/src/components/WindowThumbnailSettings.js
+++ b/src/components/WindowThumbnailSettings.js
@@ -34,28 +34,28 @@ export default class WindowThumbnailSettings extends Component {
    * @return {type}  description
    */
   render() {
-    const { thumbnailNavigationPosition } = this.props;
+    const { thumbnailNavigationPosition, t } = this.props;
 
     return (
       <>
-        <Typography>Thumbnails</Typography>
-        <RadioGroup aria-label="position" name="position" value={thumbnailNavigationPosition} onChange={this.handleChange} row>
+        <Typography>{t('thumbnails')}</Typography>
+        <RadioGroup aria-label={t('position')} name="position" value={thumbnailNavigationPosition} onChange={this.handleChange} row>
           <FormControlLabel
             value="off"
             control={<Radio color="primary" icon={<CancelPresentationIcon />} checkedIcon={<CancelPresentationIcon />} />}
-            label="Off"
+            label={t('off')}
             labelPlacement="bottom"
           />
           <FormControlLabel
             value="bottom"
             control={<Radio color="primary" icon={<ThumbnailNavigationBottomIcon />} checkedIcon={<ThumbnailNavigationBottomIcon />} />}
-            label="Bottom"
+            label={t('bottom')}
             labelPlacement="bottom"
           />
           <FormControlLabel
             value="right"
             control={<Radio color="primary" icon={<ThumbnailNavigationRightIcon />} checkedIcon={<ThumbnailNavigationRightIcon />} />}
-            label="Right"
+            label={t('right')}
             labelPlacement="bottom"
           />
         </RadioGroup>
@@ -86,4 +86,8 @@ WindowThumbnailSettings.propTypes = {
   windowId: PropTypes.string.isRequired,
   setWindowThumbnailPosition: PropTypes.func.isRequired,
   thumbnailNavigationPosition: PropTypes.string.isRequired,
+  t: PropTypes.func,
+};
+WindowThumbnailSettings.defaultProps = {
+  t: key => key,
 };
diff --git a/src/components/WindowTopBar.js b/src/components/WindowTopBar.js
index 1f300358d..46a52d0aa 100644
--- a/src/components/WindowTopBar.js
+++ b/src/components/WindowTopBar.js
@@ -8,10 +8,11 @@ import MenuIcon from '@material-ui/icons/Menu';
 import Toolbar from '@material-ui/core/Toolbar';
 import classNames from 'classnames';
 import WindowIcon from '../containers/WindowIcon';
-import WindowTopMenuButton from './WindowTopMenuButton';
+import WindowTopMenuButton from '../containers/WindowTopMenuButton';
 import WindowTopBarButtons from '../containers/WindowTopBarButtons';
 import ns from '../config/css-ns';
 
+
 /**
  * WindowTopBar
  */
@@ -35,12 +36,12 @@ class WindowTopBar extends Component {
    */
   render() {
     const {
-      removeWindow, windowId, classes, toggleWindowSideBar,
+      removeWindow, windowId, classes, toggleWindowSideBar, t,
     } = this.props;
     return (
       <Toolbar disableGutters className={classNames(classes.reallyDense, ns('window-top-bar'))} variant="dense">
         <IconButton
-          aria-label="Open window side bar"
+          aria-label={t('toggleWindowSideBar')}
           color="inherit"
           onClick={() => toggleWindowSideBar(windowId)}
         >
@@ -52,7 +53,7 @@ class WindowTopBar extends Component {
         </Typography>
         <WindowTopBarButtons windowId={windowId} />
         <WindowTopMenuButton className={ns('window-menu-btn')} windowId={windowId} />
-        <Button color="inherit" className={ns('window-close')} aria-label="Close Window" onClick={removeWindow}>&times;</Button>
+        <Button color="inherit" className={ns('window-close')} aria-label={t('closeWindow')} onClick={removeWindow}>&times;</Button>
       </Toolbar>
     );
   }
@@ -64,11 +65,13 @@ WindowTopBar.propTypes = {
   windowId: PropTypes.string.isRequired,
   classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
   toggleWindowSideBar: PropTypes.func,
+  t: PropTypes.func,
 };
 
 WindowTopBar.defaultProps = {
   manifest: null,
   toggleWindowSideBar: () => {},
+  t: key => key,
 };
 
 const styles = {
diff --git a/src/components/WindowTopMenuButton.js b/src/components/WindowTopMenuButton.js
index 3af41ad4d..a70bbb668 100644
--- a/src/components/WindowTopMenuButton.js
+++ b/src/components/WindowTopMenuButton.js
@@ -1,5 +1,4 @@
 import React, { Component } from 'react';
-import { compose } from 'redux';
 import IconButton from '@material-ui/core/IconButton';
 import MoreVertIcon from '@material-ui/icons/MoreVert';
 import { withStyles } from '@material-ui/core/styles';
@@ -44,14 +43,14 @@ class WindowTopMenuButton extends Component {
    * @return
    */
   render() {
-    const { classes, windowId } = this.props;
+    const { classes, t, windowId } = this.props;
     const { anchorEl } = this.state;
 
     return (
       <>
         <IconButton
           color="inherit"
-          aria-label="Menu"
+          aria-label={t('menu')}
           className={classes.ctrlBtn}
           aria-haspopup="true"
           onClick={this.handleMenuClick}
@@ -72,6 +71,11 @@ class WindowTopMenuButton extends Component {
 WindowTopMenuButton.propTypes = {
   windowId: PropTypes.string.isRequired,
   classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
+};
+
+WindowTopMenuButton.defaultProps = {
+  t: key => key,
 };
 
 /**
@@ -83,10 +87,4 @@ const styles = theme => ({
   },
 });
 
-
-const enhance = compose(
-  withStyles(styles),
-  // further HOC go here
-);
-
-export default enhance(WindowTopMenuButton);
+export default withStyles(styles)(WindowTopMenuButton);
diff --git a/src/components/WorkspaceAddButton.js b/src/components/WorkspaceAddButton.js
index 83936f5c0..7e37ef166 100644
--- a/src/components/WorkspaceAddButton.js
+++ b/src/components/WorkspaceAddButton.js
@@ -60,7 +60,7 @@ class WorkspaceAddButton extends Component {
    * @return
    */
   render() {
-    const { classes, manifests } = this.props;
+    const { classes, t, manifests } = this.props;
     const { lastRequested, anchorEl } = this.state;
 
     const manifestList = Object.keys(manifests).map(manifest => (
@@ -76,7 +76,7 @@ class WorkspaceAddButton extends Component {
         <Fab
           color="primary"
           id="addBtn"
-          aria-label="Add"
+          aria-label={t('add')}
           className={classes.fab}
           aria-owns={anchorEl ? 'add-form' : undefined}
           aria-haspopup="true"
@@ -105,6 +105,11 @@ class WorkspaceAddButton extends Component {
 WorkspaceAddButton.propTypes = {
   manifests: PropTypes.instanceOf(Object).isRequired,
   classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
+};
+
+WorkspaceAddButton.defaultProps = {
+  t: key => key,
 };
 
 /**
diff --git a/src/components/WorkspaceControlPanelButtons.js b/src/components/WorkspaceControlPanelButtons.js
index 0122894c5..a9f75a68f 100644
--- a/src/components/WorkspaceControlPanelButtons.js
+++ b/src/components/WorkspaceControlPanelButtons.js
@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
 import List from '@material-ui/core/List';
 import WorkspaceFullScreenButton from '../containers/WorkspaceFullScreenButton';
 import WorkspaceAddButton from '../containers/WorkspaceAddButton';
-import WorkspaceMenuButton from './WorkspaceMenuButton';
+import WorkspaceMenuButton from '../containers/WorkspaceMenuButton';
+
 /**
  *
  */
diff --git a/src/components/WorkspaceExport.js b/src/components/WorkspaceExport.js
index 8bbbb6e50..9407576c5 100644
--- a/src/components/WorkspaceExport.js
+++ b/src/components/WorkspaceExport.js
@@ -26,11 +26,11 @@ class WorkspaceExport extends Component {
    */
   render() {
     const {
-      handleClose, open, children,
+      handleClose, open, children, t,
     } = this.props;
     return (
       <Dialog id="workspace-settings" open={open} onClose={handleClose}>
-        <DialogTitle id="form-dialog-title">Download/Export</DialogTitle>
+        <DialogTitle id="form-dialog-title">{t('downloadExport')}</DialogTitle>
         <DialogContent>
           {children}
           <pre>
@@ -47,11 +47,13 @@ WorkspaceExport.propTypes = {
   open: PropTypes.bool, // eslint-disable-line react/forbid-prop-types
   children: PropTypes.node,
   state: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
 };
 
 WorkspaceExport.defaultProps = {
   open: false,
   children: null,
+  t: key => key,
 };
 
 export default WorkspaceExport;
diff --git a/src/components/WorkspaceFullScreenButton.js b/src/components/WorkspaceFullScreenButton.js
index 2c6a3685b..96bac8ab1 100644
--- a/src/components/WorkspaceFullScreenButton.js
+++ b/src/components/WorkspaceFullScreenButton.js
@@ -13,10 +13,10 @@ class WorkspaceFullScreenButton extends Component {
    * @return
    */
   render() {
-    const { classes, setWorkspaceFullscreen } = this.props;
+    const { classes, setWorkspaceFullscreen, t } = this.props;
     return (
       <ListItem>
-        <IconButton className={classes.ctrlBtn} aria-label="Full Screen" onClick={() => setWorkspaceFullscreen(true)}>
+        <IconButton className={classes.ctrlBtn} aria-label={t('fullScreen')} onClick={() => setWorkspaceFullscreen(true)}>
           <FullscreenIcon />
         </IconButton>
       </ListItem>
@@ -27,6 +27,11 @@ class WorkspaceFullScreenButton extends Component {
 WorkspaceFullScreenButton.propTypes = {
   setWorkspaceFullscreen: PropTypes.func.isRequired,
   classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
+};
+
+WorkspaceFullScreenButton.defaultProps = {
+  t: key => key,
 };
 
 /**
diff --git a/src/components/WorkspaceMenu.js b/src/components/WorkspaceMenu.js
index bcb016dab..9f37eff02 100644
--- a/src/components/WorkspaceMenu.js
+++ b/src/components/WorkspaceMenu.js
@@ -58,7 +58,7 @@ class WorkspaceMenu extends Component {
    * @return
    */
   render() {
-    const { handleClose, anchorEl } = this.props;
+    const { handleClose, anchorEl, t } = this.props;
     const { windowList, settings, exportWorkspace } = this.state;
 
     return (
@@ -72,7 +72,7 @@ class WorkspaceMenu extends Component {
             <ListItemIcon>
               <ViewHeadlineIcon />
             </ListItemIcon>
-            <Typography varient="inherit">List all open windows</Typography>
+            <Typography varient="inherit">{t('listAllOpenWindows')}</Typography>
           </MenuItem>
           <Divider />
           <MenuItem
@@ -83,7 +83,7 @@ class WorkspaceMenu extends Component {
             <ListItemIcon>
               <SettingsIcon />
             </ListItemIcon>
-            <Typography varient="inherit">Settings</Typography>
+            <Typography varient="inherit">{t('settings')}</Typography>
           </MenuItem>
           <MenuItem
             aria-haspopup="true"
@@ -93,7 +93,7 @@ class WorkspaceMenu extends Component {
             <ListItemIcon>
               <SaveAltIcon />
             </ListItemIcon>
-            <Typography varient="inherit">Download/export workspace</Typography>
+            <Typography varient="inherit">{t('downloadExportWorkspace')}</Typography>
           </MenuItem>
         </Menu>
         <WindowList
@@ -117,10 +117,12 @@ class WorkspaceMenu extends Component {
 WorkspaceMenu.propTypes = {
   handleClose: PropTypes.func.isRequired,
   anchorEl: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
 };
 
 WorkspaceMenu.defaultProps = {
   anchorEl: null,
+  t: key => key,
 };
 
 export default WorkspaceMenu;
diff --git a/src/components/WorkspaceMenuButton.js b/src/components/WorkspaceMenuButton.js
index 60631b815..913acac4a 100644
--- a/src/components/WorkspaceMenuButton.js
+++ b/src/components/WorkspaceMenuButton.js
@@ -4,7 +4,7 @@ import MenuIcon from '@material-ui/icons/Menu';
 import ListItem from '@material-ui/core/ListItem';
 import { withStyles } from '@material-ui/core/styles';
 import PropTypes from 'prop-types';
-import WorkspaceMenu from './WorkspaceMenu';
+import WorkspaceMenu from '../containers/WorkspaceMenu';
 
 /**
  */
@@ -44,7 +44,7 @@ class WorkspaceMenuButton extends Component {
    * @return
    */
   render() {
-    const { classes } = this.props;
+    const { classes, t } = this.props;
     const { anchorEl } = this.state;
 
     return (
@@ -53,7 +53,7 @@ class WorkspaceMenuButton extends Component {
           <IconButton
             color="default"
             id="menuBtn"
-            aria-label="Menu"
+            aria-label={t('menu')}
             className={classes.ctrlBtn}
             aria-haspopup="true"
             onClick={this.handleMenuClick}
@@ -73,6 +73,11 @@ class WorkspaceMenuButton extends Component {
 
 WorkspaceMenuButton.propTypes = {
   classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
+};
+
+WorkspaceMenuButton.defaultProps = {
+  t: key => key,
 };
 
 /**
diff --git a/src/components/WorkspaceSettings.js b/src/components/WorkspaceSettings.js
index fbd7c3f2c..bf84a8f55 100644
--- a/src/components/WorkspaceSettings.js
+++ b/src/components/WorkspaceSettings.js
@@ -34,15 +34,15 @@ class WorkspaceSettings extends Component {
    */
   render() {
     const {
-      handleClose, open, children, theme,
+      handleClose, open, children, theme, t,
     } = this.props;
     return (
       <Dialog id="workspace-settings" open={open} onClose={handleClose}>
-        <DialogTitle id="form-dialog-title">Settings</DialogTitle>
+        <DialogTitle id="form-dialog-title">{t('settings')}</DialogTitle>
         <DialogContent>
           {children}
           <FormControl>
-            <InputLabel htmlFor="theme-simple">Theme</InputLabel>
+            <InputLabel htmlFor="theme-simple">{t('theme')}</InputLabel>
             <Select
               value={theme}
               onChange={this.handleThemeChange}
@@ -51,8 +51,8 @@ class WorkspaceSettings extends Component {
                 id: 'theme-simple',
               }}
             >
-              <MenuItem value="light">Light</MenuItem>
-              <MenuItem value="dark">Dark</MenuItem>
+              <MenuItem value="light">{t('light')}</MenuItem>
+              <MenuItem value="dark">{t('dark')}</MenuItem>
             </Select>
           </FormControl>
         </DialogContent>
@@ -67,11 +67,13 @@ WorkspaceSettings.propTypes = {
   children: PropTypes.node,
   updateConfig: PropTypes.func.isRequired,
   theme: PropTypes.string.isRequired,
+  t: PropTypes.func,
 };
 
 WorkspaceSettings.defaultProps = {
   open: false,
   children: null,
+  t: key => key,
 };
 
 export default WorkspaceSettings;
diff --git a/src/containers/ManifestForm.js b/src/containers/ManifestForm.js
index 2f47b8cba..baae7e3c2 100644
--- a/src/containers/ManifestForm.js
+++ b/src/containers/ManifestForm.js
@@ -1,4 +1,6 @@
 import { connect } from 'react-redux';
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
 import * as actions from '../state/actions';
 import ManifestForm from '../components/ManifestForm';
 
@@ -9,5 +11,10 @@ import ManifestForm from '../components/ManifestForm';
  */
 const mapDispatchToProps = { fetchManifest: actions.fetchManifest };
 
+const enhance = compose(
+  connect(null, mapDispatchToProps),
+  withNamespaces(),
+  // further HOC go here
+);
 
-export default connect(null, mapDispatchToProps)(ManifestForm);
+export default enhance(ManifestForm);
diff --git a/src/containers/WindowList.js b/src/containers/WindowList.js
index 2350e6831..86814d210 100644
--- a/src/containers/WindowList.js
+++ b/src/containers/WindowList.js
@@ -1,4 +1,6 @@
 import { connect } from 'react-redux';
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
 import * as actions from '../state/actions';
 import WindowList from '../components/WindowList';
 
@@ -23,4 +25,10 @@ const mapStateToProps = state => (
   }
 );
 
-export default connect(mapStateToProps, mapDispatchToProps)(WindowList);
+const enhance = compose(
+  connect(mapStateToProps, mapDispatchToProps),
+  withNamespaces(),
+  // further HOC
+);
+
+export default enhance(WindowList);
diff --git a/src/containers/WindowSideBarButtons.js b/src/containers/WindowSideBarButtons.js
index 93a58e23f..e4a652b34 100644
--- a/src/containers/WindowSideBarButtons.js
+++ b/src/containers/WindowSideBarButtons.js
@@ -1,5 +1,6 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
+import { withNamespaces } from 'react-i18next';
 import * as actions from '../state/actions';
 import miradorWithPlugins from '../lib/miradorWithPlugins';
 import WindowSideBarButtons from '../components/WindowSideBarButtons';
@@ -19,6 +20,7 @@ const mapDispatchToProps = (dispatch, props) => ({
 const enhance = compose(
   connect(null, mapDispatchToProps),
   miradorWithPlugins,
+  withNamespaces(),
   // further HOC go here
 );
 
diff --git a/src/containers/WindowSideBarInfoPanel.js b/src/containers/WindowSideBarInfoPanel.js
new file mode 100644
index 000000000..0e2434944
--- /dev/null
+++ b/src/containers/WindowSideBarInfoPanel.js
@@ -0,0 +1,12 @@
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
+import WindowSideBarInfoPanel from '../components/WindowSideBarInfoPanel';
+
+const enhance = compose(
+  withNamespaces(),
+  miradorWithPlugins,
+  // further HOC
+);
+
+export default enhance(WindowSideBarInfoPanel);
diff --git a/src/containers/WindowThumbnailSettings.js b/src/containers/WindowThumbnailSettings.js
index c50b45016..a0463090f 100644
--- a/src/containers/WindowThumbnailSettings.js
+++ b/src/containers/WindowThumbnailSettings.js
@@ -1,5 +1,7 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
 import * as actions from '../state/actions';
 import { getThumbnailNavigationPosition } from '../state/selectors';
 import WindowThumbnailSettings from '../components/WindowThumbnailSettings';
@@ -24,6 +26,8 @@ const mapStateToProps = (state, props) => (
 
 const enhance = compose(
   connect(mapStateToProps, mapDispatchToProps),
+  withNamespaces(),
+  miradorWithPlugins,
   // further HOC go here
 );
 
diff --git a/src/containers/WindowTopBar.js b/src/containers/WindowTopBar.js
index 2f1a0bbda..0bc54458a 100644
--- a/src/containers/WindowTopBar.js
+++ b/src/containers/WindowTopBar.js
@@ -1,5 +1,6 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
+import { withNamespaces } from 'react-i18next';
 import * as actions from '../state/actions';
 import miradorWithPlugins from '../lib/miradorWithPlugins';
 import WindowTopBar from '../components/WindowTopBar';
@@ -17,6 +18,7 @@ const mapDispatchToProps = (dispatch, props) => ({
 const enhance = compose(
   connect(null, mapDispatchToProps),
   miradorWithPlugins,
+  withNamespaces(),
   // further HOC go here
 );
 
diff --git a/src/containers/WindowTopMenuButton.js b/src/containers/WindowTopMenuButton.js
new file mode 100644
index 000000000..ebfedfeb3
--- /dev/null
+++ b/src/containers/WindowTopMenuButton.js
@@ -0,0 +1,12 @@
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
+import WindowTopMenuButton from '../components/WindowTopMenuButton';
+
+const enhance = compose(
+  withNamespaces(),
+  miradorWithPlugins,
+  // further HOC
+);
+
+export default enhance(WindowTopMenuButton);
diff --git a/src/containers/WorkspaceAddButton.js b/src/containers/WorkspaceAddButton.js
index b9ed10040..d7faf8218 100644
--- a/src/containers/WorkspaceAddButton.js
+++ b/src/containers/WorkspaceAddButton.js
@@ -1,4 +1,7 @@
 import { connect } from 'react-redux';
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
 import WorkspaceAddButton from '../components/WorkspaceAddButton';
 
 /**
@@ -12,4 +15,10 @@ const mapStateToProps = state => (
   }
 );
 
-export default connect(mapStateToProps)(WorkspaceAddButton);
+const enhance = compose(
+  connect(mapStateToProps),
+  withNamespaces(),
+  miradorWithPlugins,
+);
+
+export default enhance(WorkspaceAddButton);
diff --git a/src/containers/WorkspaceExport.js b/src/containers/WorkspaceExport.js
index a90a4670f..82dded070 100644
--- a/src/containers/WorkspaceExport.js
+++ b/src/containers/WorkspaceExport.js
@@ -1,5 +1,7 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
 import WorkspaceExport from '../components/WorkspaceExport';
 
 /**
@@ -11,6 +13,8 @@ const mapStateToProps = state => ({ state });
 
 const enhance = compose(
   connect(mapStateToProps, {}),
+  miradorWithPlugins,
+  withNamespaces(),
   // further HOC go here
 );
 
diff --git a/src/containers/WorkspaceFullScreenButton.js b/src/containers/WorkspaceFullScreenButton.js
index 60ccaa92c..64f9bd732 100644
--- a/src/containers/WorkspaceFullScreenButton.js
+++ b/src/containers/WorkspaceFullScreenButton.js
@@ -1,4 +1,7 @@
 import { connect } from 'react-redux';
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
 import * as actions from '../state/actions';
 import WorkspaceFullScreenButton
   from '../components/WorkspaceFullScreenButton';
@@ -10,4 +13,11 @@ import WorkspaceFullScreenButton
  */
 const mapDispatchToProps = { setWorkspaceFullscreen: actions.setWorkspaceFullscreen };
 
-export default connect(null, mapDispatchToProps)(WorkspaceFullScreenButton);
+const enhance = compose(
+  connect(null, mapDispatchToProps),
+  withNamespaces(),
+  miradorWithPlugins,
+  // further HOC
+);
+
+export default enhance(WorkspaceFullScreenButton);
diff --git a/src/containers/WorkspaceMenu.js b/src/containers/WorkspaceMenu.js
new file mode 100644
index 000000000..866358d4f
--- /dev/null
+++ b/src/containers/WorkspaceMenu.js
@@ -0,0 +1,12 @@
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
+import WorkspaceMenu from '../components/WorkspaceMenu';
+
+const enhance = compose(
+  withNamespaces(),
+  miradorWithPlugins,
+  // further HOC
+);
+
+export default enhance(WorkspaceMenu);
diff --git a/src/containers/WorkspaceMenuButton.js b/src/containers/WorkspaceMenuButton.js
new file mode 100644
index 000000000..0ccd4218f
--- /dev/null
+++ b/src/containers/WorkspaceMenuButton.js
@@ -0,0 +1,12 @@
+import { compose } from 'redux';
+import { withNamespaces } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
+import WorkspaceMenuButton from '../components/WorkspaceMenuButton';
+
+const enhance = compose(
+  withNamespaces(),
+  miradorWithPlugins,
+  // further HOC
+);
+
+export default enhance(WorkspaceMenuButton);
diff --git a/src/containers/WorkspaceSettings.js b/src/containers/WorkspaceSettings.js
index a52b46300..50b697135 100644
--- a/src/containers/WorkspaceSettings.js
+++ b/src/containers/WorkspaceSettings.js
@@ -1,5 +1,6 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
+import { withNamespaces } from 'react-i18next';
 import WorkspaceSettings from '../components/WorkspaceSettings';
 import * as actions from '../state/actions';
 
@@ -25,6 +26,7 @@ const mapStateToProps = state => (
 
 const enhance = compose(
   connect(mapStateToProps, mapDispatchToProps),
+  withNamespaces(),
   // further HOC go here
 );
 
-- 
GitLab