Repository: polkadot-js/extension
Pull request #429 (2020-09-04 12:09:58)

Plumbing for i18n (key -> key)
... should be ok, need to check each line in the PR


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 42b78660..c09d6abf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ Changes:
 - Handling signing rejections (any order) transparently
 - Small overall UI and use adjustments
 - Latest upstream polkadot-js dependencies
+- Prepare for i18n translations with setup of key -> key
 
 
 ## 0.32.1 Jul 27, 2020
diff --git a/i18next-scanner.config.js b/i18next-scanner.config.js
new file mode 100644
index 00000000..15db788f
--- /dev/null
+++ b/i18next-scanner.config.js
@@ -0,0 +1,34 @@
+// Copyright 2019-2020 @polkadot/extension authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+module.exports = {
+  input: [
+    'packages/extension-ui/src/**/*.{ts,tsx}',
+    // Use ! to filter out files or directories
+    '!packages/*/src/**/*.spec.{ts,tsx}',
+    '!packages/*/src/i18n/**',
+    '!**/node_modules/**'
+  ],
+  options: {
+    debug: true,
+    defaultLng: 'en',
+    func: {
+      extensions: ['.tsx', '.ts'],
+      list: ['t', 'i18next.t', 'i18n.t']
+    },
+    keySeparator: false, // key separator
+    lngs: ['en'],
+    nsSeparator: false, // namespace separator
+    resource: {
+      jsonIndent: 2,
+      lineEnding: '\n',
+      loadPath: 'packages/extension/public/locales/{{lng}}/{{ns}}.json',
+      savePath: 'packages/extension/public/locales/{{lng}}/{{ns}}.json'
+    },
+    trans: {
+      component: 'Trans'
+    }
+  },
+  output: './'
+};
diff --git a/package.json b/package.json
index 137f7e99..4b9e8f93 100644
--- a/package.json
+++ b/package.json
@@ -8,17 +8,18 @@
     "packages/*"
   ],
   "resolutions": {
-    "@polkadot/api": "^1.32.0-beta.18",
+    "@polkadot/api": "^1.32.0-beta.19",
     "@polkadot/keyring": "^3.4.1",
-    "@polkadot/types": "^1.32.0-beta.18",
+    "@polkadot/types": "^1.32.0-beta.19",
     "@polkadot/util": "^3.4.1",
     "@polkadot/util-crypto": "^3.4.1",
     "babel-core": "^7.0.0-bridge.0",
     "typescript": "^4.0.2"
   },
   "scripts": {
-    "build": "yarn build:code && yarn build:zip",
+    "build": "yarn build:i18n && yarn build:code && yarn build:zip",
     "build:code": "NODE_ENV=production polkadot-dev-build-ts",
+    "build:i18n": "i18next-scanner --config i18next-scanner.config.js",
     "build:release": "polkadot-ci-ghact-build",
     "build:zip": "yarn build:zip:dst && yarn build:zip:src",
     "build:zip:dst": "rm -rf ./master-build.zip && cd packages/extension/build && zip -r -FS ../../../master-build.zip .",
@@ -34,9 +35,10 @@
   "devDependencies": {
     "@babel/core": "^7.11.5",
     "@polkadot/dev": "^0.55.45",
-    "@polkadot/typegen": "^1.32.0-beta.18",
+    "@polkadot/typegen": "^1.32.0-beta.19",
     "@types/jest": "^26.0.13",
     "copy-webpack-plugin": "^6.1.0",
+    "i18next-scanner": "^2.11.0",
     "sinon-chrome": "^3.0.1",
     "stylelint": "^13.7.0",
     "stylelint-config-recommended": "^3.0.0",
diff --git a/packages/extension-base/package.json b/packages/extension-base/package.json
index b2f1aa10..43055871 100644
--- a/packages/extension-base/package.json
+++ b/packages/extension-base/package.json
@@ -6,7 +6,7 @@
   "license": "Apache-2",
   "dependencies": {
     "@babel/runtime": "^7.11.2",
-    "@polkadot/api": "^1.32.0-beta.18",
+    "@polkadot/api": "^1.32.0-beta.19",
     "@polkadot/extension-inject": "0.33.0-beta.18",
     "@polkadot/keyring": "^3.4.1",
     "@polkadot/ui-keyring": "^0.59.0-beta.0"
diff --git a/packages/extension-ui/src/Popup/Accounts/Account.tsx b/packages/extension-ui/src/Popup/Accounts/Account.tsx
index a09870b8..464feee7 100644
--- a/packages/extension-ui/src/Popup/Accounts/Account.tsx
+++ b/packages/extension-ui/src/Popup/Accounts/Account.tsx
@@ -9,6 +9,7 @@ import styled from 'styled-components';
 import genesisOptions from '@polkadot/extension-chains/genesisHashes';
 
 import { ActionContext, Address, Checkbox, Dropdown, Link, MenuDivider } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { editAccount, showAccount, tieAccount } from '../../messaging';
 import { Name } from '../../partials';
 
@@ -23,6 +24,7 @@ interface EditState {
 }
 
 function Account ({ address, className, genesisHash, isExternal, isHidden, parentName, suri }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const [{ isEditing, toggleActions }, setEditing] = useState<EditState>({ isEditing: false, toggleActions: 0 });
   const [editedName, setName] = useState<string | null>(null);
@@ -67,14 +69,14 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
         className='menuItem'
         onClick={_toggleEdit}
       >
-        Rename
+        {t('Rename')}
       </Link>
       {!isExternal && (
         <Link
           className='menuItem'
           to={`/account/derive/${address}/locked`}
         >
-          Derive New Account
+          {t('Derive New Account')}
         </Link>
       )}
       <MenuDivider />
@@ -84,7 +86,7 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
           isDanger
           to={`/account/export/${address}`}
         >
-          Export Account
+          {t('Export Account')}
         </Link>
       )}
       <Link
@@ -92,14 +94,14 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
         isDanger
         to={`/account/forget/${address}`}
       >
-        Forget Account
+        {t('Forget Account')}
       </Link>
       <MenuDivider />
       <div className='menuItem'>
         <Checkbox
           checked={!isHidden}
           className='inputItem'
-          label='Visible (always inject)'
+          label={t('Visible (always inject)')}
           onClick={_toggleVisibility}
         />
       </div>
@@ -113,7 +115,7 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
         />
       </div>
     </>
-  ), [_onChangeGenesis, _toggleEdit, _toggleVisibility, address, genesisHash, isExternal, isHidden]);
+  ), [_onChangeGenesis, _toggleEdit, _toggleVisibility, address, genesisHash, isExternal, isHidden, t]);
 
   return (
     <div className={className}>
diff --git a/packages/extension-ui/src/Popup/Accounts/AddAccount.tsx b/packages/extension-ui/src/Popup/Accounts/AddAccount.tsx
index 289f4413..62de2c78 100644
--- a/packages/extension-ui/src/Popup/Accounts/AddAccount.tsx
+++ b/packages/extension-ui/src/Popup/Accounts/AddAccount.tsx
@@ -8,6 +8,7 @@ import React, { useCallback, useContext } from 'react';
 import styled from 'styled-components';
 
 import { ActionContext } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import Header from '../../partials/Header';
 import AddAccountImage from './AddAccountImage';
 
@@ -16,6 +17,7 @@ interface Props extends ThemeProps {
 }
 
 function AddAccount ({ className }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const _onClick = useCallback(
     () => onAction('/account/create'),
@@ -34,7 +36,7 @@ function AddAccount ({ className }: Props): React.ReactElement<Props> {
           <AddAccountImage onClick={_onClick}/>
         </div>
         <div className='no-accounts'>
-          <p>You currently don&apos;t have any accounts. Create your first account to get started.</p>
+          <p>{t("You currently don't have any accounts. Create your first account to get started.")}</p>
         </div>
       </div>
     </>
diff --git a/packages/extension-ui/src/Popup/Accounts/index.tsx b/packages/extension-ui/src/Popup/Accounts/index.tsx
index a0ed19a0..00cc2c87 100644
--- a/packages/extension-ui/src/Popup/Accounts/index.tsx
+++ b/packages/extension-ui/src/Popup/Accounts/index.tsx
@@ -6,11 +6,13 @@ import React, { useContext } from 'react';
 import styled from 'styled-components';
 
 import { AccountContext } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { Header } from '../../partials';
 import AccountsTree from './AccountsTree';
 import AddAccount from './AddAccount';
 
 export default function Accounts (): React.ReactElement {
+  const { t } = useTranslation();
   const { hierarchy } = useContext(AccountContext);
 
   return (
@@ -22,7 +24,7 @@ export default function Accounts (): React.ReactElement {
             <Header
               showAdd
               showSettings
-              text={'Accounts'}
+              text={t('Accounts')}
             />
             <AccountsArea>
               {hierarchy.map((json, index): React.ReactNode => (
diff --git a/packages/extension-ui/src/Popup/Authorize/Request.tsx b/packages/extension-ui/src/Popup/Authorize/Request.tsx
index 0f6f6b54..7ac9130f 100644
--- a/packages/extension-ui/src/Popup/Authorize/Request.tsx
+++ b/packages/extension-ui/src/Popup/Authorize/Request.tsx
@@ -9,6 +9,7 @@ import React, { useCallback, useContext } from 'react';
 import styled from 'styled-components';
 
 import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { approveAuthRequest, rejectAuthRequest } from '../../messaging';
 
 interface Props {
@@ -20,6 +21,7 @@ interface Props {
 }
 
 function Request ({ authId, className, isFirst, request: { origin }, url }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
 
   const _onApprove = useCallback(
@@ -36,6 +38,7 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop
     [authId, onAction]
   );
 
+  // FIXME i18next <Trans /> component to be used here
   return (
     <div className={className}>
       <RequestInfo>
@@ -57,8 +60,8 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop
         </Info>
         {isFirst && (
           <>
-            <RequestWarning>Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.</RequestWarning>
-            <AcceptButton onClick={_onApprove}>Yes, allow this application access</AcceptButton>
+            <RequestWarning>{t('Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.')}</RequestWarning>
+            <AcceptButton onClick={_onApprove}>{t('Yes, allow this application access')}</AcceptButton>
           </>
         )}
         <RejectButton>
diff --git a/packages/extension-ui/src/Popup/Authorize/index.tsx b/packages/extension-ui/src/Popup/Authorize/index.tsx
index 62b7d2fe..e23ef5d7 100644
--- a/packages/extension-ui/src/Popup/Authorize/index.tsx
+++ b/packages/extension-ui/src/Popup/Authorize/index.tsx
@@ -6,16 +6,18 @@ import React, { useContext } from 'react';
 import styled from 'styled-components';
 
 import { AuthorizeReqContext } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { Header } from '../../partials';
 import Request from './Request';
 
 export default function Authorize (): React.ReactElement {
+  const { t } = useTranslation();
   const requests = useContext(AuthorizeReqContext);
 
   return (
     <>
       <Scroll isLastRequest={requests.length === 1}>
-        <Header text='Authorize' />
+        <Header text={t('Authorize')} />
         {requests.map(({ id, request, url }, index): React.ReactNode => (
           <Request
             authId={id}
diff --git a/packages/extension-ui/src/Popup/CreateAccount/AccountName.tsx b/packages/extension-ui/src/Popup/CreateAccount/AccountName.tsx
index d88d1e37..6a5049eb 100644
--- a/packages/extension-ui/src/Popup/CreateAccount/AccountName.tsx
+++ b/packages/extension-ui/src/Popup/CreateAccount/AccountName.tsx
@@ -5,6 +5,7 @@
 import React, { useCallback, useState } from 'react';
 
 import { BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { Name, Password } from '../../partials';
 
 interface Props {
@@ -15,6 +16,7 @@ interface Props {
 }
 
 function AccountName ({ isBusy, onBackClick, onCreate }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [name, setName] = useState<string | null>(null);
   const [password, setPassword] = useState<string | null>(null);
 
@@ -39,7 +41,7 @@ function AccountName ({ isBusy, onBackClick, onCreate }: Props): React.ReactElem
           isDisabled={!password || !name}
           onClick={_onCreate}
         >
-          Add the account with the generated seed
+          {t('Add the account with the generated seed')}
         </NextStepButton>
       </ButtonArea>
     </>
diff --git a/packages/extension-ui/src/Popup/CreateAccount/CreationStep.tsx b/packages/extension-ui/src/Popup/CreateAccount/CreationStep.tsx
index 775d0327..19fe5328 100644
--- a/packages/extension-ui/src/Popup/CreateAccount/CreationStep.tsx
+++ b/packages/extension-ui/src/Popup/CreateAccount/CreationStep.tsx
@@ -8,6 +8,7 @@ import React from 'react';
 import styled from 'styled-components';
 
 import { ActionText, Title } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 
 interface Props {
   step: number;
@@ -16,6 +17,8 @@ interface Props {
 }
 
 function CreationStep ({ className, onClick, step }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
+
   return (
     <div className={className}>
       <div>
@@ -25,12 +28,13 @@ function CreationStep ({ className, onClick, step }: Props): React.ReactElement<
       </div>
       <ActionText
         onClick={onClick}
-        text={step === 1 ? 'Cancel' : 'Back'}
+        text={step === 1 ? t('Cancel') : t('Back')}
       />
     </div>
   );
 }
 
+// FIXME i18n
 const CreateAnAccount = styled(Title).attrs(() => ({
   children: 'Create an account:'
 }))`
diff --git a/packages/extension-ui/src/Popup/CreateAccount/Mnemonic.tsx b/packages/extension-ui/src/Popup/CreateAccount/Mnemonic.tsx
index eb834882..d67cc2d5 100644
--- a/packages/extension-ui/src/Popup/CreateAccount/Mnemonic.tsx
+++ b/packages/extension-ui/src/Popup/CreateAccount/Mnemonic.tsx
@@ -6,6 +6,7 @@ import React, { useCallback, useState } from 'react';
 
 import { ButtonArea, Checkbox, MnemonicSeed, NextStepButton, VerticalSpace, Warning } from '../../components';
 import useToast from '../../hooks/useToast';
+import useTranslation from '../../hooks/useTranslation';
 
 interface Props {
   onNextStep: () => void;
@@ -24,12 +25,14 @@ const onCopy = (): void => {
 };
 
 function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [isMnemonicSaved, setIsMnemonicSaved] = useState(false);
   const { show } = useToast();
+
   const _onCopy = useCallback((): void => {
     onCopy();
-    show('Copied');
-  }, [show]);
+    show(t('Copied'));
+  }, [show, t]);
 
   return (
     <>
@@ -37,11 +40,11 @@ function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
         onCopy={_onCopy}
         seed={seed}
       />
-      <Warning>Please write down your wallet’s mnemonic seed and keep it in a safe place. The mnemonic can be used to restore your wallet. Keep it carefully to not lose your assets.</Warning>
+      <Warning>{t("Please write down your wallet's mnemonic seed and keep it in a safe place. The mnemonic can be used to restore your wallet. Keep it carefully to not lose your assets.")}</Warning>
       <VerticalSpace />
       <Checkbox
         checked={isMnemonicSaved}
-        label='I have saved my mnemonic seed safely.'
+        label={t('I have saved my mnemonic seed safely.')}
         onChange={setIsMnemonicSaved}
       />
       <ButtonArea>
@@ -49,7 +52,7 @@ function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
           isDisabled={!isMnemonicSaved}
           onClick={onNextStep}
         >
-          Next step
+          {t('Next step')}
         </NextStepButton>
       </ButtonArea>
     </>
diff --git a/packages/extension-ui/src/Popup/CreateAccount/index.tsx b/packages/extension-ui/src/Popup/CreateAccount/index.tsx
index ff455708..321fcac2 100644
--- a/packages/extension-ui/src/Popup/CreateAccount/index.tsx
+++ b/packages/extension-ui/src/Popup/CreateAccount/index.tsx
@@ -3,13 +3,16 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import React, { useCallback, useContext, useEffect, useState } from 'react';
+
 import { ActionContext, Address, Loading } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { createAccountSuri, createSeed } from '../../messaging';
 import { HeaderWithSteps } from '../../partials';
 import AccountName from './AccountName';
 import Mnemonic from './Mnemonic';
 
 export default function CreateAccount (): React.ReactElement {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const [isBusy, setIsBusy] = useState(false);
   const [step, setStep] = useState(1);
@@ -46,7 +49,7 @@ export default function CreateAccount (): React.ReactElement {
     <>
       <HeaderWithSteps
         step={step}
-        text='Create an account:&nbsp;'
+        text={t('Create an account')}
       />
       <Loading>
         <div>
diff --git a/packages/extension-ui/src/Popup/Derive/DerivationPath.tsx b/packages/extension-ui/src/Popup/Derive/DerivationPath.tsx
index d8c50bbc..ba9121fb 100644
--- a/packages/extension-ui/src/Popup/Derive/DerivationPath.tsx
+++ b/packages/extension-ui/src/Popup/Derive/DerivationPath.tsx
@@ -10,6 +10,7 @@ import styled from 'styled-components';
 import lockIcon from '../../assets/lock.svg';
 import unlockIcon from '../../assets/unlock.svg';
 import { InputWithLabel, Svg, Button } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 
 interface Props {
   className?: string;
@@ -21,8 +22,10 @@ interface Props {
 }
 
 function DerivationPath ({ className, defaultPath, isError, onChange }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [path, setPath] = useState<string>(defaultPath);
   const [isDisabled, setIsDisabled] = useState(false);
+
   const _onExpand = useCallback(() => setIsDisabled(!isDisabled), [isDisabled]);
 
   const _onChange = useCallback((newPath: string): void => {
@@ -40,11 +43,11 @@ function DerivationPath ({ className, defaultPath, isError, onChange }: Props):
             isError={isError || !path}
             label={
               isDisabled
-                ? 'Derivation Path'
-                : 'Derivation Path (unlock to edit)'
+                ? t('Derivation Path')
+                : t('Derivation Path (unlock to edit)')
             }
             onChange={_onChange}
-            placeholder='//hard/soft'
+            placeholder={t('//hard/soft')}
             value={path}
           />
         </PathInput>
diff --git a/packages/extension-ui/src/Popup/Derive/SelectParent.tsx b/packages/extension-ui/src/Popup/Derive/SelectParent.tsx
index f8fefe4b..2a90e3ef 100644
--- a/packages/extension-ui/src/Popup/Derive/SelectParent.tsx
+++ b/packages/extension-ui/src/Popup/Derive/SelectParent.tsx
@@ -7,6 +7,7 @@ import styled from 'styled-components';
 import { assert } from '@polkadot/util';
 
 import { AccountContext, ActionContext, Address, ButtonArea, Checkbox, InputWithLabel, Label, NextStepButton, VerticalSpace } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { validateAccount, validateDerivationPath } from '../../messaging';
 import { nextDerivationPath } from '../../util/nextDerivationPath';
 import AddressDropdown from './AddressDropdown';
@@ -20,6 +21,7 @@ interface Props {
 }
 
 export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress, parentGenesis }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const [isBusy, setIsBusy] = useState(false);
   const { accounts, hierarchy } = useContext(AccountContext);
@@ -93,7 +95,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
       {!isLocked && (
         <CheckboxWithSmallerMargins
           checked={shouldAccountBeDerived}
-          label='Derive new account from existing'
+          label={t('Derive new account from existing')}
           onChange={setShouldAccountBeDerived}
         />
       )}
@@ -106,7 +108,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
             />
           )
           : (
-            <Label label='Choose Parent Account:'>
+            <Label label={t('Choose Parent Account:')}>
               <AddressDropdown
                 allAddresses={allAddresses}
                 onSelect={_onParentChange}
@@ -121,7 +123,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
             data-export-password
             isError={!isProperParentPassword}
             isFocused
-            label='enter the password for the account you want to derive from'
+            label={t('enter the password for the account you want to derive from')}
             onChange={_onParentPasswordEnter}
             type='password'
             value={parentPassword}
@@ -146,7 +148,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
               isDisabled={!isProperParentPassword || !suriPath}
               onClick={_onSubmit}
             >
-              Create a derived account
+              {t('Create a derived account')}
             </NextStepButton>
           )
           : (
@@ -154,7 +156,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
               data-button-action='create root account'
               onClick={_goCreate}
             >
-              Create account from new seed
+              {t('Create account from new seed')}
             </NextStepButton>
           )
         }
diff --git a/packages/extension-ui/src/Popup/Derive/index.tsx b/packages/extension-ui/src/Popup/Derive/index.tsx
index 2ca35351..0ae51d0c 100644
--- a/packages/extension-ui/src/Popup/Derive/index.tsx
+++ b/packages/extension-ui/src/Popup/Derive/index.tsx
@@ -6,6 +6,7 @@ import React, { useCallback, useContext, useMemo, useState } from 'react';
 import { useParams } from 'react-router';
 
 import { AccountContext, ActionContext, Address, BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { deriveAccount } from '../../messaging';
 import { HeaderWithSteps, Name, Password } from '../../partials';
 import { SelectParent } from './SelectParent';
@@ -28,6 +29,7 @@ interface ConfirmState {
 }
 
 function Derive ({ isLocked }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const { accounts } = useContext(AccountContext);
   const { address: parentAddress } = useParams<AddressState>();
@@ -69,7 +71,7 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
     <>
       <HeaderWithSteps
         step={account ? 2 : 1}
-        text='Add new account:&nbsp;'
+        text={t('Add new account')}
       />
       {!account && (
         <SelectParent
@@ -101,7 +103,7 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
               isDisabled={!password}
               onClick={_onCreate}
             >
-              Create derived account
+              {t('Create derived account')}
             </NextStepButton>
           </ButtonArea>
         </>
diff --git a/packages/extension-ui/src/Popup/Export.tsx b/packages/extension-ui/src/Popup/Export.tsx
index 0bb2a16d..405466db 100644
--- a/packages/extension-ui/src/Popup/Export.tsx
+++ b/packages/extension-ui/src/Popup/Export.tsx
@@ -6,6 +6,7 @@ import React, { useCallback, useContext, useState } from 'react';
 import { RouteComponentProps, withRouter } from 'react-router';
 
 import { ActionBar, ActionContext, ActionText, Address, Button, InputWithLabel, Warning } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { exportAccount } from '../messaging';
 import { Header } from '../partials';
 import styled from 'styled-components';
@@ -15,6 +16,7 @@ const MIN_LENGTH = 6;
 type Props = RouteComponentProps<{ address: string }>;
 
 function Export ({ match: { params: { address } } }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const [isBusy, setIsBusy] = useState(false);
   const [pass, setPass] = useState('');
@@ -55,17 +57,17 @@ function Export ({ match: { params: { address } } }: Props): React.ReactElement<
     <>
       <Header
         showBackArrow
-        text='Export account'
+        text={t('Export account')}
       />
       <div>
         <Address address={address}>
-          <MovedWarning danger>You are exporting your account. Keep it safe and don&apos;t share it with anyone.</MovedWarning>
+          <MovedWarning danger>{t("You are exporting your account. Keep it safe and don't share it with anyone.")}</MovedWarning>
           <ActionArea>
             <InputWithLabel
               data-export-password
               disabled={isBusy}
               isError={pass.length < MIN_LENGTH || wrongPasswordHighlight}
-              label='password for this account'
+              label={t('password for this account')}
               onChange={setPass}
               type='password'
             />
@@ -77,12 +79,12 @@ function Export ({ match: { params: { address } } }: Props): React.ReactElement<
               isDisabled={pass.length === 0}
               onClick={_onExportButtonClick}
             >
-              I want to export this account
+              {t('I want to export this account')}
             </Button>
             <CancelButton>
               <ActionText
                 onClick={_goHome}
-                text='Cancel'
+                text={t('Cancel')}
               />
             </CancelButton>
           </ActionArea>
diff --git a/packages/extension-ui/src/Popup/Forget.tsx b/packages/extension-ui/src/Popup/Forget.tsx
index 3e30f800..a6311ed9 100644
--- a/packages/extension-ui/src/Popup/Forget.tsx
+++ b/packages/extension-ui/src/Popup/Forget.tsx
@@ -7,12 +7,14 @@ import { RouteComponentProps, withRouter } from 'react-router';
 import styled from 'styled-components';
 
 import { ActionContext, Address, Button, Warning, ActionBar, ActionText } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { forgetAccount } from '../messaging';
 import { Header } from '../partials';
 
 type Props = RouteComponentProps<{ address: string }>;
 
 function Forget ({ match: { params: { address } } }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
 
   const _goHome = useCallback(
@@ -32,24 +34,22 @@ function Forget ({ match: { params: { address } } }: Props): React.ReactElement<
     <>
       <Header
         showBackArrow
-        text='Forget account'
+        text={t('Forget account')}
       />
       <div>
         <Address address={address}>
-          <MovedWarning isDanger>
-            You are about to remove the account. This means that you will not be able to access it via this extension anymore. If you wish to recover it, you would need to use the seed.
-          </MovedWarning>
+          <MovedWarning isDanger>{t('You are about to remove the account. This means that you will not be able to access it via this extension anymore. If you wish to recover it, you would need to use the seed.')}</MovedWarning>
           <ActionArea>
             <Button
               isDanger
               onClick={_onClick}
             >
-              I want to forget this account
+              {t('I want to forget this account')}
             </Button>
             <CancelButton>
               <ActionText
                 onClick={_goHome}
-                text='Cancel'
+                text={t('Cancel')}
               />
             </CancelButton>
           </ActionArea>
diff --git a/packages/extension-ui/src/Popup/ImportQr.tsx b/packages/extension-ui/src/Popup/ImportQr.tsx
index fb39a1da..ba229965 100644
--- a/packages/extension-ui/src/Popup/ImportQr.tsx
+++ b/packages/extension-ui/src/Popup/ImportQr.tsx
@@ -6,10 +6,12 @@ import React, { useContext, useState } from 'react';
 import { QrScanAddress } from '@polkadot/react-qr';
 
 import { ActionContext, Address, NextStepButton, ButtonArea, VerticalSpace } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { createAccountExternal } from '../messaging';
 import { Header, Name } from '../partials';
 
 export default function ImportQr (): React.ReactElement {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const [account, setAccount] = useState<null | { content: string; genesisHash: string }>(null);
   const [name, setName] = useState<string | null>(null);
@@ -27,7 +29,7 @@ export default function ImportQr (): React.ReactElement {
     <>
       <Header
         showBackArrow
-        text='Scan Address Qr'
+        text={t('Scan Address Qr')}
       />
       {!account && (
         <div>
@@ -52,7 +54,7 @@ export default function ImportQr (): React.ReactElement {
               <NextStepButton
                 onClick={_onCreate}
               >
-                Add the account with identified address
+                {t('Add the account with identified address')}
               </NextStepButton>
             </ButtonArea>
           )}
diff --git a/packages/extension-ui/src/Popup/ImportSeed.tsx b/packages/extension-ui/src/Popup/ImportSeed.tsx
index c10255f4..377bcb81 100644
--- a/packages/extension-ui/src/Popup/ImportSeed.tsx
+++ b/packages/extension-ui/src/Popup/ImportSeed.tsx
@@ -6,6 +6,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
 import styled from 'styled-components';
 
 import { AccountContext, ActionContext, Address, ButtonArea, NextStepButton, TextAreaWithLabel, ValidatedInput, VerticalSpace } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { createAccountSuri, validateSeed } from '../messaging';
 import { Header, Name, Password } from '../partials';
 import { allOf, isNotShorterThan, Result } from '../util/validators';
@@ -26,6 +27,7 @@ const isSeedValid = allOf(
 );
 
 export default function Import (): React.ReactElement {
+  const { t } = useTranslation();
   const { accounts } = useContext(AccountContext);
   const onAction = useContext(ActionContext);
   const [isBusy, setIsBusy] = useState(false);
@@ -71,7 +73,7 @@ export default function Import (): React.ReactElement {
     <>
       <HeaderWithSmallerMargin
         showBackArrow
-        text='Import account'
+        text={t('Import account')}
       />
       <div>
         <Address
@@ -83,7 +85,7 @@ export default function Import (): React.ReactElement {
         // eslint-disable-next-line @typescript-eslint/no-use-before-define
         component={SeedInput}
         isFocused
-        label='existing 12 or 24-word mnemonic seed'
+        label={t('existing 12 or 24-word mnemonic seed')}
         onValidatedChange={_onChangeSeed}
         rowsCount={2}
         validator={isSeedValid}
@@ -102,7 +104,7 @@ export default function Import (): React.ReactElement {
               isBusy={isBusy}
               onClick={_onCreate}
             >
-              Add the account with the supplied seed
+              {t('Add the account with the supplied seed')}
             </NextStepButton>
           </ButtonArea>
         </>
diff --git a/packages/extension-ui/src/Popup/Metadata/Request.tsx b/packages/extension-ui/src/Popup/Metadata/Request.tsx
index ed478b77..ed6108ee 100644
--- a/packages/extension-ui/src/Popup/Metadata/Request.tsx
+++ b/packages/extension-ui/src/Popup/Metadata/Request.tsx
@@ -10,6 +10,7 @@ import styled from 'styled-components';
 
 import { ActionBar, ActionContext, Button, Link, Table, Warning } from '../../components';
 import useMetadata from '../../hooks/useMetadata';
+import useTranslation from '../../hooks/useTranslation';
 import { approveMetaRequest, rejectMetaRequest } from '../../messaging';
 
 interface Props {
@@ -20,6 +21,7 @@ interface Props {
 }
 
 function Request ({ className, metaId, request, url }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const chain = useMetadata(request.genesisHash);
   const onAction = useContext(ActionContext);
 
@@ -45,44 +47,44 @@ function Request ({ className, metaId, request, url }: Props): React.ReactElemen
     <div className={className}>
       <Table>
         <tr>
-          <td className='label'>from</td>
+          <td className='label'>{t('from')}</td>
           <td className='data'>{url}</td>
         </tr>
         <tr>
-          <td className='label'>chain</td>
+          <td className='label'>{t('chain')}</td>
           <td className='data'>{request.chain}</td>
         </tr>
         <tr>
-          <td className='label'>icon</td>
+          <td className='label'>{t('icon')}</td>
           <td className='data'>{request.icon}</td>
         </tr>
         <tr>
-          <td className='label'>decimals</td>
+          <td className='label'>{t('decimals')}</td>
           <td className='data'>{request.tokenDecimals}</td>
         </tr>
         <tr>
-          <td className='label'>symbol</td>
+          <td className='label'>{t('symbol')}</td>
           <td className='data'>{request.tokenSymbol}</td>
         </tr>
         <tr>
-          <td className='label'>upgrade</td>
-          <td className='data'>{chain ? chain.specVersion : 'unknown'} -&gt; {request.specVersion}</td>
+          <td className='label'>{t('upgrade')}</td>
+          <td className='data'>{chain ? chain.specVersion : t('<unknown>')} -&gt; {request.specVersion}</td>
         </tr>
       </Table>
       <div className='requestInfo'>
-        <Warning className='requestWarning'>This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.</Warning>
+        <Warning className='requestWarning'>{t('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.')}</Warning>
         <Button
           className='btnAccept'
           onClick={_onApprove}
         >
-            Yes, do this metadata update
+          {t('Yes, do this metadata update')}
         </Button>
         <ActionBar className='btnReject'>
           <Link
             isDanger
             onClick={_onReject}
           >
-            Reject
+            {t('Reject')}
           </Link>
         </ActionBar>
       </div>
diff --git a/packages/extension-ui/src/Popup/Metadata/index.tsx b/packages/extension-ui/src/Popup/Metadata/index.tsx
index 7b227180..e410009b 100644
--- a/packages/extension-ui/src/Popup/Metadata/index.tsx
+++ b/packages/extension-ui/src/Popup/Metadata/index.tsx
@@ -5,15 +5,17 @@
 import React, { useContext } from 'react';
 
 import { Loading, MetadataReqContext } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { Header } from '../../partials';
 import Request from './Request';
 
 export default function Metadata (): React.ReactElement {
+  const { t } = useTranslation();
   const requests = useContext(MetadataReqContext);
 
   return (
     <>
-      <Header text='Metadata' />
+      <Header text={t('Metadata')} />
       {requests[0]
         ? (
           <Request
diff --git a/packages/extension-ui/src/Popup/RestoreJson.tsx b/packages/extension-ui/src/Popup/RestoreJson.tsx
index 7acb8a50..783b785f 100644
--- a/packages/extension-ui/src/Popup/RestoreJson.tsx
+++ b/packages/extension-ui/src/Popup/RestoreJson.tsx
@@ -3,12 +3,14 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { KeyringPair$Json } from '@polkadot/keyring/types';
+
 import React, { useCallback, useContext, useEffect, useState } from 'react';
-import { AccountContext, ActionContext, InputWithLabel, InputFileWithLabel, Button, Address } from '../components';
-import { u8aToString } from '@polkadot/util';
 import styled from 'styled-components';
-import { jsonRestore, jsonVerifyPassword, jsonVerifyFile } from '../messaging';
+import { u8aToString } from '@polkadot/util';
 
+import { AccountContext, ActionContext, InputWithLabel, InputFileWithLabel, Button, Address } from '../components';
+import useTranslation from '../hooks/useTranslation';
+import { jsonRestore, jsonVerifyPassword, jsonVerifyFile } from '../messaging';
 import { Header } from '../partials';
 
 interface FileState {
@@ -41,6 +43,7 @@ async function parseFile (file: Uint8Array): Promise<FileState> {
 }
 
 export default function Upload (): React.ReactElement {
+  const { t } = useTranslation();
   const { accounts } = useContext(AccountContext);
   const onAction = useContext(ActionContext);
   const [isBusy, setIsBusy] = useState(false);
@@ -93,7 +96,7 @@ export default function Upload (): React.ReactElement {
     <>
       <HeaderWithSmallerMargin
         showBackArrow
-        text='Restore from JSON'
+        text={t('Restore from JSON')}
       />
       <div>
         <div>
@@ -106,13 +109,13 @@ export default function Upload (): React.ReactElement {
         <InputFileWithLabel
           accept={acceptedFormats}
           isError={!isFileValid}
-          label={'backup file'}
+          label={t('backup file')}
           onChange={_onChangeFile}
           withLabel
         />
         <InputWithLabel
           isError={!isPassValid}
-          label='Password for this file'
+          label={t('Password for this file')}
           onChange={_onChangePass}
           type='password'
         />
@@ -121,7 +124,7 @@ export default function Upload (): React.ReactElement {
           isDisabled={!isFileValid || !isPassValid}
           onClick={_onRestore}
         >
-          Restore
+          {t('Restore')}
         </Button>
       </div>
     </>
diff --git a/packages/extension-ui/src/Popup/Signing/Bytes.tsx b/packages/extension-ui/src/Popup/Signing/Bytes.tsx
index e95766c0..cbe8e314 100644
--- a/packages/extension-ui/src/Popup/Signing/Bytes.tsx
+++ b/packages/extension-ui/src/Popup/Signing/Bytes.tsx
@@ -5,6 +5,8 @@
 import styled from 'styled-components';
 import React from 'react';
 
+import useTranslation from '../../hooks/useTranslation';
+
 interface Props {
   className?: string;
   bytes: string;
@@ -12,15 +14,17 @@ interface Props {
 }
 
 function Bytes ({ bytes, className, url }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
+
   return (
     <table className={className}>
       <tbody>
         <tr>
-          <td className='label'>from</td>
+          <td className='label'>{t('from')}</td>
           <td className='data'>{url}</td>
         </tr>
         <tr>
-          <td className='label'>bytes</td>
+          <td className='label'>{t('bytes')}</td>
           <td className='data'>{bytes}</td>
         </tr>
       </tbody>
diff --git a/packages/extension-ui/src/Popup/Signing/Extrinsic.tsx b/packages/extension-ui/src/Popup/Signing/Extrinsic.tsx
index 85262220..e4075e7c 100644
--- a/packages/extension-ui/src/Popup/Signing/Extrinsic.tsx
+++ b/packages/extension-ui/src/Popup/Signing/Extrinsic.tsx
@@ -12,6 +12,7 @@ import { formatNumber, bnToBn } from '@polkadot/util';
 
 import { Table } from '../../components';
 import useMetadata from '../../hooks/useMetadata';
+import useTranslation from '../../hooks/useTranslation';
 
 interface Decoded {
   args: AnyJson | null;
@@ -43,11 +44,11 @@ function decodeMethod (data: string, isDecoded: boolean, chain: Chain | null, sp
   return { args, method };
 }
 
-function renderMethod (data: string, { args, method }: Decoded): React.ReactNode {
+function renderMethod (data: string, { args, method }: Decoded, t: (key: string) => string): React.ReactNode {
   if (!args || !method) {
     return (
       <tr>
-        <td className='label'>method data</td>
+        <td className='label'>{t('method data')}</td>
         <td className='data'>{data}</td>
       </tr>
     );
@@ -56,7 +57,7 @@ function renderMethod (data: string, { args, method }: Decoded): React.ReactNode
   return (
     <>
       <tr>
-        <td className='label'>method</td>
+        <td className='label'>{t('method')}</td>
         <td className='data'>
           <details>
             <summary>{method.sectionName}.{method.methodName}{
@@ -70,7 +71,7 @@ function renderMethod (data: string, { args, method }: Decoded): React.ReactNode
       </tr>
       {method.meta && (
         <tr>
-          <td className='label'>info</td>
+          <td className='label'>{t('info')}</td>
           <td className='data'>
             <details>
               <summary>{method.meta.documentation.map((d) => d.toString().trim()).join(' ')}</summary>
@@ -82,18 +83,24 @@ function renderMethod (data: string, { args, method }: Decoded): React.ReactNode
   );
 }
 
-function mortalityAsString (era: ExtrinsicEra, hexBlockNumber: string): string {
+function mortalityAsString (era: ExtrinsicEra, hexBlockNumber: string, t: (key: string, options?: { replace: Record<string, string> }) => string): string {
   if (era.isImmortalEra) {
-    return 'immortal';
+    return t('immortal');
   }
 
   const blockNumber = bnToBn(hexBlockNumber);
   const mortal = era.asMortalEra;
 
-  return `mortal, valid from #${formatNumber(mortal.birth(blockNumber))} to #${formatNumber(mortal.death(blockNumber))}`;
+  return t('mortal, valid from {{birth}} to {{death}}', {
+    replace: {
+      birth: formatNumber(mortal.birth(blockNumber)),
+      death: formatNumber(mortal.death(blockNumber))
+    }
+  });
 }
 
 function Extrinsic ({ className, isDecoded, payload: { era, nonce, tip }, request: { blockNumber, genesisHash, method, specVersion: hexSpec }, url }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const chain = useMetadata(genesisHash);
   const specVersion = useRef(bnToBn(hexSpec)).current;
   const [decoded, setDecoded] = useState<Decoded>({ args: null, method: null });
@@ -108,31 +115,31 @@ function Extrinsic ({ className, isDecoded, payload: { era, nonce, tip }, reques
       isFull
     >
       <tr>
-        <td className='label'>from</td>
+        <td className='label'>{t('from')}</td>
         <td className='data'>{url}</td>
       </tr>
       <tr>
-        <td className='label'>{chain ? 'chain' : 'genesis'}</td>
+        <td className='label'>{chain ? t('chain') : t('genesis')}</td>
         <td className='data'>{chain ? chain.name : genesisHash}</td>
       </tr>
       <tr>
-        <td className='label'>version</td>
+        <td className='label'>{t('version')}</td>
         <td className='data'>{specVersion.toNumber()}</td>
       </tr>
       <tr>
-        <td className='label'>nonce</td>
+        <td className='label'>{t('nonce')}</td>
         <td className='data'>{formatNumber(nonce)}</td>
       </tr>
       {!tip.isEmpty && (
         <tr>
-          <td className='label'>tip</td>
+          <td className='label'>{t('tip')}</td>
           <td className='data'>{formatNumber(tip)}</td>
         </tr>
       )}
-      {renderMethod(method, decoded)}
+      {renderMethod(method, decoded, t)}
       <tr>
         <td className='label'>lifetime</td>
-        <td className='data'>{mortalityAsString(era, blockNumber)}</td>
+        <td className='data'>{mortalityAsString(era, blockNumber, t)}</td>
       </tr>
     </Table>
   );
diff --git a/packages/extension-ui/src/Popup/Signing/Qr.tsx b/packages/extension-ui/src/Popup/Signing/Qr.tsx
index efef59a1..70579bf5 100644
--- a/packages/extension-ui/src/Popup/Signing/Qr.tsx
+++ b/packages/extension-ui/src/Popup/Signing/Qr.tsx
@@ -10,6 +10,7 @@ import styled from 'styled-components';
 import { QrDisplayPayload, QrScanSignature } from '@polkadot/react-qr';
 
 import { Button } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 
 interface Props {
   children?: React.ReactNode;
@@ -22,6 +23,7 @@ interface Props {
 const CMD_MORTAL = 2;
 
 function Qr ({ className, onSignature, payload, request }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [isScanning, setIsScanning] = useState(false);
   const [payloadU8a, setPayloadU8a] = useState(new Uint8Array());
 
@@ -49,7 +51,7 @@ function Qr ({ className, onSignature, payload, request }: Props): React.ReactEl
           className='scanButton'
           onClick={_onShowQr}
         >
-          Scan signature via camera
+          {t('Scan signature via camera')}
         </Button>
       )}
     </div>
diff --git a/packages/extension-ui/src/Popup/Signing/Request.tsx b/packages/extension-ui/src/Popup/Signing/Request.tsx
index 30150c06..497b42bc 100644
--- a/packages/extension-ui/src/Popup/Signing/Request.tsx
+++ b/packages/extension-ui/src/Popup/Signing/Request.tsx
@@ -10,6 +10,7 @@ import React, { useCallback, useContext, useState, useEffect } from 'react';
 import { TypeRegistry } from '@polkadot/types';
 
 import { ActionBar, ActionContext, Address, Button, ButtonArea, Checkbox, Link, VerticalSpace } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { approveSignPassword, approveSignSignature, cancelSignRequest, isSignLocked } from '../../messaging';
 import Bytes from './Bytes';
 import Extrinsic from './Extrinsic';
@@ -39,6 +40,7 @@ function isRawPayload (payload: SignerPayloadJSON | SignerPayloadRaw): payload i
 }
 
 export default function Request ({ account: { isExternal }, buttonText, isFirst, request, signId, url }: Props): React.ReactElement<Props> | null {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
   const [{ hexBytes, payload }, setData] = useState<Data>({ hexBytes: null, payload: null });
   const [error, setError] = useState<string | null>(null);
@@ -123,7 +125,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
       >
         <Checkbox
           checked={!!isSavedPass}
-          label="Don't ask me again for the next 15 minutes"
+          label={t("Don't ask me again for the next 15 minutes")}
           onChange={setIsSavedPass}
         />
       </Unlock>
@@ -172,7 +174,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
               isDanger
               onClick={_onCancel}
             >
-              Cancel
+              {t('Cancel')}
             </Link>
           </CancelButton>
         </SignArea>
@@ -198,7 +200,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
               isDanger
               onClick={_onCancel}
             >
-              Reject
+              {t('Reject')}
             </Link>
           </CancelButton>
         </SignArea>
diff --git a/packages/extension-ui/src/Popup/Signing/Unlock.tsx b/packages/extension-ui/src/Popup/Signing/Unlock.tsx
index 17a75aa2..63424af5 100644
--- a/packages/extension-ui/src/Popup/Signing/Unlock.tsx
+++ b/packages/extension-ui/src/Popup/Signing/Unlock.tsx
@@ -5,6 +5,7 @@
 import React, { useCallback, useEffect, useState } from 'react';
 
 import { Button, InputWithLabel } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 
 interface Props {
   buttonText: string;
@@ -16,6 +17,7 @@ interface Props {
 }
 
 function Unlock ({ buttonText, children, className, error, isBusy, onSign }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [ownError, setError] = useState<string | null>();
   const [password, setPassword] = useState('');
 
@@ -42,7 +44,7 @@ function Unlock ({ buttonText, children, className, error, isBusy, onSign }: Pro
         disabled={isBusy}
         isError={!password || !!ownError}
         isFocused
-        label='Password for this account'
+        label={t('Password for this account')}
         onChange={_onChangePassword}
         onEnter={_onClick}
         type='password'
diff --git a/packages/extension-ui/src/Popup/Signing/index.tsx b/packages/extension-ui/src/Popup/Signing/index.tsx
index f0701aa2..42cbf876 100644
--- a/packages/extension-ui/src/Popup/Signing/index.tsx
+++ b/packages/extension-ui/src/Popup/Signing/index.tsx
@@ -7,11 +7,13 @@ import { SignerPayloadJSON } from '@polkadot/types/types';
 import React, { useCallback, useContext, useEffect, useState } from 'react';
 
 import { Loading, SigningReqContext } from '../../components';
+import useTranslation from '../../hooks/useTranslation';
 import { Header } from '../../partials';
 import Request from './Request';
 import TransactionIndex from './TransactionIndex';
 
 export default function Signing (): React.ReactElement {
+  const { t } = useTranslation();
   const requests = useContext(SigningReqContext);
   const [requestIndex, setRequestIndex] = useState(0);
 
@@ -46,7 +48,7 @@ export default function Signing (): React.ReactElement {
   return request
     ? (
       <>
-        <Header text={isTransaction ? 'Transaction' : 'Sign message'}>
+        <Header text={isTransaction ? t('Transaction') : t('Sign message')}>
           {requests.length > 1 && (
             <TransactionIndex
               index={requestIndex}
@@ -58,7 +60,7 @@ export default function Signing (): React.ReactElement {
         </Header>
         <Request
           account={request.account}
-          buttonText={isTransaction ? 'Sign the transaction' : 'Sign the message'}
+          buttonText={isTransaction ? t('Sign the transaction') : t('Sign the message')}
           isFirst={requestIndex === 0}
           request={request.request}
           signId={request.id}
diff --git a/packages/extension-ui/src/Popup/Welcome.tsx b/packages/extension-ui/src/Popup/Welcome.tsx
index 98064577..e550d99c 100644
--- a/packages/extension-ui/src/Popup/Welcome.tsx
+++ b/packages/extension-ui/src/Popup/Welcome.tsx
@@ -8,9 +8,11 @@ import React, { useContext } from 'react';
 import styled from 'styled-components';
 
 import { ActionContext, Box, Button, ButtonArea, List, VerticalSpace } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { Header } from '../partials';
 
 export default function Welcome (): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const onAction = useContext(ActionContext);
 
   const _onClick = (): void => {
@@ -20,19 +22,19 @@ export default function Welcome (): React.ReactElement<Props> {
 
   return (
     <>
-      <Header text='Welcome'/>
-      <Note>Before we start, just a couple of notes regarding use:</Note>
+      <Header text={t('Welcome')} />
+      <Note>{t('Before we start, just a couple of notes regarding use:')}</Note>
       <TextBox>
         <List>
-          <li>We do not send any clicks, pageviews or events to a central server</li>
-          <li>We do not use any trackers or analytics</li>
-          <li>We don&apos;t collect keys, addresses or any information - your information never leaves this machine</li>
+          <li>{t('We do not send any clicks, pageviews or events to a central server')}</li>
+          <li>{t('We do not use any trackers or analytics')}</li>
+          <li>{t("We don't collect keys, addresses or any information - your information never leaves this machine")}</li>
         </List>
       </TextBox>
-      <Note>... we are not in the information collection business (even anonymized).</Note>
+      <Note>{t('... we are not in the information collection business (even anonymized).')}</Note>
       <VerticalSpace />
       <ButtonArea>
-        <Button onClick={_onClick}>Understood, let me continue</Button>
+        <Button onClick={_onClick}>{t('Understood, let me continue')}</Button>
       </ButtonArea>
     </>
   );
diff --git a/packages/extension-ui/src/components/Address.tsx b/packages/extension-ui/src/components/Address.tsx
index b6591ee8..dc87988f 100644
--- a/packages/extension-ui/src/components/Address.tsx
+++ b/packages/extension-ui/src/components/Address.tsx
@@ -19,6 +19,7 @@ import parentArrow from '../assets/arrowParentLabel.svg';
 import { AccountContext, SettingsContext } from './contexts';
 import useOutsideClick from '../hooks/useOutsideClick';
 import useMetadata from '../hooks/useMetadata';
+import useTranslation from '../hooks/useTranslation';
 import useToast from '../hooks/useToast';
 import Identicon from './Identicon';
 import Menu from './Menu';
@@ -72,6 +73,7 @@ function recodeAddress (address: string, accounts: AccountWithChildren[], chain:
 const ACCOUNTS_SCREEN_HEIGHT = 550;
 
 function Address ({ actions, address, children, className, genesisHash, isHidden, name, parentName, suri, toggleActions }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const { accounts } = useContext(AccountContext);
   const settings = useContext(SettingsContext);
   const chain = useMetadata(genesisHash, true);
@@ -107,9 +109,9 @@ function Address ({ actions, address, children, className, genesisHash, isHidden
 
   const theme = ((chain && chain.icon) || 'polkadot') as 'polkadot';
   const _onClick = useCallback((): void => setShowActionsMenu(!showActionsMenu), [showActionsMenu]);
-  const _onCopy = useCallback((): void => show('Copied'), [show]);
+  const _onCopy = useCallback((): void => show(t('Copied')), [show, t]);
 
-  const displayedName = name || (account && account.name) || '<unknown>';
+  const displayedName = name || (account && account.name) || t('<unknown>');
 
   return (
     <div className={className}>
@@ -160,7 +162,7 @@ function Address ({ actions, address, children, className, genesisHash, isHidden
             </div>
           )}
           <div className='addressDisplay'>
-            <FullAddress data-field='address'>{formatted || '<unknown>'}</FullAddress>
+            <FullAddress data-field='address'>{formatted || t('<unknown>')}</FullAddress>
             <CopyToClipboard text={(formatted && formatted) || ''} >
               <Svg
                 onClick={_onCopy}
diff --git a/packages/extension-ui/src/components/InputFileWithLabel.tsx b/packages/extension-ui/src/components/InputFileWithLabel.tsx
index c149053e..9084c243 100644
--- a/packages/extension-ui/src/components/InputFileWithLabel.tsx
+++ b/packages/extension-ui/src/components/InputFileWithLabel.tsx
@@ -9,6 +9,7 @@ import Dropzone, { DropzoneRef } from 'react-dropzone';
 import styled from 'styled-components';
 import { formatNumber, isHex, u8aToString, hexToU8a } from '@polkadot/util';
 
+import useTranslation from '../hooks/useTranslation';
 import Label from './Label';
 
 function classes (...classNames: (boolean | null | string | undefined)[]): string {
@@ -59,6 +60,7 @@ function convertResult (result: ArrayBuffer, convertHex?: boolean): Uint8Array {
 }
 
 function InputFile ({ accept, className = '', clearContent, convertHex, isDisabled, isError = false, label, onChange, placeholder }: InputFileProps): React.ReactElement<InputFileProps> {
+  const { t } = useTranslation();
   const dropRef = createRef<DropzoneRef>();
   const [file, setFile] = useState<FileState | undefined>();
 
@@ -103,8 +105,13 @@ function InputFile ({ accept, className = '', clearContent, convertHex, isDisabl
           <em className='label' >
             {
               !file || clearContent
-                ? placeholder || 'click to select or drag and drop the file here'
-                : placeholder || `${file.name} (${formatNumber(file.size)} bytes)`
+                ? placeholder || t('click to select or drag and drop the file here')
+                : placeholder || t('{{name}} ({{size}} bytes)', {
+                  replace: {
+                    name: file.name,
+                    size: formatNumber(file.size)
+                  }
+                })
             }
           </em>
         </div>
diff --git a/packages/extension-ui/src/components/Loading.tsx b/packages/extension-ui/src/components/Loading.tsx
index ebc62f58..fd50cea5 100644
--- a/packages/extension-ui/src/components/Loading.tsx
+++ b/packages/extension-ui/src/components/Loading.tsx
@@ -4,14 +4,18 @@
 
 import React from 'react';
 
+import useTranslation from '../hooks/useTranslation';
+
 interface Props {
   children?: React.ReactNode;
 }
 
 export default function Loading ({ children }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
+
   if (!children) {
     return (
-      <div>... loading ...</div>
+      <div>{t('... loading ...')}</div>
     );
   }
 
diff --git a/packages/extension-ui/src/components/MnemonicSeed.tsx b/packages/extension-ui/src/components/MnemonicSeed.tsx
index a28a79f1..51308f67 100644
--- a/packages/extension-ui/src/components/MnemonicSeed.tsx
+++ b/packages/extension-ui/src/components/MnemonicSeed.tsx
@@ -8,6 +8,7 @@ import React, { MouseEventHandler } from 'react';
 import styled from 'styled-components';
 
 import copy from '../assets/copy.svg';
+import useTranslation from '../hooks/useTranslation';
 import ActionText from './ActionText';
 import TextAreaWithLabel from './TextAreaWithLabel';
 
@@ -18,12 +19,14 @@ interface Props {
 }
 
 function MnemonicSeed ({ className, onCopy, seed }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
+
   return (
     <div className={className}>
       <TextAreaWithLabel
         className='mnemonicDisplay'
         isReadOnly
-        label='Generated 12-word mnemonic seed:'
+        label={t('Generated 12-word mnemonic seed:')}
         value={seed}
       />
       <div className='buttonsRow'>
@@ -32,7 +35,7 @@ function MnemonicSeed ({ className, onCopy, seed }: Props): React.ReactElement<P
           data-seed-action='copy'
           icon={copy}
           onClick={onCopy}
-          text='Copy to clipboard'
+          text={t('Copy to clipboard')}
         />
       </div>
     </div>
diff --git a/packages/extension-ui/src/hooks/useTranslation.ts b/packages/extension-ui/src/hooks/useTranslation.ts
new file mode 100644
index 00000000..bac7035c
--- /dev/null
+++ b/packages/extension-ui/src/hooks/useTranslation.ts
@@ -0,0 +1,20 @@
+// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+interface TranslationOptions {
+  replace: Record<string, string | number>
+}
+
+interface Translation {
+  t: (key: string, options?: TranslationOptions) => string;
+}
+
+const defaultTranslation: Translation = {
+  t: (key: string, { replace }: TranslationOptions = { replace: {} }) =>
+    Object.entries(replace).reduce((result: string, [rk, rv]) => result.replace(`{{${rk}}}`, `${rv}`), key)
+};
+
+export default function useTranslation (): Translation {
+  return defaultTranslation;
+}
diff --git a/packages/extension-ui/src/partials/Back.tsx b/packages/extension-ui/src/partials/Back.tsx
index 08248197..c16f8de3 100644
--- a/packages/extension-ui/src/partials/Back.tsx
+++ b/packages/extension-ui/src/partials/Back.tsx
@@ -8,6 +8,7 @@ import React from 'react';
 import styled from 'styled-components';
 
 import ArrowLeftImage from '../assets/arrowLeft.svg';
+import useTranslation from '../hooks/useTranslation';
 import { Link, Svg } from '../components';
 
 interface Props extends ThemeProps {
@@ -16,11 +17,13 @@ interface Props extends ThemeProps {
 }
 
 function Back ({ className, to = '/' }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
+
   return (
     <div className={className}>
       <BackLink to={to}>
         <ArrowLeft />
-        Back
+        {t('Back')}
       </BackLink>
     </div>
   );
diff --git a/packages/extension-ui/src/partials/HeaderWithSteps.tsx b/packages/extension-ui/src/partials/HeaderWithSteps.tsx
index 421d4176..6c7068a5 100644
--- a/packages/extension-ui/src/partials/HeaderWithSteps.tsx
+++ b/packages/extension-ui/src/partials/HeaderWithSteps.tsx
@@ -55,6 +55,7 @@ export default React.memo(styled(HeaderWithSteps)(({ theme }: Props) => `
     display: flex;
     justify-content: space-between;
     flex-grow: 1;
+    padding-left: 1em;
     padding-right: 24px;
     margin-top: 3px;
   }
diff --git a/packages/extension-ui/src/partials/MenuAdd.tsx b/packages/extension-ui/src/partials/MenuAdd.tsx
index f585ba83..5bcd3adf 100644
--- a/packages/extension-ui/src/partials/MenuAdd.tsx
+++ b/packages/extension-ui/src/partials/MenuAdd.tsx
@@ -12,6 +12,7 @@ import plusIcon from '../assets/plus.svg';
 import qrIcon from '../assets/qr.svg';
 import seedIcon from '../assets/secret.svg';
 import { AccountContext, Link, MediaContext, Menu, MenuDivider, MenuItem, Svg } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { jsonRestoreWindowOpen } from '../messaging';
 
 interface Props extends ThemeProps {
@@ -22,6 +23,7 @@ interface Props extends ThemeProps {
 const isPopup = window.innerWidth <= 480;
 
 function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const { master } = useContext(AccountContext);
   const mediaAllowed = useContext(MediaContext);
 
@@ -39,8 +41,8 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
           <Svg src={plusIcon} />
           <span>{
             master
-              ? 'Create new account (root or derived)'
-              : 'Create new account'
+              ? t('Create new account (root or derived)')
+              : t('Create new account')
           }</span>
         </Link>
       </MenuItem>
@@ -48,7 +50,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
       <MenuItem className='menuItem'>
         <Link to='/account/import-seed'>
           <Svg src={seedIcon} />
-          <span>Import account from pre-existing seed</span>
+          <span>{t('Import account from pre-existing seed')}</span>
         </Link>
       </MenuItem>
       <MenuItem className='menuItem'>
@@ -57,7 +59,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
           to={isPopup ? undefined : '/account/restore-json'}
         >
           <Svg src={fileIcon} />
-          <span>Restore account from backup JSON file</span>
+          <span>{t('Restore account from backup JSON file')}</span>
         </Link>
       </MenuItem>
       <MenuDivider />
@@ -67,7 +69,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
           to='/account/import-qr'
         >
           <Svg src={qrIcon} />
-          <span>Attach external QR-signer account</span>
+          <span>{t('Attach external QR-signer account')}</span>
         </Link>
       </MenuItem>
     </Menu>
diff --git a/packages/extension-ui/src/partials/MenuSettings.tsx b/packages/extension-ui/src/partials/MenuSettings.tsx
index 635277b4..7fc77e4f 100644
--- a/packages/extension-ui/src/partials/MenuSettings.tsx
+++ b/packages/extension-ui/src/partials/MenuSettings.tsx
@@ -10,6 +10,7 @@ import settings from '@polkadot/ui-settings';
 
 import FullScreenIcon from '../assets/fullscreen.svg';
 import { ActionText, Checkbox, Dropdown, Menu, MenuDivider, MenuItem, Svg, Switch, ThemeSwitchContext, themes } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { windowOpen } from '../messaging';
 
 interface Option {
@@ -22,12 +23,13 @@ interface Props extends ThemeProps {
   reference: React.MutableRefObject<null>;
 }
 
-const isPopup = window.innerWidth <= 480;
+const isPopup = window.innerWidth <= 560;
 const prefixOptions = settings.availablePrefixes
   .filter(({ value }) => value !== -1)
   .map(({ text, value }): Option => ({ text, value: `${value}` }));
 
 function MenuSettings ({ className, reference }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [camera, setCamera] = useState(settings.camera === 'on');
   const [prefix, setPrefix] = useState(`${settings.prefix === -1 ? 42 : settings.prefix}`);
   const themeContext = useContext<Theme>(ThemeContext);
@@ -69,19 +71,19 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
       <MenuDivider />
       <MenuItem
         className='setting'
-        title='External QR accounts and Access'
+        title={t('External QR accounts and Access')}
       >
         <Checkbox
           checked={camera}
           className='checkbox'
-          label='Allow Camera Access'
+          label={t('Allow Camera Access')}
           onChange={setCamera}
         />
       </MenuItem>
       <MenuDivider />
       <MenuItem
         className='setting'
-        title='Display address format For'
+        title={t('Display address format For')}
       >
         <Dropdown
           className='dropdown'
@@ -99,7 +101,7 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
               className='openWindow'
               icon={FullScreenIcon}
               onClick={windowOpen}
-              text='Open extension in new window'
+              text={t('Open extension in new window')}
             />
           </MenuItem>
         </>
diff --git a/packages/extension-ui/src/partials/Name.tsx b/packages/extension-ui/src/partials/Name.tsx
index 4840f968..5e5248ae 100644
--- a/packages/extension-ui/src/partials/Name.tsx
+++ b/packages/extension-ui/src/partials/Name.tsx
@@ -5,6 +5,7 @@
 import React, { useContext } from 'react';
 
 import { AccountContext, InputWithLabel, ValidatedInput } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { isNotShorterThan } from '../util/validators';
 
 interface Props {
@@ -16,11 +17,14 @@ interface Props {
   onChange: (name: string | null) => void;
 }
 
+// FIXME i18n
 const isNameValid = isNotShorterThan(3, 'Account name is too short');
 
-export default function Name ({ address, className, isFocused, label = 'A descriptive name for your account', onBlur, onChange }: Props): React.ReactElement<Props> {
+export default function Name ({ address, className, isFocused, label, onBlur, onChange }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const { accounts } = useContext(AccountContext);
-  const account = accounts.find((account): boolean => account.address === address);
+
+  const account = accounts.find((account) => account.address === address);
   const startValue = account && account.name;
 
   return (
@@ -30,7 +34,7 @@ export default function Name ({ address, className, isFocused, label = 'A descri
       data-input-name
       defaultValue={startValue}
       isFocused={isFocused}
-      label={label}
+      label={label || t('A descriptive name for your account')}
       onBlur={onBlur}
       onValidatedChange={onChange}
       type='text'
diff --git a/packages/extension-ui/src/partials/Password.tsx b/packages/extension-ui/src/partials/Password.tsx
index 64c5a7bc..7044d6c6 100644
--- a/packages/extension-ui/src/partials/Password.tsx
+++ b/packages/extension-ui/src/partials/Password.tsx
@@ -5,6 +5,7 @@
 import React, { useState, useEffect } from 'react';
 
 import { InputWithLabel, ValidatedInput } from '../components';
+import useTranslation from '../hooks/useTranslation';
 import { allOf, isNotShorterThan, isSameAs, Validator } from '../util/validators';
 
 interface Props {
@@ -13,6 +14,8 @@ interface Props {
 }
 
 const MIN_LENGTH = 6;
+
+// FIXME i18n
 const isFirstPasswordValid = isNotShorterThan(MIN_LENGTH, 'Password is too short');
 const isSecondPasswordValid = (firstPassword: string): Validator<string> => allOf(
   isNotShorterThan(MIN_LENGTH, 'Password is too short'),
@@ -20,6 +23,7 @@ const isSecondPasswordValid = (firstPassword: string): Validator<string> => allO
 );
 
 export default function Password ({ isFocussed, onChange }: Props): React.ReactElement<Props> {
+  const { t } = useTranslation();
   const [pass1, setPass1] = useState<string | null>(null);
   const [pass2, setPass2] = useState<string | null>(null);
 
@@ -33,7 +37,7 @@ export default function Password ({ isFocussed, onChange }: Props): React.ReactE
         component={InputWithLabel}
         data-input-password
         isFocused={isFocussed}
-        label='A new password for this account'
+        label={t('A new password for this account')}
         onValidatedChange={setPass1}
         type='password'
         validator={isFirstPasswordValid}
@@ -42,7 +46,7 @@ export default function Password ({ isFocussed, onChange }: Props): React.ReactE
         <ValidatedInput
           component={InputWithLabel}
           data-input-repeat-password
-          label='Repeat password for verification'
+          label={t('Repeat password for verification')}
           onValidatedChange={setPass2}
           type='password'
           validator={isSecondPasswordValid(pass1)}
diff --git a/packages/extension/public/locales/en/translation.json b/packages/extension/public/locales/en/translation.json
new file mode 100644
index 00000000..f182b0ed
--- /dev/null
+++ b/packages/extension/public/locales/en/translation.json
@@ -0,0 +1,98 @@
+{
+  "Copied": "",
+  "<unknown>": "",
+  "click to select or drag and drop the file here": "",
+  "{{name}} ({{size}} bytes)": "",
+  "... loading ...": "",
+  "Generated 12-word mnemonic seed:": "",
+  "Copy to clipboard": "",
+  "Back": "",
+  "Create new account (root or derived)": "",
+  "Create new account": "",
+  "Import account from pre-existing seed": "",
+  "Restore account from backup JSON file": "",
+  "Attach external QR-signer account": "",
+  "External QR accounts and Access": "",
+  "Allow Camera Access": "",
+  "Display address format For": "",
+  "Open extension in new window": "",
+  "A descriptive name for your account": "",
+  "A new password for this account": "",
+  "Repeat password for verification": "",
+  "Rename": "",
+  "Derive New Account": "",
+  "Export Account": "",
+  "Forget Account": "",
+  "Visible (always inject)": "",
+  "You currently don&apos;t have any accounts. Create your first account to get started.": "",
+  "Accounts": "",
+  "Authorize": "",
+  "Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.": "",
+  "Yes, allow this application access": "",
+  "Add the account with the generated seed": "",
+  "Cancel": "",
+  "Create an account": "",
+  "Please write down your wallet’s mnemonic seed and keep it in a safe place. The mnemonic can be used to restore your wallet. Keep it carefully to not lose your assets.": "",
+  "I have saved my mnemonic seed safely.": "",
+  "Next step": "",
+  "Derivation Path": "",
+  "Derivation Path (unlock to edit)": "",
+  "//hard/soft": "",
+  "Add new account": "",
+  "Create derived account": "",
+  "Derive new account from existing": "",
+  "Choose Parent Account:": "",
+  "enter the password for the account you want to derive from": "",
+  "Create a derived account": "",
+  "Create account from new seed": "",
+  "Export account": "",
+  "You are exporting your account. Keep it safe and don&apos;t share it with anyone.": "",
+  "password for this account": "",
+  "I want to export this account": "",
+  "Forget account": "",
+  "You are about to remove the account. This means that you will not be able to access it via this extension anymore. If you wish to recover it, you would need to use the seed.": "",
+  "I want to forget this account": "",
+  "Scan Address Qr": "",
+  "Add the account with identified address": "",
+  "Import account": "",
+  "existing 12 or 24-word mnemonic seed": "",
+  "Add the account with the supplied seed": "",
+  "Metadata": "",
+  "from": "",
+  "chain": "",
+  "icon": "",
+  "decimals": "",
+  "symbol": "",
+  "upgrade": "",
+  "This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.": "",
+  "Yes, do this metadata update": "",
+  "Reject": "",
+  "Restore from JSON": "",
+  "backup file": "",
+  "Password for this file": "",
+  "Restore": "",
+  "bytes": "",
+  "method data": "",
+  "method": "",
+  "info": "",
+  "immortal": "",
+  "mortal, valid from {{birth}} to {{death}}": "",
+  "genesis": "",
+  "version": "",
+  "nonce": "",
+  "tip": "",
+  "Transaction": "",
+  "Sign message": "",
+  "Sign the transaction": "",
+  "Sign the message": "",
+  "Scan signature via camera": "",
+  "Don't ask me again for the next 15 minutes": "",
+  "Password for this account": "",
+  "Welcome": "",
+  "Before we start, just a couple of notes regarding use:": "",
+  "We do not send any clicks, pageviews or events to a central server": "",
+  "We do not use any trackers or analytics": "",
+  "We don't collect keys, addresses or any information - your information never leaves this machine": "",
+  "... we are not in the information collection business (even anonymized).": "",
+  "Understood, let me continue": ""
+}
diff --git a/yarn.lock b/yarn.lock
index 2658b5b2..d5b6336e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1400,7 +1400,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.11.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4":
+"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.11.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4":
   version: 7.11.2
   resolution: "@babel/runtime@npm:7.11.2"
   dependencies:
@@ -2890,42 +2890,42 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@polkadot/api-derive@npm:1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/api-derive@npm:1.32.0-beta.18"
+"@polkadot/api-derive@npm:1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/api-derive@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/api": 1.32.0-beta.18
-    "@polkadot/rpc-core": 1.32.0-beta.18
-    "@polkadot/rpc-provider": 1.32.0-beta.18
-    "@polkadot/types": 1.32.0-beta.18
+    "@polkadot/api": 1.32.0-beta.19
+    "@polkadot/rpc-core": 1.32.0-beta.19
+    "@polkadot/rpc-provider": 1.32.0-beta.19
+    "@polkadot/types": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     "@polkadot/util-crypto": ^3.4.1
     bn.js: ^5.1.3
     memoizee: ^0.4.14
     rxjs: ^6.6.2
-  checksum: 9d6ebb0ed02a0f969aa82192acad567c7907be23d568c8aa1d8ce84f5d4638e66e6a3c0cb3f856cc993d66a98a407c20cc4c7f06b233b9f2b1ad2cd24d5594ab
+  checksum: 175b973c314b85c795c6dc0eda1918dc81a82db545afcdbe98a43d723b5f138046cb53ce897803610735a257d354c192827d29fe92e135925e022b098e53b427
   languageName: node
   linkType: hard
 
-"@polkadot/api@npm:^1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/api@npm:1.32.0-beta.18"
+"@polkadot/api@npm:^1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/api@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/api-derive": 1.32.0-beta.18
+    "@polkadot/api-derive": 1.32.0-beta.19
     "@polkadot/keyring": ^3.4.1
-    "@polkadot/metadata": 1.32.0-beta.18
-    "@polkadot/rpc-core": 1.32.0-beta.18
-    "@polkadot/rpc-provider": 1.32.0-beta.18
-    "@polkadot/types": 1.32.0-beta.18
-    "@polkadot/types-known": 1.32.0-beta.18
+    "@polkadot/metadata": 1.32.0-beta.19
+    "@polkadot/rpc-core": 1.32.0-beta.19
+    "@polkadot/rpc-provider": 1.32.0-beta.19
+    "@polkadot/types": 1.32.0-beta.19
+    "@polkadot/types-known": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     "@polkadot/util-crypto": ^3.4.1
     bn.js: ^5.1.3
     eventemitter3: ^4.0.7
     rxjs: ^6.6.2
-  checksum: 7d197efc12d9eb20f04d4a2dd6df3df7fab9615c2e6fb07ac8070cf91aa53d6585830f5922081fdc1128b757ce1dbbb54bff3505d09dd363353c9dbb78fdca9c
+  checksum: 6ad788be0503d14f680387eb7106ffa097607057a68184461649762e61e38d15e9b55532047e08892235550c3590ec203b62a7a56507a1d09b24760da98f81e1
   languageName: node
   linkType: hard
 
@@ -3028,7 +3028,7 @@ __metadata:
   resolution: "@polkadot/extension-base@workspace:packages/extension-base"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/api": ^1.32.0-beta.18
+    "@polkadot/api": ^1.32.0-beta.19
     "@polkadot/extension-inject": 0.33.0-beta.18
     "@polkadot/keyring": ^3.4.1
     "@polkadot/ui-keyring": ^0.59.0-beta.0
@@ -3136,17 +3136,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@polkadot/metadata@npm:1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/metadata@npm:1.32.0-beta.18"
+"@polkadot/metadata@npm:1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/metadata@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/types": 1.32.0-beta.18
-    "@polkadot/types-known": 1.32.0-beta.18
+    "@polkadot/types": 1.32.0-beta.19
+    "@polkadot/types-known": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     "@polkadot/util-crypto": ^3.4.1
     bn.js: ^5.1.3
-  checksum: cc36a0e045c5d634d23698f458e7c140ff4e30472382f3e1c9d37e57b03080b289aedb50404f5fbbfd1235033cf2501634f0714db5f17d84e62139581f7b2b0e
+  checksum: 0b5f7c5a7eb6fb916e7c21184927124d17d9ed1c0f37868a425ffca7b9abbd1e3662cb1277b3eb57c829104928bd514f0e66988f7f83b966ea9e9b94783839c7
   languageName: node
   linkType: hard
 
@@ -3189,49 +3189,49 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@polkadot/rpc-core@npm:1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/rpc-core@npm:1.32.0-beta.18"
+"@polkadot/rpc-core@npm:1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/rpc-core@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/metadata": 1.32.0-beta.18
-    "@polkadot/rpc-provider": 1.32.0-beta.18
-    "@polkadot/types": 1.32.0-beta.18
+    "@polkadot/metadata": 1.32.0-beta.19
+    "@polkadot/rpc-provider": 1.32.0-beta.19
+    "@polkadot/types": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     memoizee: ^0.4.14
     rxjs: ^6.6.2
-  checksum: 549397b17856459020630a093a9721bf95540d487c33c40c88b75c21186c4ffa3bbf5eec1372035a17f1d1e2eafc179a389da9141ba31b3a4f517f5847b32c21
+  checksum: db4288ddf2404ba57a1cbb4cb1b6083251514c8b030c9d145bd8517ca0d21ffaec386d52550b9643997b0b7b9d220b67eacd2891cb6439fad575f1ea8e849009
   languageName: node
   linkType: hard
 
-"@polkadot/rpc-provider@npm:1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/rpc-provider@npm:1.32.0-beta.18"
+"@polkadot/rpc-provider@npm:1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/rpc-provider@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/metadata": 1.32.0-beta.18
-    "@polkadot/types": 1.32.0-beta.18
+    "@polkadot/metadata": 1.32.0-beta.19
+    "@polkadot/types": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     "@polkadot/util-crypto": ^3.4.1
     bn.js: ^5.1.3
     eventemitter3: ^4.0.7
     isomorphic-fetch: ^2.2.1
     websocket: ^1.0.32
-  checksum: d75760d69667519fbe6dfaa33447c7e0b506ceb276f089c612454a7b11b76da9a1b19e8b7d3afba7d5ab29b1a6bdc019e7545f5de99538ab6923a3740035d5b4
+  checksum: 724228e6c055b34abe4ce1f462f61ac8412d80bd4ba934d5afed57041686ce1190a8324216cdf600ca3f14064754fe1ceb54ccf3fb31e4861b4d0b8a439c408e
   languageName: node
   linkType: hard
 
-"@polkadot/typegen@npm:^1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/typegen@npm:1.32.0-beta.18"
+"@polkadot/typegen@npm:^1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/typegen@npm:1.32.0-beta.19"
   dependencies:
     "@babel/core": ^7.11.5
     "@babel/register": ^7.11.5
     "@babel/runtime": ^7.11.2
-    "@polkadot/api": 1.32.0-beta.18
-    "@polkadot/metadata": 1.32.0-beta.18
-    "@polkadot/rpc-provider": 1.32.0-beta.18
-    "@polkadot/types": 1.32.0-beta.18
+    "@polkadot/api": 1.32.0-beta.19
+    "@polkadot/metadata": 1.32.0-beta.19
+    "@polkadot/rpc-provider": 1.32.0-beta.19
+    "@polkadot/types": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     handlebars: ^4.7.6
     websocket: ^1.0.32
@@ -3242,35 +3242,35 @@ __metadata:
     polkadot-types-from-defs: scripts/polkadot-types-from-defs.js
     polkadot-types-internal-interfaces: scripts/polkadot-types-internal-interfaces.js
     polkadot-types-internal-metadata: scripts/polkadot-types-internal-metadata.js
-  checksum: addb4c895591d8740d8b315fc4997c8356b7c178bfc87744c5541cd4defd31087bfffb54ac3d60f09f61ae8b8ae44dafc025eddfce71def6b782f30f95dbbdb7
+  checksum: 80c64cb897536e166a062b4e22db82853cf736dbd4c3011f0bea70cb01c369f9e8b3c93170a57858354502e2e9e6c4e3db29cfa5c847ab988e48eb0456677457
   languageName: node
   linkType: hard
 
-"@polkadot/types-known@npm:1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/types-known@npm:1.32.0-beta.18"
+"@polkadot/types-known@npm:1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/types-known@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/types": 1.32.0-beta.18
+    "@polkadot/types": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     bn.js: ^5.1.3
-  checksum: f39a400ce0dbcafc8c7d8b86c4227ddda5504bacaa70f9a507bdfc4fd4dd57ce19f9893990b8b7403d0db9f3056cf34061ee75e7fe77a72546c69d3e24ad43d6
+  checksum: 89cd41aa9118a740366b6543c16f837858672135dfff38777f0574eaa27e0738d4d986bc47e77059a84e40a68918bc4938e714f3daab2be51939d46e51c75e25
   languageName: node
   linkType: hard
 
-"@polkadot/types@npm:^1.32.0-beta.18":
-  version: 1.32.0-beta.18
-  resolution: "@polkadot/types@npm:1.32.0-beta.18"
+"@polkadot/types@npm:^1.32.0-beta.19":
+  version: 1.32.0-beta.19
+  resolution: "@polkadot/types@npm:1.32.0-beta.19"
   dependencies:
     "@babel/runtime": ^7.11.2
-    "@polkadot/metadata": 1.32.0-beta.18
+    "@polkadot/metadata": 1.32.0-beta.19
     "@polkadot/util": ^3.4.1
     "@polkadot/util-crypto": ^3.4.1
     "@types/bn.js": ^4.11.6
     bn.js: ^5.1.3
     memoizee: ^0.4.14
     rxjs: ^6.6.2
-  checksum: f68ca0d6aff2ff7dd59880eeae37c2622c5571f7d170800645d74b85281269d34cf0603e2a07b78d065578041d4f18736de768f948fdfa85180bef8b48b2cb87
+  checksum: 574ffd1cf5cb9ed1497d9190ab603f2a422433875693f265c5dace18ced157b49ed5eaf43936180b2b8b50856a41f116f23da757c38d6d28d9d5416bcd712762
   languageName: node
   linkType: hard
 
@@ -4549,6 +4549,44 @@ __metadata:
   languageName: node
   linkType: hard
 
+"acorn-bigint@npm:^0.4.0":
+  version: 0.4.0
+  resolution: "acorn-bigint@npm:0.4.0"
+  peerDependencies:
+    acorn: ^6.0.0
+  checksum: 8eb8e2439a2efbcecf7356c8baa6973c229aaf5f6e420367f144118e426a30e09d50c424e48bb7b2958b477b9d27b99c876706e116ffd7b6f1684954b4010891
+  languageName: node
+  linkType: hard
+
+"acorn-class-fields@npm:^0.3.1":
+  version: 0.3.7
+  resolution: "acorn-class-fields@npm:0.3.7"
+  dependencies:
+    acorn-private-class-elements: ^0.2.7
+  peerDependencies:
+    acorn: ^6 || ^7 || ^8
+  checksum: 293aafcb03cbc224e7c2c5352f9980f1bff0565681c47074c90b82171514f9d42b928f32c8010c19a6afe5af2881458ef67e1cbcdd6f4062dd38e1bf3a825b6d
+  languageName: node
+  linkType: hard
+
+"acorn-dynamic-import@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "acorn-dynamic-import@npm:4.0.0"
+  peerDependencies:
+    acorn: ^6.0.0
+  checksum: ce61df77522b5eaee93f4a61043af9b059590d382bdbf5bf4a3717416d0cfb7bebbacb736d82764ba51381d2d55db4b27477384bb7e2642283941b52bcd9d008
+  languageName: node
+  linkType: hard
+
+"acorn-export-ns-from@npm:^0.1.0":
+  version: 0.1.0
+  resolution: "acorn-export-ns-from@npm:0.1.0"
+  peerDependencies:
+    acorn: ^6.0.1
+  checksum: e38b49d9dc071ba46b52709f6870311fb8144c199d832bc5e73e794c55a2b13cfb13477502a970184f8d9df5de51fef28c3f75840634fa165d6a17dc251cdabf
+  languageName: node
+  linkType: hard
+
 "acorn-globals@npm:^6.0.0":
   version: 6.0.0
   resolution: "acorn-globals@npm:6.0.0"
@@ -4559,6 +4597,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"acorn-import-meta@npm:^1.0.0":
+  version: 1.1.0
+  resolution: "acorn-import-meta@npm:1.1.0"
+  peerDependencies:
+    acorn: ^6.0.0 || ^7.0.0
+  checksum: 16facde86452de81c367bc31dcc90778b7b762cd6f666b1ddd7fec375e0261aa7a758a5f118cad0bc94b1acfbf5036c4bf657788693832b2db4ad3bc5b49732f
+  languageName: node
+  linkType: hard
+
 "acorn-jsx@npm:^5.2.0":
   version: 5.2.0
   resolution: "acorn-jsx@npm:5.2.0"
@@ -4568,6 +4615,81 @@ __metadata:
   languageName: node
   linkType: hard
 
+"acorn-logical-assignment@npm:^0.1.0":
+  version: 0.1.4
+  resolution: "acorn-logical-assignment@npm:0.1.4"
+  peerDependencies:
+    acorn: ^6 || ^7 || ^8
+  checksum: 27146e907a45b14be13b9884119cec35339458f9f17ebd4f0d00e902db2b2b97cf102eff63a7691aca80512d95022e0eaf4952aa9120cbe59c390c611168cba3
+  languageName: node
+  linkType: hard
+
+"acorn-numeric-separator@npm:^0.3.0":
+  version: 0.3.6
+  resolution: "acorn-numeric-separator@npm:0.3.6"
+  peerDependencies:
+    acorn: ^6 || ^7 || ^8
+  checksum: cc6f801804aa9859b034790d0d46ccd58d96793d8f2a3b1454c303fcb16b634cb630da9b1ea121066bdcf6b50d207a0f5f63642eeeddf4deef2a85b613b5c5bd
+  languageName: node
+  linkType: hard
+
+"acorn-private-class-elements@npm:^0.2.7":
+  version: 0.2.7
+  resolution: "acorn-private-class-elements@npm:0.2.7"
+  peerDependencies:
+    acorn: ^6.1.0 || ^7 || ^8
+  checksum: fd6edc936a30c28622ce30dedaedc815ba64c845219bbf0b965b44466d4a0c8c3e8c02037085c9eecf4ffff5179441c8e0639689164735b24d30c61a70fbb351
+  languageName: node
+  linkType: hard
+
+"acorn-private-methods@npm:^0.3.0":
+  version: 0.3.3
+  resolution: "acorn-private-methods@npm:0.3.3"
+  dependencies:
+    acorn-private-class-elements: ^0.2.7
+  peerDependencies:
+    acorn: ^6 || ^7 || ^8
+  checksum: dac318d03bfd104a0594c056a7aaa37386d7b08c2c72a7aa8d06e1c7b71c47888696db533a1e04e997d078bbba4e49b39561af4b0be8e4e5f08eb5ff971f15a1
+  languageName: node
+  linkType: hard
+
+"acorn-stage3@npm:^2.0.0":
+  version: 2.1.0
+  resolution: "acorn-stage3@npm:2.1.0"
+  dependencies:
+    acorn-bigint: ^0.4.0
+    acorn-class-fields: ^0.3.1
+    acorn-dynamic-import: ^4.0.0
+    acorn-export-ns-from: ^0.1.0
+    acorn-import-meta: ^1.0.0
+    acorn-logical-assignment: ^0.1.0
+    acorn-numeric-separator: ^0.3.0
+    acorn-private-methods: ^0.3.0
+    acorn-static-class-features: ^0.2.0
+  peerDependencies:
+    acorn: ^6.1.0
+  checksum: 20c62583ce9f4d86220894238fa83837b72ed604d9bd90baba8c30ac2902daddc8ed35a3d88006188f4c6c9795aa7a13b2ae7770a3db7993b0f75c8306036b46
+  languageName: node
+  linkType: hard
+
+"acorn-static-class-features@npm:^0.2.0":
+  version: 0.2.4
+  resolution: "acorn-static-class-features@npm:0.2.4"
+  dependencies:
+    acorn-private-class-elements: ^0.2.7
+  peerDependencies:
+    acorn: ^6.1.0 || ^7 || ^8
+  checksum: 432363817de3b8792e652f72f38230fe72d19fd34efa701b01b8f966622c97be3d64d2310f24468b76f11d4495b019b33e027d9618d9a941c94d5e7f91bd13bd
+  languageName: node
+  linkType: hard
+
+"acorn-walk@npm:^6.0.0":
+  version: 6.2.0
+  resolution: "acorn-walk@npm:6.2.0"
+  checksum: 3bd8415090ecfcf0a40e9bdde722993104d209d8e7721b48d9c77c46fb1dd261cc29ae0ee47e6532db9fbfe96d911b19ec0d72a383b20ed331364ab18d35b75b
+  languageName: node
+  linkType: hard
+
 "acorn-walk@npm:^7.1.1":
   version: 7.2.0
   resolution: "acorn-walk@npm:7.2.0"
@@ -4575,7 +4697,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"acorn@npm:^6.4.1":
+"acorn@npm:^6.0.0, acorn@npm:^6.4.1":
   version: 6.4.1
   resolution: "acorn@npm:6.4.1"
   bin:
@@ -4871,6 +4993,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"append-buffer@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "append-buffer@npm:1.0.2"
+  dependencies:
+    buffer-equal: ^1.0.0
+  checksum: 3f205f30482cedf135f4aaf1266845ea13b237196c10e42ac6628604708b6b7bd9128abfa0a5c6351a0832fafa0855ecdb0eed979816b1b43278f063684f8e27
+  languageName: node
+  linkType: hard
+
 "application-config-path@npm:^0.1.0":
   version: 0.1.0
   resolution: "application-config-path@npm:0.1.0"
@@ -6394,6 +6525,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"buffer-equal@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "buffer-equal@npm:1.0.0"
+  checksum: c071efa3279c6af39333dc879acd0cdf5245c22e547d50cca4cc2225f50042fea7950031437e6db81a952c663b56e84aaabb80ee06980bfeca24573768e19553
+  languageName: node
+  linkType: hard
+
 "buffer-from@npm:^1.0.0":
   version: 1.1.1
   resolution: "buffer-from@npm:1.1.1"
@@ -7108,7 +7246,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"clone-deep@npm:^4.0.1":
+"clone-buffer@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "clone-buffer@npm:1.0.0"
+  checksum: 70d92e1482af3cfc4e1f1f620ac2a34c0a441da4760f46a250a5f75df7b5f054f27c4358467ba3915832b2a9674b469fa93b7f9e3bc97ade3227e20952ea3404
+  languageName: node
+  linkType: hard
+
+"clone-deep@npm:^4.0.0, clone-deep@npm:^4.0.1":
   version: 4.0.1
   resolution: "clone-deep@npm:4.0.1"
   dependencies:
@@ -7137,6 +7282,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"clone-stats@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "clone-stats@npm:1.0.0"
+  checksum: fc70411afba2115e5b2800b76ad434a79e0fe81d7ad8016c9351937fbec14a10c56314c5df545549bc5046a4b21b7bb2ee45a6393dcccf279abd5554c04629f2
+  languageName: node
+  linkType: hard
+
 "clone@npm:^1.0.2":
   version: 1.0.4
   resolution: "clone@npm:1.0.4"
@@ -7144,6 +7296,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"clone@npm:^2.1.1":
+  version: 2.1.2
+  resolution: "clone@npm:2.1.2"
+  checksum: 85232d66015d2d703dc59812e30049931d97c7815bf70569ae4fb7a66be257f46fcf47040e4e7050966ca195a9e615d59d73ba9e39fc37eedba1a76865f27ab1
+  languageName: node
+  linkType: hard
+
+"cloneable-readable@npm:^1.0.0":
+  version: 1.1.3
+  resolution: "cloneable-readable@npm:1.1.3"
+  dependencies:
+    inherits: ^2.0.1
+    process-nextick-args: ^2.0.0
+    readable-stream: ^2.3.5
+  checksum: b7dda8125e898a788ac6427003337920532658b8b62b8ae3ae1c45ca877e3159c51c260efeb89f5a23b18bc62031bfcd5d2dfe5beada4a657d93f5fd2c8b7e50
+  languageName: node
+  linkType: hard
+
 "co@npm:^4.6.0":
   version: 4.6.0
   resolution: "co@npm:4.6.0"
@@ -7285,6 +7455,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"commander@npm:^3.0.1":
+  version: 3.0.2
+  resolution: "commander@npm:3.0.2"
+  checksum: 28071a49d23606c1e31f1f275d2aac9c03833cb3442e04b798ffbcd5f7fb7a1fbfc6b5bc6a69b862df83f475378665623788118d189c254a173248c452bd77a2
+  languageName: node
+  linkType: hard
+
 "commander@npm:^4.0.1, commander@npm:^4.1.1":
   version: 4.1.1
   resolution: "commander@npm:4.1.1"
@@ -7593,7 +7770,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"convert-source-map@npm:^1.1.0, convert-source-map@npm:^1.4.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0":
+"convert-source-map@npm:^1.1.0, convert-source-map@npm:^1.4.0, convert-source-map@npm:^1.5.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0":
   version: 1.7.0
   resolution: "convert-source-map@npm:1.7.0"
   dependencies:
@@ -8998,6 +9175,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"editions@npm:^2.2.0":
+  version: 2.3.1
+  resolution: "editions@npm:2.3.1"
+  dependencies:
+    errlop: ^2.0.0
+    semver: ^6.3.0
+  checksum: 9942b48d9611680868077ad7bf568bc31978f4b0df52b061586c5f865b8654c655daedeea1e2ea8bc023f259c9493b13d938a0216a5fce4eaabaf62ebdfe12dc
+  languageName: node
+  linkType: hard
+
 "ee-first@npm:1.1.1":
   version: 1.1.1
   resolution: "ee-first@npm:1.1.1"
@@ -9114,6 +9301,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"ensure-array@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "ensure-array@npm:1.0.0"
+  checksum: 92d530ab676ce54d7fcc4a0bf64a85e0a1375283a6a1009482439e630ef55639b480a7463bbbfdfb7c2dff5906b2996271367cd88f5dbb09f41705f6161c8403
+  languageName: node
+  linkType: hard
+
 "entities@npm:^1.1.1, entities@npm:~1.1.1":
   version: 1.1.2
   resolution: "entities@npm:1.1.2"
@@ -9233,6 +9427,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"eol@npm:^0.9.1":
+  version: 0.9.1
+  resolution: "eol@npm:0.9.1"
+  checksum: c77ee68dbe5505f06bb3033d2294437385e579175248123613b36fce9ee707e0f94fae111e7beafb75f656794b5e96b709b9de2dfb2ac5d6ad10cda4cf6de99c
+  languageName: node
+  linkType: hard
+
 "err-code@npm:^1.0.0":
   version: 1.1.2
   resolution: "err-code@npm:1.1.2"
@@ -9240,6 +9441,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"errlop@npm:^2.0.0":
+  version: 2.2.0
+  resolution: "errlop@npm:2.2.0"
+  checksum: 0a82c77722d19b455db129e4cfc1df12ffeea55453262945aea038a662d927c10181b8ce2c1b53a96cb52ca50efb7852cb47b923428c7fe8a81f8778198e5ccb
+  languageName: node
+  linkType: hard
+
 "errno@npm:^0.1.3, errno@npm:~0.1.7":
   version: 0.1.7
   resolution: "errno@npm:0.1.7"
@@ -10456,7 +10664,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"flush-write-stream@npm:^1.0.0":
+"flush-write-stream@npm:^1.0.0, flush-write-stream@npm:^1.0.2":
   version: 1.1.1
   resolution: "flush-write-stream@npm:1.1.1"
   dependencies:
@@ -10606,6 +10814,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"fs-mkdirp-stream@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "fs-mkdirp-stream@npm:1.0.0"
+  dependencies:
+    graceful-fs: ^4.1.11
+    through2: ^2.0.3
+  checksum: a432e19f94af5eefa6a5268ada83994caf94b4ba4898116261ce541c8680858144e23258cb6239aa49a991f94f51e07411e9b2612df1e6ea1f41be094e3e1f67
+  languageName: node
+  linkType: hard
+
 "fs-readdir-recursive@npm:^1.1.0":
   version: 1.1.0
   resolution: "fs-readdir-recursive@npm:1.1.0"
@@ -11026,6 +11244,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"glob-stream@npm:^6.1.0":
+  version: 6.1.0
+  resolution: "glob-stream@npm:6.1.0"
+  dependencies:
+    extend: ^3.0.0
+    glob: ^7.1.1
+    glob-parent: ^3.1.0
+    is-negated-glob: ^1.0.0
+    ordered-read-streams: ^1.0.0
+    pumpify: ^1.3.5
+    readable-stream: ^2.1.5
+    remove-trailing-separator: ^1.0.1
+    to-absolute-glob: ^2.0.0
+    unique-stream: ^2.0.2
+  checksum: b453b3da5a4507722597cfaf0b93756caa09cc8d7cdd13aabce04104804dc5b010afc93d72bcbd87b2f3f8f600e953a42bb2ebdeeb62586eba016fc9961b86b7
+  languageName: node
+  linkType: hard
+
 "glob-to-regexp@npm:^0.3.0":
   version: 0.3.0
   resolution: "glob-to-regexp@npm:0.3.0"
@@ -11273,7 +11509,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.3, graceful-fs@npm:^4.2.4":
+"graceful-fs@npm:^4.0.0, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.3, graceful-fs@npm:^4.2.4":
   version: 4.2.4
   resolution: "graceful-fs@npm:4.2.4"
   checksum: d095ee4dc6eacc76814cd52d5d185b860119378a6fd4888e7d4e94983095c54d4f6369942a5e3d759cdbdd4e3ee7eaeb27a39ff938c6ee4610894fd9de46b6cb
@@ -11308,6 +11544,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"gulp-sort@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "gulp-sort@npm:2.0.0"
+  dependencies:
+    through2: ^2.0.1
+  checksum: 4f43385ecf68fd230147bbb1702bbac2cc8970cfa008b18bc0915cf5c553da15fc0178b4b07a5fd37847733f4c36cdd981c2abec5b912523ef471d8a36308c78
+  languageName: node
+  linkType: hard
+
 "handle-thing@npm:^2.0.0":
   version: 2.0.1
   resolution: "handle-thing@npm:2.0.1"
@@ -11871,6 +12116,45 @@ __metadata:
   languageName: node
   linkType: hard
 
+"i18next-scanner@npm:^2.11.0":
+  version: 2.11.0
+  resolution: "i18next-scanner@npm:2.11.0"
+  dependencies:
+    acorn: ^6.0.0
+    acorn-dynamic-import: ^4.0.0
+    acorn-jsx: ^5.2.0
+    acorn-stage3: ^2.0.0
+    acorn-walk: ^6.0.0
+    chalk: ^2.4.1
+    clone-deep: ^4.0.0
+    commander: ^3.0.1
+    deepmerge: ^4.0.0
+    ensure-array: ^1.0.0
+    eol: ^0.9.1
+    esprima: ^4.0.0
+    gulp-sort: ^2.0.0
+    i18next: "*"
+    lodash: ^4.0.0
+    parse5: ^5.0.0
+    sortobject: ^1.1.1
+    through2: ^3.0.1
+    vinyl: ^2.2.0
+    vinyl-fs: ^3.0.1
+  bin:
+    i18next-scanner: bin/cli.js
+  checksum: 6c7397220c5b8ea38e87a4aede1a0028a18dd5a7b2ce8837e0839bcf5121b4748ac4c01a42a982007caf7fca5f52bb9ebd481a63d482656e0bfb98de9adee5c9
+  languageName: node
+  linkType: hard
+
+"i18next@npm:*":
+  version: 19.7.0
+  resolution: "i18next@npm:19.7.0"
+  dependencies:
+    "@babel/runtime": ^7.10.1
+  checksum: 7f6fa6576c64c3a89b5228ef69a6229ddcb6aa2ec7a20375f0663c4fb4ead5442d9ef00e59053f6c73b2665df990a75e0fd446e0e2a5b640d92662a109f19734
+  languageName: node
+  linkType: hard
+
 "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24":
   version: 0.4.24
   resolution: "iconv-lite@npm:0.4.24"
@@ -12230,6 +12514,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-absolute@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "is-absolute@npm:1.0.0"
+  dependencies:
+    is-relative: ^1.0.0
+    is-windows: ^1.0.1
+  checksum: 4b8ebda658ee6d820cc9ef77c7228f5048e4824cd0092415d41d94ae9d59d8cf27cb1a322958161059be1887d7d8bc85ef10e1444ed763731b45229b74bb7ad1
+  languageName: node
+  linkType: hard
+
 "is-accessor-descriptor@npm:^0.1.6":
   version: 0.1.6
   resolution: "is-accessor-descriptor@npm:0.1.6"
@@ -12589,6 +12883,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-negated-glob@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "is-negated-glob@npm:1.0.0"
+  checksum: add3803c20bc80cfc8151eb618fe9ee537d2cf5dfa77080100c3262207943627fc562e671d457f88bcc52b788dc588fdaf2bdbfa753f02d1449912aab5286069
+  languageName: node
+  linkType: hard
+
 "is-negative-zero@npm:^2.0.0":
   version: 2.0.0
   resolution: "is-negative-zero@npm:2.0.0"
@@ -12799,6 +13100,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-relative@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "is-relative@npm:1.0.0"
+  dependencies:
+    is-unc-path: ^1.0.0
+  checksum: a93a7b57d8fa2090757eb2193a58fcf318cd2963787d25cd756842b75c1d78a814105245deec16303a0df3e9263dbb587d55545ad684d6035b3534016a2bc4b3
+  languageName: node
+  linkType: hard
+
 "is-resolvable@npm:^1.0.0":
   version: 1.1.0
   resolution: "is-resolvable@npm:1.1.0"
@@ -12884,6 +13194,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-unc-path@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "is-unc-path@npm:1.0.0"
+  dependencies:
+    unc-path-regex: ^0.1.2
+  checksum: ee43c89aa0dcf0292e50b0994cdb02d8c14bebea54d87f447915374982a66ffdf6e24780c7306c23a454a083c5b05a87dc84c9432bb17bbeddb1a4c6e52575c0
+  languageName: node
+  linkType: hard
+
 "is-url@npm:^1.1.0, is-url@npm:^1.2.4":
   version: 1.2.4
   resolution: "is-url@npm:1.2.4"
@@ -12891,13 +13210,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"is-utf8@npm:^0.2.0":
+"is-utf8@npm:^0.2.0, is-utf8@npm:^0.2.1":
   version: 0.2.1
   resolution: "is-utf8@npm:0.2.1"
   checksum: c72f604d72b72f6a57f9b2e22c9b6f480e869b3f0efe141bd1dfbc36655225043ca8c1316ff8e343ef641cf80868c9e4a37345492f31402abd5ab68e09367977
   languageName: node
   linkType: hard
 
+"is-valid-glob@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "is-valid-glob@npm:1.0.0"
+  checksum: 7d61129ee5051409cd8b1ba738d1ce7108a20b39f297671f2c07a1d2737fc13d0cc476261b9a5fa6491252e6dbf0bc7cef04444b14455e929b285fddc2f48895
+  languageName: node
+  linkType: hard
+
 "is-whitespace-character@npm:^1.0.0":
   version: 1.0.4
   resolution: "is-whitespace-character@npm:1.0.4"
@@ -13880,6 +14206,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"lazystream@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "lazystream@npm:1.0.0"
+  dependencies:
+    readable-stream: ^2.0.5
+  checksum: c5f628687ddbc762965814d80d80faa44e0c2ece207eee5783cbc656dc230c46bd18002719ea41a5f15646754070f67be11f2b8c2c1f04084f2395a355d84cb8
+  languageName: node
+  linkType: hard
+
 "lcov-parse@npm:^1.0.0":
   version: 1.0.0
   resolution: "lcov-parse@npm:1.0.0"
@@ -13889,6 +14224,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"lead@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "lead@npm:1.0.0"
+  dependencies:
+    flush-write-stream: ^1.0.2
+  checksum: 8cac773a199e178e2904cc2bf5392c841b825dbe49394da8ee062d69d6399a6931f6fc6b4c399e3c94f171e8f002e7b539beea9e90c0b4bc79711838bb6b2558
+  languageName: node
+  linkType: hard
+
 "lerna@npm:^3.22.1":
   version: 3.22.1
   resolution: "lerna@npm:3.22.1"
@@ -14213,7 +14557,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lodash@npm:^4.15.0, lodash@npm:^4.16.3, lodash@npm:^4.17.11, lodash@npm:^4.17.12, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.3, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.2.1":
+"lodash@npm:^4.0.0, lodash@npm:^4.15.0, lodash@npm:^4.16.3, lodash@npm:^4.17.11, lodash@npm:^4.17.12, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.3, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.2.1":
   version: 4.17.20
   resolution: "lodash@npm:4.17.20"
   checksum: c62101d2500c383b5f174a7e9e6fe8098149ddd6e9ccfa85f36d4789446195f5c4afd3cfba433026bcaf3da271256566b04a2bf2618e5a39f6e67f8c12030cb6
@@ -15700,6 +16044,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"now-and-later@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "now-and-later@npm:2.0.1"
+  dependencies:
+    once: ^1.3.2
+  checksum: c3130e565f3ac4b8d3b6b329d2f8a8391c844a7c8718bc4747b77d1b431fa5259be92d8e88ba1dd0f79963539146dae735f46d48540e079367a917a9adad7488
+  languageName: node
+  linkType: hard
+
 "npm-bundled@npm:^1.0.1":
   version: 1.1.1
   resolution: "npm-bundled@npm:1.1.1"
@@ -15891,7 +16244,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"object.assign@npm:^4.1.0":
+"object.assign@npm:^4.0.4, object.assign@npm:^4.1.0":
   version: 4.1.0
   resolution: "object.assign@npm:4.1.0"
   dependencies:
@@ -15997,7 +16350,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0":
+"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.3.2, once@npm:^1.4.0":
   version: 1.4.0
   resolution: "once@npm:1.4.0"
   dependencies:
@@ -16098,6 +16451,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"ordered-read-streams@npm:^1.0.0":
+  version: 1.0.1
+  resolution: "ordered-read-streams@npm:1.0.1"
+  dependencies:
+    readable-stream: ^2.0.1
+  checksum: f650ae7590d2696284001016bd4012200a9d4df41b7b25909019cdd8df207abb6ac84d70c91370808745bc0512933f54c34a668a7afc5ada384d8acc7b4380c4
+  languageName: node
+  linkType: hard
+
 "original@npm:^1.0.0":
   version: 1.0.2
   resolution: "original@npm:1.0.2"
@@ -16486,7 +16848,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"parse5@npm:5.1.1":
+"parse5@npm:5.1.1, parse5@npm:^5.0.0":
   version: 5.1.1
   resolution: "parse5@npm:5.1.1"
   checksum: fad72ff5010ee8a6f0a38b83fc886b71a54d746d5c4ff5aad74d6ba1fe87b9606585bf32aa200b015ce329e0906f50f2851f29876abeacd5c13567c7a0455362
@@ -17454,6 +17816,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"process-nextick-args@npm:^2.0.0, process-nextick-args@npm:~2.0.0":
+  version: 2.0.1
+  resolution: "process-nextick-args@npm:2.0.1"
+  checksum: ddeb0f07d0d5efa649c2c5e39d1afd0e3668df2b392d036c8a508b0034f7beffbc474b3c2f7fd3fed2dc4113cef8f1f7e00d05690df3c611b36f6c7efd7852d1
+  languageName: node
+  linkType: hard
+
 "process-nextick-args@npm:~1.0.6":
   version: 1.0.7
   resolution: "process-nextick-args@npm:1.0.7"
@@ -17461,13 +17830,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"process-nextick-args@npm:~2.0.0":
-  version: 2.0.1
-  resolution: "process-nextick-args@npm:2.0.1"
-  checksum: ddeb0f07d0d5efa649c2c5e39d1afd0e3668df2b392d036c8a508b0034f7beffbc474b3c2f7fd3fed2dc4113cef8f1f7e00d05690df3c611b36f6c7efd7852d1
-  languageName: node
-  linkType: hard
-
 "process@npm:^0.11.10":
   version: 0.11.10
   resolution: "process@npm:0.11.10"
@@ -17638,7 +18000,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"pumpify@npm:^1.3.3":
+"pumpify@npm:^1.3.3, pumpify@npm:^1.3.5":
   version: 1.5.1
   resolution: "pumpify@npm:1.5.1"
   dependencies:
@@ -18101,7 +18463,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"readable-stream@npm:1 || 2, readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.6, readable-stream@npm:^2.1.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.6, readable-stream@npm:~2.3.6":
+"readable-stream@npm:1 || 2, readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.5, readable-stream@npm:^2.0.6, readable-stream@npm:^2.1.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.6, readable-stream@npm:~2.3.6":
   version: 2.3.7
   resolution: "readable-stream@npm:2.3.7"
   dependencies:
@@ -18500,6 +18862,27 @@ __metadata:
   languageName: node
   linkType: hard
 
+"remove-bom-buffer@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "remove-bom-buffer@npm:3.0.0"
+  dependencies:
+    is-buffer: ^1.1.5
+    is-utf8: ^0.2.1
+  checksum: c80bef6cb3788f37ea81bfc355c77da77f5c8963c50c4fdf20769e5804068e74f57dd6321b56c1c27a319768cbd8ad320afd92f4a63faf9b3c80ee06b4c9cb10
+  languageName: node
+  linkType: hard
+
+"remove-bom-stream@npm:^1.2.0":
+  version: 1.2.0
+  resolution: "remove-bom-stream@npm:1.2.0"
+  dependencies:
+    remove-bom-buffer: ^3.0.0
+    safe-buffer: ^5.1.0
+    through2: ^2.0.3
+  checksum: 88a3f3004e0bb812276dc0ce1dd7760666440dad8ef621beb3620ddf5ede54bb35107042f0326b29113793519758a7661bb48d15892d0ce2f262051a5254576e
+  languageName: node
+  linkType: hard
+
 "remove-markdown@npm:^0.2.2":
   version: 0.2.2
   resolution: "remove-markdown@npm:0.2.2"
@@ -18557,6 +18940,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"replace-ext@npm:^1.0.0":
+  version: 1.0.1
+  resolution: "replace-ext@npm:1.0.1"
+  checksum: 29b0f4ec6fda1591eb9b7c2d300b3a099f61ab0f6870ac5c62a5fa1cc8208085b8c5bf77684e76dcddfc37734831449c92ac488bc2ba9d899476db6be9b4240c
+  languageName: node
+  linkType: hard
+
 "request-promise-core@npm:1.1.4":
   version: 1.1.4
   resolution: "request-promise-core@npm:1.1.4"
@@ -18714,6 +19104,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"resolve-options@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "resolve-options@npm:1.1.0"
+  dependencies:
+    value-or-function: ^3.0.0
+  checksum: a9387bac0c242b9346e9c1af24b9054eaa4ef5923ceee0be53d592ffb47506c5e1780dbbfa9f21bf631587635e7779eca7a5e8362187c1d6855407dc1dab1067
+  languageName: node
+  linkType: hard
+
 "resolve-pathname@npm:^3.0.0":
   version: 3.0.0
   resolution: "resolve-pathname@npm:3.0.0"
@@ -18866,9 +19265,10 @@ __metadata:
   dependencies:
     "@babel/core": ^7.11.5
     "@polkadot/dev": ^0.55.45
-    "@polkadot/typegen": ^1.32.0-beta.18
+    "@polkadot/typegen": ^1.32.0-beta.19
     "@types/jest": ^26.0.13
     copy-webpack-plugin: ^6.1.0
+    i18next-scanner: ^2.11.0
     sinon-chrome: ^3.0.1
     stylelint: ^13.7.0
     stylelint-config-recommended: ^3.0.0
@@ -19585,6 +19985,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"sortobject@npm:^1.1.1":
+  version: 1.3.0
+  resolution: "sortobject@npm:1.3.0"
+  dependencies:
+    editions: ^2.2.0
+  checksum: 221858d753fcec9fb0de73d2cc5eae238a3f61800ca1380326431d5c542debdcb43ec3615ae332d090930564091ff8594426e150c52c9e1ac7a2e26f5be3f791
+  languageName: node
+  linkType: hard
+
 "source-list-map@npm:^2.0.0":
   version: 2.0.1
   resolution: "source-list-map@npm:2.0.1"
@@ -20704,7 +21113,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"through2@npm:^2.0.0, through2@npm:^2.0.2, through2@npm:~2.0.3":
+"through2-filter@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "through2-filter@npm:3.0.0"
+  dependencies:
+    through2: ~2.0.0
+    xtend: ~4.0.0
+  checksum: 0b667941b8970bb32221cc10d10c58bbe49c80abbc39bdb0f741e03fb442f5235f4df2ff79f1539c1fdf3c90bedb69d1db34640c84eb9582da7044eb5ce19e3d
+  languageName: node
+  linkType: hard
+
+"through2@npm:^2.0.0, through2@npm:^2.0.1, through2@npm:^2.0.2, through2@npm:^2.0.3, through2@npm:~2.0.0, through2@npm:~2.0.3":
   version: 2.0.5
   resolution: "through2@npm:2.0.5"
   dependencies:
@@ -20714,7 +21133,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"through2@npm:^3.0.0":
+"through2@npm:^3.0.0, through2@npm:^3.0.1":
   version: 3.0.2
   resolution: "through2@npm:3.0.2"
   dependencies:
@@ -20818,6 +21237,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"to-absolute-glob@npm:^2.0.0":
+  version: 2.0.2
+  resolution: "to-absolute-glob@npm:2.0.2"
+  dependencies:
+    is-absolute: ^1.0.0
+    is-negated-glob: ^1.0.0
+  checksum: b2f4257e042a8923526f91ab1983ca3de33478ad0a12a945ef625387925ae11c47aabe8a45c234f86285a39dfa3caf1c9bede5363147d9b648ad30f44efbf78b
+  languageName: node
+  linkType: hard
+
 "to-arraybuffer@npm:^1.0.0":
   version: 1.0.1
   resolution: "to-arraybuffer@npm:1.0.1"
@@ -20893,6 +21322,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"to-through@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "to-through@npm:2.0.0"
+  dependencies:
+    through2: ^2.0.3
+  checksum: c4b135b0984fb88e8462032cf4d8ed744c01f3eee869892260ea4b9a981994e7665d1774f2c5682a455a6a59c7f0b4bda70298540d3ccbf5ef04c8847f738534
+  languageName: node
+  linkType: hard
+
 "toggle-selection@npm:^1.0.6":
   version: 1.0.6
   resolution: "toggle-selection@npm:1.0.6"
@@ -21299,6 +21737,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"unc-path-regex@npm:^0.1.2":
+  version: 0.1.2
+  resolution: "unc-path-regex@npm:0.1.2"
+  checksum: 585e29357917a8b529e05db14a3f2e9486258a5826ca9c0eb4f9173c006968ceffba201766d2ff08d38a1e014b69c519294981b29e669a81ea357c0ffd6e326b
+  languageName: node
+  linkType: hard
+
 "unherit@npm:^1.0.4":
   version: 1.1.3
   resolution: "unherit@npm:1.1.3"
@@ -21398,6 +21843,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"unique-stream@npm:^2.0.2":
+  version: 2.3.1
+  resolution: "unique-stream@npm:2.3.1"
+  dependencies:
+    json-stable-stringify-without-jsonify: ^1.0.1
+    through2-filter: ^3.0.0
+  checksum: 9064f196d58c10ff774c340659e4c7f45f578176dc282212ecfa3864174159fe0ec2895e0804e5da22f23df92d3772cc02ba2510734fcb2ff17b8d975e0d9dab
+  languageName: node
+  linkType: hard
+
 "unique-string@npm:^1.0.0":
   version: 1.0.0
   resolution: "unique-string@npm:1.0.0"
@@ -21817,6 +22272,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"value-or-function@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "value-or-function@npm:3.0.0"
+  checksum: ea8dfbd31d4e7336a1b5a8f2dc0dc6e623b3eb843e116170e094d952842542e5f29a565a15eda424ae1174897d7abf7fd8a21cd9284dbebf4dbd6611bd59e159
+  languageName: node
+  linkType: hard
+
 "vary@npm:~1.1.2":
   version: 1.1.2
   resolution: "vary@npm:1.1.2"
@@ -21872,6 +22334,60 @@ __metadata:
   languageName: node
   linkType: hard
 
+"vinyl-fs@npm:^3.0.1":
+  version: 3.0.3
+  resolution: "vinyl-fs@npm:3.0.3"
+  dependencies:
+    fs-mkdirp-stream: ^1.0.0
+    glob-stream: ^6.1.0
+    graceful-fs: ^4.0.0
+    is-valid-glob: ^1.0.0
+    lazystream: ^1.0.0
+    lead: ^1.0.0
+    object.assign: ^4.0.4
+    pumpify: ^1.3.5
+    readable-stream: ^2.3.3
+    remove-bom-buffer: ^3.0.0
+    remove-bom-stream: ^1.2.0
+    resolve-options: ^1.1.0
+    through2: ^2.0.0
+    to-through: ^2.0.0
+    value-or-function: ^3.0.0
+    vinyl: ^2.0.0
+    vinyl-sourcemap: ^1.1.0
+  checksum: d12dc2e6f644014e4e4f9c612d9693f9f15ebf5dd830fc15c63fe9102ee10b879afb93ff43b91e2abecea833b66ca44ccaf5a2f30bbe7b0e7bf1311ffe5127ca
+  languageName: node
+  linkType: hard
+
+"vinyl-sourcemap@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "vinyl-sourcemap@npm:1.1.0"
+  dependencies:
+    append-buffer: ^1.0.2
+    convert-source-map: ^1.5.0
+    graceful-fs: ^4.1.6
+    normalize-path: ^2.1.1
+    now-and-later: ^2.0.0
+    remove-bom-buffer: ^3.0.0
+    vinyl: ^2.0.0
+  checksum: 372d6f0797596bb7d86a3c86029a93f08a98816a615e418d482c77a2de95de5f65f46f6b488a56994d8dcb639b16b1e680b19e41e993479ef4729fc65cc45fb4
+  languageName: node
+  linkType: hard
+
+"vinyl@npm:^2.0.0, vinyl@npm:^2.2.0":
+  version: 2.2.0
+  resolution: "vinyl@npm:2.2.0"
+  dependencies:
+    clone: ^2.1.1
+    clone-buffer: ^1.0.0
+    clone-stats: ^1.0.0
+    cloneable-readable: ^1.0.0
+    remove-trailing-separator: ^1.0.1
+    replace-ext: ^1.0.0
+  checksum: a8c27638aafd77078d210e7ff0eff5724ec39cca1c5e8997b9c39024023e65037bcb4f775fd7903d8099a4d32b135950f937174a945619082c3ce704e66cd0a0
+  languageName: node
+  linkType: hard
+
 "vm-browserify@npm:^1.0.1":
   version: 1.1.2
   resolution: "vm-browserify@npm:1.1.2"
@@ -22661,7 +23177,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"xtend@npm:>=4.0.0 <4.1.0-0, xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:~4.0.1":
+"xtend@npm:>=4.0.0 <4.1.0-0, xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:~4.0.0, xtend@npm:~4.0.1":
   version: 4.0.2
   resolution: "xtend@npm:4.0.2"
   checksum: 37ee522a3e9fb9b143a400c30b21dc122aa8c9c9411c6afae1005a4617dc20a21765c114d544e37a6bb60c2733dd8ee0a44ed9e80d884ac78cccd30b5e0ab0da


Test patch:
diff --git a/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx b/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx
index 24e3de83..6c5b47cb 100644
--- a/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx
+++ b/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx
@@ -76,7 +76,7 @@ describe('Create Account', () => {
     it('clicking on Next activates phase 2', () => {
       check(wrapper.find('input[type="checkbox"]'));
       wrapper.find('button').simulate('click');
-      expect(wrapper.find(Header).text()).toBe('Create an account: 2/2Cancel');
+      expect(wrapper.find(Header).text()).toBe('Create an account2/2Cancel');
     });
   });