diff --git a/__tests__/integration/mirador/index.html b/__tests__/integration/mirador/index.html
index 5c097b29396425ee4ea3f7b69922b2546a291d6a..0d1d65e24fa85abd8f7eb23399e921b323f83fa8 100644
--- a/__tests__/integration/mirador/index.html
+++ b/__tests__/integration/mirador/index.html
@@ -17,7 +17,7 @@
          canvasIndex: 2,
        },
        {
-         loadedManifest: 'https://media.nga.gov/public/manifests/nga_highlights.json',
+         loadedManifest: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json',
          thumbnailNavigationPosition: 'off',
        }]
      });
diff --git a/__tests__/src/components/App.test.js b/__tests__/src/components/App.test.js
index 145241c5e9499b3ffb0a7aac8a51a78e22fd59a0..258bc47b49d9e4de45e91dbd991d4db1a21ae34b 100644
--- a/__tests__/src/components/App.test.js
+++ b/__tests__/src/components/App.test.js
@@ -1,29 +1,52 @@
 import React from 'react';
 import { shallow } from 'enzyme';
+import { MuiThemeProvider } from '@material-ui/core/styles';
+import Fullscreen from 'react-fullscreen-crossbrowser';
+import WorkspaceControlPanel from '../../../src/components/WorkspaceControlPanel';
+import Workspace from '../../../src/containers/Workspace';
 import App from '../../../src/components/App';
 
+/** */
+function createWrapper(props) {
+  return shallow(
+    <App
+      isFullscreenEnabled={false}
+      setWorkspaceFullscreen={() => {}}
+      theme="light"
+      classes={{}}
+      {...props}
+    />,
+  ).dive(); // to unwrapp HOC created by withStyle()
+}
+
 describe('App', () => {
-  it('renders without an error', () => {
-    const wrapper = shallow(
-      <App
-        manifests={[]}
-        workspace={{}}
-        config={{ theme: 'light' }}
-      />,
-    );
-    expect(wrapper.dive().find('div.mirador-app').length).toBe(1);
+  it('should render outer element correctly', () => {
+    const wrapper = createWrapper();
+    expect(wrapper.find('div.mirador-app').length).toBe(1);
+  });
+
+  it('should render all needed elements ', () => {
+    const wrapper = createWrapper();
+    expect(wrapper.find(MuiThemeProvider).length).toBe(1);
+    expect(wrapper.find(Fullscreen).length).toBe(1);
+    expect(wrapper.find(Workspace).length).toBe(1);
+    expect(wrapper.find(WorkspaceControlPanel).length).toBe(1);
   });
 
-  describe('FullScreen', () => {
-    it('is enabled by the workspace.fullscreen state', () => {
-      const wrapper = shallow(
-        <App
-          manifests={[]}
-          workspace={{ isFullscreenEnabled: true }}
-          config={{ theme: 'light' }}
-        />,
-      );
-      expect(wrapper.dive().find('FullScreen').first().prop('enabled')).toEqual(true);
-    });
+  it('should pass setWorkspaceFullscreen to Fullscreen.onChange', () => {
+    const mockFn = jest.fn();
+    const wrapper = createWrapper({ setWorkspaceFullscreen: mockFn });
+    expect(wrapper.find(Fullscreen).first().prop('onChange'))
+      .toBe(mockFn);
+  });
+
+  it('should pass isFullscreenEnabled to Fullscreen.enabled', () => {
+    let wrapper = createWrapper({ isFullscreenEnabled: false });
+    expect(wrapper.find(Fullscreen).first().prop('enabled'))
+      .toEqual(false);
+
+    wrapper = createWrapper({ isFullscreenEnabled: true });
+    expect(wrapper.find(Fullscreen).first().prop('enabled'))
+      .toEqual(true);
   });
 });
diff --git a/__tests__/src/components/ThumbnailNavigation.test.js b/__tests__/src/components/ThumbnailNavigation.test.js
index a702eeafa84f20f8e960c14aaca18f19df982897..16266e59003bab8f59620f905564bdd6a59695df 100644
--- a/__tests__/src/components/ThumbnailNavigation.test.js
+++ b/__tests__/src/components/ThumbnailNavigation.test.js
@@ -19,11 +19,9 @@ describe('ThumbnailNavigation', () => {
     );
     wrapper = shallow(
       <ThumbnailNavigation
-        manifest={{
-          id: 'http://foo',
-          manifestation: manifesto.create(manifestJson),
-          isFetching: false,
-        }}
+        canvases={
+          manifesto.create(manifestJson).getSequences()[0].getCanvases()
+        }
         window={{
           id: 'foobar',
           canvasIndex: 1,
diff --git a/__tests__/src/components/WindowIcon.test.js b/__tests__/src/components/WindowIcon.test.js
index d5a209d73edf42cffb9d4729b23ced59b1230883..93a26f5f58a47466e0f039739874992d914e24df 100644
--- a/__tests__/src/components/WindowIcon.test.js
+++ b/__tests__/src/components/WindowIcon.test.js
@@ -2,40 +2,27 @@ import React from 'react';
 import { shallow } from 'enzyme';
 import WindowIcon from '../../../src/components/WindowIcon';
 
-describe('WindowIcon', () => {
-  let wrapper;
-  let manifestation;
-
-  describe('without a manifestation', () => {
-    beforeEach(() => {
-      wrapper = shallow(<WindowIcon />).dive();
-    });
-
-    it('renders without an error', () => {
-      expect(wrapper.find('img').length).toBe(0);
-    });
-  });
+/** createWrapper */
+function createWrapper(props) {
+  return shallow(
+    <WindowIcon
+      manifestLogo=""
+      classses={{}}
+      {...props}
+    />,
+  ).dive(); // to unwrap HOC created by withStyles();
+}
 
-
-  describe('with a manifestation without a logo', () => {
-    beforeEach(() => {
-      manifestation = { getLogo: () => null };
-      wrapper = shallow(<WindowIcon manifestation={manifestation} />).dive();
-    });
-
-    it('renders without an error', () => {
-      expect(wrapper.find('img').length).toBe(0);
-    });
+describe('WindowIcon', () => {
+  it('should render nothing if no manifest logo given', () => {
+    const wrapper = createWrapper();
+    expect(wrapper.find('img').length).toBe(0);
   });
 
-  describe('with a manifestation with a logo', () => {
-    beforeEach(() => {
-      manifestation = { getLogo: () => 'http://example.com/thumbnail.jpg' };
-      wrapper = shallow(<WindowIcon manifestation={manifestation} classes={{ logo: 'logo-class' }} />).dive();
-    });
-
-    it('renders without an error', () => {
-      expect(wrapper.find('img.logo-class[src="http://example.com/thumbnail.jpg"]').length).toBe(1);
-    });
+  it('should render logo if manifest logo is given', () => {
+    const manifestLogo = 'http://foo.bar';
+    const wrapper = createWrapper({ manifestLogo });
+    expect(wrapper.find('img').first().prop('src'))
+      .toEqual(manifestLogo);
   });
 });
diff --git a/__tests__/src/components/WindowList.test.js b/__tests__/src/components/WindowList.test.js
index 896dd2aa64e23c0f18d7ff4f1c5169536dff97af..0bc7a7e143d131c70a935633b51c503bba976f89 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 0ffde2da597f94e00f98d9e6751c72681307bb5f..b57978af86b66edb99bb61010868908957f6e9ad 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 e74a3e79929e997a818a56538cf1402fbf66c676..21c6fabc32f12d3076bea7c6d4862de33376c7fa 100644
--- a/__tests__/src/components/WindowSideBarInfoPanel.test.js
+++ b/__tests__/src/components/WindowSideBarInfoPanel.test.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import { shallow } from 'enzyme';
+import Typography from '@material-ui/core/Typography';
 import createStore from '../../../src/state/createStore';
 import * as actions from '../../../src/state/actions';
 import WindowSideBarInfoPanel from '../../../src/components/WindowSideBarInfoPanel';
@@ -13,12 +14,28 @@ describe('WindowSideBarInfoPanel', () => {
   beforeEach(() => {
     store.dispatch(actions.receiveManifest('foo', fixture));
     manifest = store.getState().manifests.foo;
-    wrapper = shallow(<WindowSideBarInfoPanel manifest={manifest} />);
+    wrapper = shallow(
+      <WindowSideBarInfoPanel manifest={manifest} />,
+    ).dive();
   });
 
   it('renders without an error', () => {
-    expect(wrapper.find('h2').text()).toBe('About this item');
-    expect(wrapper.find('h3').text()).toBe('Bodleian Library Human Freaks 2 (33)');
-    expect(wrapper.find('.mirador-window-sidebar-info-panel div').text()).toBe('[Handbill of Mr. Becket, [1787] ]');
+    expect(
+      wrapper.find('WithStyles(Typography)[variant="h2"]').first().matchesElement(
+        <Typography>aboutThisItem</Typography>,
+      ),
+    ).toBe(true);
+
+    expect(
+      wrapper.find('WithStyles(Typography)[variant="h3"]').first().matchesElement(
+        <Typography>Bodleian Library Human Freaks 2 (33)</Typography>,
+      ),
+    ).toBe(true);
+
+    expect(
+      wrapper.find('WithStyles(Typography)[variant="body2"]').first().matchesElement(
+        <Typography>[Handbill of Mr. Becket, [1787] ]</Typography>,
+      ),
+    ).toBe(true);
   });
 });
diff --git a/__tests__/src/components/WindowSideBarPanel.test.js b/__tests__/src/components/WindowSideBarPanel.test.js
index 1478f900e36add28440a0f2a47a87ea14f07c015..f31c05019910c90a36cb66ede06d229e6d24a5f7 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('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('WindowSideBarInfoPanel').length).toBe(0);
+      expect(wrapper.find(WindowSideBarInfoPanel).length).toBe(0);
     });
   });
 });
diff --git a/__tests__/src/components/WindowThumbnailSettings.test.js b/__tests__/src/components/WindowThumbnailSettings.test.js
index 6453d313cf205b5511c9bb6c95ad00ecb8da5379..7b835555a7e0c9c24a1b2bf37e0aa743c27030d7 100644
--- a/__tests__/src/components/WindowThumbnailSettings.test.js
+++ b/__tests__/src/components/WindowThumbnailSettings.test.js
@@ -1,27 +1,47 @@
 import React from 'react';
 import { shallow } from 'enzyme';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import RadioGroup from '@material-ui/core/RadioGroup';
+import Typography from '@material-ui/core/Typography';
 import WindowThumbnailSettings from '../../../src/components/WindowThumbnailSettings';
 
+/** create wrapper */
+function createWrapper(props) {
+  return shallow(
+    <WindowThumbnailSettings
+      windowId="xyz"
+      setWindowThumbnailPosition={() => {}}
+      thumbnailNavigationPosition="off"
+      {...props}
+    />,
+  );
+}
+
 describe('WindowThumbnailSettings', () => {
-  let wrapper;
-  const setWindowThumbnailPosition = jest.fn();
-  beforeEach(() => {
-    wrapper = shallow(
-      <WindowThumbnailSettings
-        windowId="xyz"
-        setWindowThumbnailPosition={setWindowThumbnailPosition}
-        thumbnailNavigationPosition="bottom"
-      />,
-    );
+  it('renders all elements correctly', () => {
+    const wrapper = createWrapper();
+    expect(wrapper.find(Typography).length).toBe(1);
+    expect(wrapper.find(RadioGroup).length).toBe(1);
+    const labels = wrapper.find(FormControlLabel);
+    expect(labels.length).toBe(3);
+    expect(labels.at(0).props().value).toBe('off');
+    expect(labels.at(1).props().value).toBe('bottom');
+    expect(labels.at(2).props().value).toBe('right');
   });
 
-  it('renders without an error', () => {
-    expect(wrapper.find('WithStyles(Typography)').dive().dive().text()).toBe('Thumbnails');
-    expect(wrapper.find('RadioGroup').props().value).toBe('bottom');
+  it('should set the correct label active', () => {
+    let wrapper = createWrapper({ thumbnailNavigationPosition: 'bottom' });
+    expect(wrapper.find(RadioGroup).props().value).toBe('bottom');
+    wrapper = createWrapper({ thumbnailNavigationPosition: 'right' });
+    expect(wrapper.find(RadioGroup).props().value).toBe('right');
   });
 
   it('updates state when the thumbnail config selection changes', () => {
-    wrapper.find('RadioGroup').first().simulate('change', { target: { value: 'off' } });
+    const setWindowThumbnailPosition = jest.fn();
+    const wrapper = createWrapper({ setWindowThumbnailPosition });
+    wrapper.find(RadioGroup).first().simulate('change', { target: { value: 'off' } });
     expect(setWindowThumbnailPosition).toHaveBeenCalledWith('xyz', 'off');
+    wrapper.find(RadioGroup).first().simulate('change', { target: { value: 'right' } });
+    expect(setWindowThumbnailPosition).toHaveBeenCalledWith('xyz', 'right');
   });
 });
diff --git a/__tests__/src/components/WindowTopBar.test.js b/__tests__/src/components/WindowTopBar.test.js
index 250249f37af3c547cf540201c7d912e525662eda..86cc567291eeb31e5d1136fd1663c6109df3e5d8 100644
--- a/__tests__/src/components/WindowTopBar.test.js
+++ b/__tests__/src/components/WindowTopBar.test.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import WindowTopBar from '../../../src/components/WindowTopBar';
+import WindowIcon from '../../../src/containers/WindowIcon';
 
 const manifestFixture = {
   manifestation: {
@@ -23,6 +24,7 @@ describe('WindowTopBar', () => {
         removeWindow={mockRemoveWindow}
         toggleWindowSideBar={mockToggleWindowSideBar}
         classes={{}}
+        t={key => key}
       />,
     ).dive();
   });
@@ -37,7 +39,7 @@ describe('WindowTopBar', () => {
   });
 
   it('renders a window icon', () => {
-    expect(topBar.find('WithStyles(WindowIcon)').length).toBe(1);
+    expect(topBar.find(WindowIcon).length).toBe(1);
   });
 
   it('calls the toggleWindowSideBar prop when the menu IconButton is clicked', () => {
diff --git a/__tests__/src/components/WindowTopMenu.test.js b/__tests__/src/components/WindowTopMenu.test.js
index 47851603c78bd7d8081aaa8c8192344d094aae13..d2b260854166e6c87d7790af4e1b1efba1dbf29f 100644
--- a/__tests__/src/components/WindowTopMenu.test.js
+++ b/__tests__/src/components/WindowTopMenu.test.js
@@ -1,17 +1,52 @@
 import React from 'react';
 import { shallow } from 'enzyme';
+import ListItem from '@material-ui/core/ListItem';
+import Menu from '@material-ui/core/Menu';
+import Divider from '@material-ui/core/Divider';
+import WindowThumbnailSettings from '../../../src/containers/WindowThumbnailSettings';
 import WindowTopMenu from '../../../src/components/WindowTopMenu';
 
+/** create wrapper */
+function createWrapper(props) {
+  return shallow(
+    <WindowTopMenu
+      windowId="xyz"
+      handleClose={() => {}}
+      anchorEl={null}
+      {...props}
+    />,
+  );
+}
+
 describe('WindowTopMenu', () => {
-  let wrapper;
-  let handleClose;
-  beforeEach(() => {
-    handleClose = jest.fn();
-    wrapper = shallow(<WindowTopMenu windowId="xyz" handleClose={handleClose} />).dive();
+  it('renders all needed elements', () => {
+    const wrapper = createWrapper();
+    expect(wrapper.find(Menu).length).toBe(1);
+    expect(wrapper.find(ListItem).length).toBe(1);
+    expect(wrapper.find(WindowThumbnailSettings).length).toBe(1);
+    expect(wrapper.find(Divider).length).toBe(1);
+  });
+
+  it('passes windowId to <WindowThumbnailSettings/>', () => {
+    const wrapper = createWrapper();
+    expect(wrapper.find(WindowThumbnailSettings)
+      .first().props().windowId).toBe('xyz');
+  });
+
+  it('passses correct props to <Menu/> when no achor element given', () => {
+    const handleClose = jest.fn();
+    const wrapper = createWrapper({ handleClose });
+    expect(wrapper.find(Menu).first().props().anchorEl).toBe(null);
+    expect(wrapper.find(Menu).first().props().open).toBe(false);
+    expect(wrapper.find(Menu).first().props().onClose).toBe(handleClose);
   });
 
-  it('renders without an error', () => {
-    expect(wrapper.find('WithStyles(Menu)').length).toBe(1);
-    expect(wrapper.find('Connect(WindowThumbnailSettings)').length).toBe(1);
+  it('passses correct props to <Menu/> when no achor element given', () => {
+    const handleClose = jest.fn();
+    const anchorEl = {};
+    const wrapper = createWrapper({ anchorEl, handleClose });
+    expect(wrapper.find(Menu).first().props().anchorEl).toBe(anchorEl);
+    expect(wrapper.find(Menu).first().props().open).toBe(true);
+    expect(wrapper.find(Menu).first().props().onClose).toBe(handleClose);
   });
 });
diff --git a/__tests__/src/components/WindowTopMenuButton.test.js b/__tests__/src/components/WindowTopMenuButton.test.js
index c016c58da0146cf137c8b53a7c0b93e8f472f78a..e36db1c72fe205f6fdfebfb259892bcf4fa212f7 100644
--- a/__tests__/src/components/WindowTopMenuButton.test.js
+++ b/__tests__/src/components/WindowTopMenuButton.test.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import WindowTopMenuButton from '../../../src/components/WindowTopMenuButton';
+import WindowTopMenu from '../../../src/containers/WindowTopMenu';
 
 describe('WindowTopMenuButton', () => {
   let wrapper;
@@ -15,6 +16,6 @@ describe('WindowTopMenuButton', () => {
   });
   it('when clicked, updates the state', () => {
     wrapper.find('WithStyles(IconButton)').simulate('click', { currentTarget: 'x' });
-    expect(wrapper.find('Connect(miradorWithPlugins(WithStyles(WindowTopMenu)))').props().anchorEl).toBe('x');
+    expect(wrapper.find(WindowTopMenu).props().anchorEl).toBe('x');
   });
 });
diff --git a/__tests__/src/components/WorkspaceControlPanelButtons.test.js b/__tests__/src/components/WorkspaceControlPanelButtons.test.js
index c4c28fbf164d354c9fcbbbab76c607a446f1b66e..a66c8dcb5c7b0d7b37fdc4d746d0421d700435d0 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 97e1612c65a8109334c5b49bee49b3ae71f58dc8..4fcb48a6bf5d0ecf5ecf3a4b68b2f836ba067fe0 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 bc9117a59a87c798963677c9225ec31d8c9574fc..27c11729c9568368cfb7881ae3c51cbfebe2745c 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/__tests__/src/selectors/index.test.js b/__tests__/src/selectors/index.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..f3b3d48e832b087e0014e6e361accd2a5b10f9d4
--- /dev/null
+++ b/__tests__/src/selectors/index.test.js
@@ -0,0 +1,96 @@
+import manifesto from 'manifesto.js';
+import manifestFixture from '../../fixtures/version-2/001.json';
+import {
+  getWindowManifest,
+  getManifestLogo,
+  getManifestCanvases,
+  getThumbnailNavigationPosition,
+} from '../../../src/state/selectors';
+
+
+describe('getWindowManifest()', () => {
+  const state = {
+    windows: {
+      a: { id: 'a', manifestId: 'x' },
+      b: { id: 'b', manifestId: 'y' },
+      c: { id: 'c' },
+    },
+    manifests: {
+      x: { id: 'x' },
+    },
+  };
+
+  it('should return the manifest of a certain window', () => {
+    const received = getWindowManifest(state, 'a');
+    const expected = { id: 'x' };
+    expect(received).toEqual(expected);
+  });
+
+  it('should return undefined if window doesnt exist', () => {
+    const received = getWindowManifest(state, 'unknown');
+    expect(received).toBeUndefined();
+  });
+
+  it('should return undefined if window has no manifest id', () => {
+    const received = getWindowManifest(state, 'c');
+    expect(received).toBeUndefined();
+  });
+
+  it('should return undefined if manifest does not exist', () => {
+    const received = getWindowManifest(state, 'b');
+    expect(received).toBeUndefined();
+  });
+});
+
+describe('getManifestLogo()', () => {
+  it('should return manifest logo id', () => {
+    const manifest = { manifestation: manifesto.create(manifestFixture) };
+    const received = getManifestLogo(manifest);
+    expect(received).toEqual(manifestFixture.logo['@id']);
+  });
+
+  it('should return null if manifest has no logo', () => {
+    const manifest = { manifestation: manifesto.create({}) };
+    const received = getManifestLogo(manifest);
+    expect(received).toBeNull();
+  });
+});
+
+describe('getManifestCanvases', () => {
+  it('returns an empty array if the manifestation is not loaded', () => {
+    const manifest = {};
+    const received = getManifestCanvases(manifest);
+    expect(received).toEqual([]);
+  });
+
+  it('returns canvases from the manifest', () => {
+    const manifest = { manifestation: manifesto.create(manifestFixture) };
+    const received = getManifestCanvases(manifest);
+    expect(received.length).toBe(1);
+    expect(received[0].id).toBe('https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json');
+  });
+});
+
+describe('getThumbnailNavigationPosition', () => {
+  const state = {
+    windows: {
+      a: { id: 'a', thumbnailNavigationPosition: 'bottom' },
+      b: { id: 'b' },
+    },
+  };
+
+  it('should return thumbnail navigation position if window exists', () => {
+    const received = getThumbnailNavigationPosition(state, 'a');
+    expect(received).toBe('bottom');
+  });
+
+  it('should return undefined if position does not exist in window', () => {
+    const received = getThumbnailNavigationPosition(state, 'b');
+    expect(received).toBeUndefined();
+  });
+
+  it('should return undefined if window does not exists', () => {
+    const received = getThumbnailNavigationPosition(state, 'c');
+    expect(received).toBeUndefined();
+  });
+});
diff --git a/locales/en/translation.json b/locales/en/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..3ca92e21328345b5afbeff4ef4d8c37ddc80a007
--- /dev/null
+++ b/locales/en/translation.json
@@ -0,0 +1,28 @@
+{
+  "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/package.json b/package.json
index d147f892767a6bc609542207aa5e4a093cc8f04b..5e0c9cbdd54832473e5e6c0110ba08746cecc621 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
     "classnames": "^2.2.6",
     "css-ns": "^1.2.2",
     "deepmerge": "^3.1.0",
+    "i18next": "^14.0.1",
     "intersection-observer": "^0.5.1",
     "manifesto.js": "^3.0.9",
     "node-fetch": "^2.3.0",
@@ -43,6 +44,7 @@
     "react": "^16.7.0",
     "react-dom": "^16.4.0",
     "react-fullscreen-crossbrowser": "^1.0.9",
+    "react-i18next": "^9.0.10",
     "react-mosaic-component": "^2.0.2",
     "react-redux": "^6.0.0",
     "react-virtualized": "^9.21.0",
diff --git a/src/components/App.js b/src/components/App.js
index 05b03c507cdb06412824ea44fb36eab0c445973e..7b653149cc2404d786f75f25d26139d536ce7aec 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -13,28 +13,34 @@ import ns from '../config/css-ns';
  */
 class App extends Component {
   /**
-   * render
-   * @return {String} - HTML markup for the component
-   */
-  render() {
-    const {
-      workspace, setWorkspaceFullscreen, config, classes,
-    } = this.props;
-    const theme = createMuiTheme({
+  */
+  makeMuiTheme() {
+    const { theme } = this.props;
+    return createMuiTheme({
       palette: {
-        type: config.theme,
+        type: theme,
       },
       typography: {
         useNextVariants: true,
       },
     });
+  }
+
+  /**
+   * render
+   * @return {String} - HTML markup for the component
+   */
+  render() {
+    const {
+      isFullscreenEnabled, setWorkspaceFullscreen, classes,
+    } = this.props;
 
     return (
       <div className={classNames(classes.background, ns('app'))}>
-        <MuiThemeProvider theme={theme}>
+        <MuiThemeProvider theme={this.makeMuiTheme()}>
           <Fullscreen
-            enabled={workspace.isFullscreenEnabled}
-            onChange={isFullscreenEnabled => setWorkspaceFullscreen(isFullscreenEnabled)}
+            enabled={isFullscreenEnabled}
+            onChange={setWorkspaceFullscreen}
           >
             <Workspace />
           </Fullscreen>
@@ -46,17 +52,15 @@ class App extends Component {
 }
 
 App.propTypes = {
-  config: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
-  workspace: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+  theme: PropTypes.string.isRequired, // eslint-disable-line react/forbid-prop-types
+  isFullscreenEnabled: PropTypes.bool, // eslint-disable-line react/forbid-prop-types
   classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types,
-  setWorkspaceFullscreen: PropTypes.func,
+  setWorkspaceFullscreen: PropTypes.func.isRequired,
 };
 
 App.defaultProps = {
-  workspace: {},
-  setWorkspaceFullscreen: () => {},
+  isFullscreenEnabled: false,
 };
-
 /**
  Material UI style overrides
  @private
diff --git a/src/components/ManifestForm.js b/src/components/ManifestForm.js
index 4ea3eeee329f7ab307ba8da95b67e31c56547f5c..bc9e3251389cd5a71680fceedfbe1822ad0141e9 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/ThumbnailNavigation.js b/src/components/ThumbnailNavigation.js
index edf65753bb3dbbb2cd800af11e63a49aefeabbee..e7de786093b5c7dda810350049406ffb3dda4dda 100644
--- a/src/components/ThumbnailNavigation.js
+++ b/src/components/ThumbnailNavigation.js
@@ -15,28 +15,10 @@ class ThumbnailNavigation extends Component {
   constructor(props) {
     super(props);
 
-    const canvases = (props.manifest.manifestation)
-      ? props.manifest.manifestation.getSequences()[0].getCanvases() : [];
-    this.state = { canvases, manifest: props.manifest };
-
     this.cellRenderer = this.cellRenderer.bind(this);
     this.calculateScaledWidth = this.calculateScaledWidth.bind(this);
   }
 
-  /**
-   */
-  static getDerivedStateFromProps(props, state) {
-    // Any time the manifest changes,
-    // Reset any parts of state that are tied to that manifest (canvases).
-    if (props.manifest !== state.manifest) {
-      return {
-        canvases: props.manifest.manifestation.getSequences()[0].getCanvases(),
-        manifest: props.manifest,
-      };
-    }
-    return null;
-  }
-
   /**
    * Determines whether the current index is the rendered canvas, providing
    * a useful class.
@@ -57,9 +39,8 @@ class ThumbnailNavigation extends Component {
       columnIndex, key, style,
     } = options;
     const {
-      window, setCanvas, config,
+      window, setCanvas, config, canvases,
     } = this.props;
-    const { canvases } = this.state;
     const canvas = canvases[columnIndex];
     return (
       <div
@@ -90,8 +71,7 @@ class ThumbnailNavigation extends Component {
    * in this simple case, a column == canvas.
    */
   calculateScaledWidth(options) {
-    const { config } = this.props;
-    const { canvases } = this.state;
+    const { config, canvases } = this.props;
     const canvas = new ManifestoCanvas(canvases[options.index]);
     return Math.floor(config.thumbnailNavigation.height * canvas.aspectRatio) + 8;
   }
@@ -100,8 +80,7 @@ class ThumbnailNavigation extends Component {
    * Renders things
    */
   render() {
-    const { config, window } = this.props;
-    const { canvases } = this.state;
+    const { config, window, canvases } = this.props;
     if (window.thumbnailNavigationPosition === 'off') {
       return <></>;
     }
@@ -136,7 +115,7 @@ class ThumbnailNavigation extends Component {
 
 ThumbnailNavigation.propTypes = {
   config: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
-  manifest: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  canvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
   setCanvas: PropTypes.func.isRequired,
   window: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
 };
diff --git a/src/components/WindowIcon.js b/src/components/WindowIcon.js
index 28a4edf9579a1ba771553f8e4d024f06acd9eb95..63894236feb9c737ba040e706ceb0c301e58d656 100644
--- a/src/components/WindowIcon.js
+++ b/src/components/WindowIcon.js
@@ -10,30 +10,32 @@ class WindowIcon extends Component {
    * @return
    */
   render() {
-    const {
-      manifestation, classes,
-    } = this.props;
+    const { manifestLogo, classes } = this.props;
 
-    if (manifestation && manifestation.getLogo()) {
-      return (<img src={manifestation.getLogo()} alt="" role="presentation" className={classes.logo} />);
-    }
+    const img = manifestLogo && (
+      <img
+        src={manifestLogo}
+        alt=""
+        role="presentation"
+        className={classes.logo}
+      />
+    );
 
     return (
-      <></>
+      <>
+        {img}
+      </>
     );
   }
 }
 
 WindowIcon.propTypes = {
-  manifestation: PropTypes.shape({
-    getLogo: PropTypes.func,
-  }),
-  classes: PropTypes.shape({ logo: PropTypes.string }),
+  manifestLogo: PropTypes.string,
+  classes: PropTypes.shape({ logo: PropTypes.string }).isRequired,
 };
 
 WindowIcon.defaultProps = {
-  manifestation: null,
-  classes: {},
+  manifestLogo: null,
 };
 
 const styles = {
diff --git a/src/components/WindowList.js b/src/components/WindowList.js
index df0ffdf703b9fbfac363afa2957818d4b583ba80..99d5c8fc7421105972a2b5b49f8ae36c897503b1 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 037349f635a591b5326c3a68edd4f5a82f4eaf01..122c22dc61a6ea6bb57f13a3e63c1b05e915d1d2 100644
--- a/src/components/WindowSideBarButtons.js
+++ b/src/components/WindowSideBarButtons.js
@@ -23,16 +23,19 @@ 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"
-          color="inherit"
+          aria-label={
+            this.sideBarPanelCurrentlySelected('info')
+              ? t('closeInfoCompanionWindow')
+              : t('openInfoCompanionWindow')
+          }
           onClick={() => (toggleWindowSideBarPanel('info'))}
         >
           <InfoIcon
-            color={this.sideBarPanelCurrentlySelected('info') ? 'action' : 'inherit'}
+            color={this.sideBarPanelCurrentlySelected('info') ? 'primary' : 'inherit'}
           />
         </IconButton>
       </>
@@ -43,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 83de8502b1559a935a636c4e4dc5bd816e61ef73..aa646786848c2b78dd24c9efa59b7790c86a93d9 100644
--- a/src/components/WindowSideBarInfoPanel.js
+++ b/src/components/WindowSideBarInfoPanel.js
@@ -1,11 +1,13 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
+import Typography from '@material-ui/core/Typography';
+import { withStyles } from '@material-ui/core/styles';
 import ns from '../config/css-ns';
 
 /**
  * WindowSideBarInfoPanel
  */
-export default class WindowSideBarInfoPanel extends Component {
+class WindowSideBarInfoPanel extends Component {
   /**
    * manifestLabel - get the label from the manifesto manifestation
    * @return String
@@ -37,21 +39,36 @@ export default class WindowSideBarInfoPanel extends Component {
    * @return
    */
   render() {
+    const { classes, t } = this.props;
     return (
       <div className={ns('window-sidebar-info-panel')}>
-        <h2>About this item</h2>
-        <h3>{this.manifestLabel()}</h3>
-        <div>{this.manifestDescription()}</div>
+        <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>
     );
   }
 }
 
 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,
 };
+
+/**
+ * @private
+ */
+const styles = theme => ({
+  windowSideBarH2: theme.typography.h5,
+  windowSideBarH3: theme.typography.h6,
+});
+
+export default withStyles(styles)(WindowSideBarInfoPanel);
diff --git a/src/components/WindowSideBarPanel.js b/src/components/WindowSideBarPanel.js
index a34d1622aaf65cd00ec322c677caeb5e1e2f67e5..2d647b70178e3c29f8e028f6d16f5f0dd77c1f2f 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 ced6f4958653b9ab31e071868e424bd207faff92..518a74cd2884f1a5ac42dab26ae0ec4ddcaff2fc 100644
--- a/src/components/WindowThumbnailSettings.js
+++ b/src/components/WindowThumbnailSettings.js
@@ -16,8 +16,6 @@ export default class WindowThumbnailSettings extends Component {
    */
   constructor(props) {
     super(props);
-    this.state = {
-    };
     this.handleChange = this.handleChange.bind(this);
   }
 
@@ -36,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>
@@ -88,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 23c9b07ddc3e5c06de8cdeb24966d58d8463a0c3..46a52d0aab63a07c41a398ef4b49c0a895eb2600 100644
--- a/src/components/WindowTopBar.js
+++ b/src/components/WindowTopBar.js
@@ -7,11 +7,12 @@ import IconButton from '@material-ui/core/IconButton';
 import MenuIcon from '@material-ui/icons/Menu';
 import Toolbar from '@material-ui/core/Toolbar';
 import classNames from 'classnames';
-import WindowIcon from './WindowIcon';
-import WindowTopMenuButton from './WindowTopMenuButton';
+import WindowIcon from '../containers/WindowIcon';
+import WindowTopMenuButton from '../containers/WindowTopMenuButton';
 import WindowTopBarButtons from '../containers/WindowTopBarButtons';
 import ns from '../config/css-ns';
 
+
 /**
  * WindowTopBar
  */
@@ -35,24 +36,24 @@ class WindowTopBar extends Component {
    */
   render() {
     const {
-      removeWindow, windowId, classes, toggleWindowSideBar, manifest,
+      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)}
         >
           <MenuIcon />
         </IconButton>
-        <WindowIcon manifestation={manifest.manifestation} />
+        <WindowIcon windowId={windowId} />
         <Typography variant="h3" noWrap color="inherit" className={classes.typographyBody}>
           {this.titleContent()}
         </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/WindowTopMenu.js b/src/components/WindowTopMenu.js
index a7e41a2f84ee04866fef1eee2fec2ad5bf2d5e44..b8d799abb772164ba0f2650933ee81c837c4004a 100644
--- a/src/components/WindowTopMenu.js
+++ b/src/components/WindowTopMenu.js
@@ -1,24 +1,13 @@
 import React, { Component } from 'react';
-import { compose } from 'redux';
 import ListItem from '@material-ui/core/ListItem';
 import Menu from '@material-ui/core/Menu';
 import Divider from '@material-ui/core/Divider';
-import { withStyles } from '@material-ui/core/styles';
 import PropTypes from 'prop-types';
 import WindowThumbnailSettings from '../containers/WindowThumbnailSettings';
 
 /**
  */
 class WindowTopMenu extends Component {
-  /**
-   * constructor -
-   */
-  constructor(props) {
-    super(props);
-    this.state = {
-    };
-  }
-
   /**
    * render
    * @return
@@ -50,15 +39,4 @@ WindowTopMenu.defaultProps = {
   anchorEl: null,
 };
 
-/**
- * @private
- */
-const styles = theme => ({
-});
-
-const enhance = compose(
-  withStyles(styles),
-  // further HOC go here
-);
-
-export default enhance(WindowTopMenu);
+export default WindowTopMenu;
diff --git a/src/components/WindowTopMenuButton.js b/src/components/WindowTopMenuButton.js
index a8b66b249e84f772ca3e55f0f7670382e37851a7..a70bbb66850040fab080ded3669b0a650381b521 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="primary"
-          aria-label="Menu"
+          color="inherit"
+          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 83936f5c003266e3995e8a0331ad51bf5ff0f5b8..7e37ef16667e28f073280ebb0268d473cff181c7 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 0122894c567d9845be21f27a7d944c8ff1a2c34d..a9f75a68f0864bab7aa1df954c3483037010a902 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 8bbbb6e50d81673d155a65185b5f002609ae9221..9407576c5ca1259923c6089c231f28251a8345be 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 2c6a3685bb5a3aafb442a1699877736a250b0e17..96bac8ab1b0f6ea5fbd744b51227a213939677b6 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 bcb016dab8b2d5372b2d0925a270edb1daf4dd57..9f37eff027313e9c5279c2fa5ea040a1cf5fc2d9 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 3f2a60cb99ec38933f20d5d939924d5836ba287e..913acac4a1efd36c79a16501db09bd744021be54 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,16 +44,16 @@ class WorkspaceMenuButton extends Component {
    * @return
    */
   render() {
-    const { classes } = this.props;
+    const { classes, t } = this.props;
     const { anchorEl } = this.state;
 
     return (
       <>
         <ListItem>
           <IconButton
-            color="primary"
+            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/WorkspaceMosaic.js b/src/components/WorkspaceMosaic.js
index 8a7b0b0eb6326d0a3c581bfbf5f10244a280fe9b..a4ba5bd7a5cec1210927d5e7e17562617a9bcab1 100644
--- a/src/components/WorkspaceMosaic.js
+++ b/src/components/WorkspaceMosaic.js
@@ -52,7 +52,7 @@ class WorkspaceMosaic extends React.Component {
    */
   determineWorkspaceLayout() {
     const { windows, workspace } = this.props;
-    const windowKeys = Object.keys(windows);
+    const windowKeys = Object.keys(windows).sort();
     const leaveKeys = getLeaves(workspace.layout);
     // Check every window is in the layout, and all layout windows are present
     // in store
diff --git a/src/components/WorkspaceSettings.js b/src/components/WorkspaceSettings.js
index fbd7c3f2c8f3c35a2c3f2eac1451571b84ca31a2..bf84a8f552ac544d1242850b977792a410f41323 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/App.js b/src/containers/App.js
index aa351221c3c86f47cff6e27be3c63c9b170aacd9..1806662aebf92f736f8fe3038c6d733ca73d54e3 100644
--- a/src/containers/App.js
+++ b/src/containers/App.js
@@ -10,9 +10,8 @@ import App from '../components/App';
  */
 const mapStateToProps = state => (
   {
-    config: state.config,
-    workspace: state.workspace,
-    manifests: state.manifests,
+    theme: state.config.theme,
+    isFullscreenEnabled: state.workspace.isFullscreenEnabled,
   }
 );
 
@@ -22,7 +21,6 @@ const mapStateToProps = state => (
  * @private
  */
 const mapDispatchToProps = {
-  fetchManifest: actions.fetchManifest,
   setWorkspaceFullscreen: actions.setWorkspaceFullscreen,
 };
 
diff --git a/src/containers/ManifestForm.js b/src/containers/ManifestForm.js
index 2f47b8cba5d401741246c860b44be0f6aa782750..baae7e3c29edc9fc0d9d1967acbee96c0d274228 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/ThumbnailNavigation.js b/src/containers/ThumbnailNavigation.js
index 9ff864133eef034210f54e973b0e812d9b9f41a1..0f7b5658f41d6a193347fe3cc898b18d54668eb4 100644
--- a/src/containers/ThumbnailNavigation.js
+++ b/src/containers/ThumbnailNavigation.js
@@ -3,13 +3,14 @@ import { connect } from 'react-redux';
 import miradorWithPlugins from '../lib/miradorWithPlugins';
 import * as actions from '../state/actions';
 import ThumbnailNavigation from '../components/ThumbnailNavigation';
-
+import { getManifestCanvases } from '../state/selectors';
 /**
  * mapStateToProps - used to hook up state to props
  * @memberof ThumbnailNavigation
  * @private
  */
-const mapStateToProps = ({ config }) => ({
+const mapStateToProps = ({ config }, { manifest }) => ({
+  canvases: getManifestCanvases(manifest),
   config,
 });
 
diff --git a/src/containers/WindowIcon.js b/src/containers/WindowIcon.js
new file mode 100644
index 0000000000000000000000000000000000000000..49e61e703c4b8c3c820b674fc009748a4e0c60ce
--- /dev/null
+++ b/src/containers/WindowIcon.js
@@ -0,0 +1,10 @@
+import { connect } from 'react-redux';
+import { getWindowManifest, getManifestLogo } from '../state/selectors';
+import WindowIcon from '../components/WindowIcon';
+
+/** */
+const mapStateToProps = (state, { windowId }) => ({
+  manifestLogo: getManifestLogo(getWindowManifest(state, windowId)),
+});
+
+export default connect(mapStateToProps)(WindowIcon);
diff --git a/src/containers/WindowList.js b/src/containers/WindowList.js
index 2350e6831221fc8fda24aae91e3e7cd0b9a66669..86814d210609682ed68a4259d967afa028d1f23c 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 93a58e23f8b1ab6288bda18cc31f7b730e02c641..e4a652b3483c0e250c4d81a0f7fb49d13bf650aa 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 0000000000000000000000000000000000000000..0e2434944b72965b9656fc6a828a77b7ab0f2b43
--- /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 75b0e947253ea17e06667eb8d7bfa27f146fb56b..a0463090f5707e1289ab7e8a47e208fc505e19b3 100644
--- a/src/containers/WindowThumbnailSettings.js
+++ b/src/containers/WindowThumbnailSettings.js
@@ -1,6 +1,9 @@
 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';
 
 /**
@@ -17,12 +20,14 @@ const mapDispatchToProps = { setWindowThumbnailPosition: actions.setWindowThumbn
  */
 const mapStateToProps = (state, props) => (
   {
-    thumbnailNavigationPosition: state.windows[props.windowId].thumbnailNavigationPosition,
+    thumbnailNavigationPosition: getThumbnailNavigationPosition(state, props.windowId),
   }
 );
 
 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 2f1a0bbdabafd958df5ab85f0cbbbff151c85054..0bc54458ae7b4ef4712ece6e2f422459c746b68e 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 0000000000000000000000000000000000000000..ebfedfeb3d1d114cd7eabc4ea478137e681fe8fc
--- /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 b9ed10040df56b50b030ec03866cc9427fd24ce7..d7faf821822af61679adf76eeedd1a521624ef93 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 a90a4670fb89a2dc0a37338be8349c198726f9ae..82dded070825354cb5fffba8ae5d5d3513218597 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 60ccaa92c4331f6129ffd9b50595c01f656255dd..64f9bd73242ac485818d9b53a94ec1ea9196a16e 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 0000000000000000000000000000000000000000..866358d4fede4ed20468237fbf47e8cb0288c2b6
--- /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 0000000000000000000000000000000000000000..0ccd4218f13526ebaac8a74b08b4b81abe17eb1a
--- /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 a52b463006165a812e01d231e9144683fc600838..50b6971352e0d0d3ec10e975d20410732cc7fc47 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
 );
 
diff --git a/src/i18n.js b/src/i18n.js
new file mode 100644
index 0000000000000000000000000000000000000000..dac6958f05709d746f951f1f33fb68cbc2f62a6f
--- /dev/null
+++ b/src/i18n.js
@@ -0,0 +1,22 @@
+import i18n from 'i18next';
+import { reactI18nextModule } from 'react-i18next';
+import en from '../locales/en/translation.json';
+
+// Load translations for each language
+const resources = {
+  en,
+};
+
+i18n
+  .use(reactI18nextModule)
+  .init({
+    resources,
+    lng: 'en',
+    fallbackLng: 'en',
+
+    interpolation: {
+      escapeValue: false, // react is already safe from xss
+    },
+  });
+
+export default i18n;
diff --git a/src/lib/MiradorViewer.js b/src/lib/MiradorViewer.js
index 3bd09c9171fd2d27ac310784449093394a57a5c6..2fd7d8b5558780c8d4e97db1ac81ed04bfd8870f 100644
--- a/src/lib/MiradorViewer.js
+++ b/src/lib/MiradorViewer.js
@@ -1,12 +1,14 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
 import { Provider } from 'react-redux';
+import { I18nextProvider } from 'react-i18next';
 import deepmerge from 'deepmerge';
 import App from '../containers/App';
 import createRootReducer from '../state/reducers/index';
 import createStore from '../state/createStore';
 import * as actions from '../state/actions';
 import settings from '../config/settings';
+import i18n from '../i18n';
 
 /**
  * Default Mirador instantiation
@@ -26,7 +28,9 @@ class MiradorViewer {
 
     ReactDOM.render(
       <Provider store={this.store}>
-        <App config={config} />
+        <I18nextProvider i18n={i18n}>
+          <App config={config} />
+        </I18nextProvider>
       </Provider>,
       document.getElementById(config.id),
     );
diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..7cc2c24eca30ff1640fd5ac163fb46ee0975b5a5
--- /dev/null
+++ b/src/state/selectors/index.js
@@ -0,0 +1,45 @@
+
+/**
+* Return the manifest that belongs to a certain window.
+* @param {object} state
+* @param {String} windowId
+* @return {object}
+*/
+export function getWindowManifest(state, windowId) {
+  return state.windows[windowId]
+    && state.windows[windowId].manifestId
+    && state.manifests[state.windows[windowId].manifestId];
+}
+
+/**
+* Return the logo of a manifest or null
+* @param {object} manifest
+* @return {String|null}
+*/
+export function getManifestLogo(manifest) {
+  return manifest.manifestation
+    && manifest.manifestation.getLogo();
+}
+
+/**
+* Return the logo of a manifest or null
+* @param {object} manifest
+* @return {String|null}
+*/
+export function getManifestCanvases(manifest) {
+  if (!manifest.manifestation) {
+    return [];
+  }
+
+  return manifest.manifestation.getSequences()[0].getCanvases();
+}
+
+/** Return position of thumbnail navigation in a certain window.
+* @param {object} state
+* @param {String} windowId
+* @param {String}
+*/
+export function getThumbnailNavigationPosition(state, windowId) {
+  return state.windows[windowId]
+    && state.windows[windowId].thumbnailNavigationPosition;
+}