Swap to actual react-i18next (#431)

* Swap to actual react-i18next

* Update i18n creation
This commit is contained in:
Jaco Greeff 2020-09-04 16:32:42 +02:00 committed by GitHub
parent fddb842085
commit 206c0b4d85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 183 additions and 118 deletions

View File

@ -13,9 +13,12 @@
"@polkadot/react-qr": "^0.59.0-beta.0", "@polkadot/react-qr": "^0.59.0-beta.0",
"@polkadot/util": "^3.4.1", "@polkadot/util": "^3.4.1",
"@polkadot/util-crypto": "^3.4.1", "@polkadot/util-crypto": "^3.4.1",
"i18next": "^19.7.0",
"i18next-browser-languagedetector": "^5.0.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-dropzone": "^11.0.3", "react-dropzone": "^11.0.3",
"react-i18next": "^11.7.2",
"react-is": "^16.13.1", "react-is": "^16.13.1",
"react-router": "^5.2.0", "react-router": "^5.2.0",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",

View File

@ -69,14 +69,14 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
className='menuItem' className='menuItem'
onClick={_toggleEdit} onClick={_toggleEdit}
> >
{t('Rename')} {t<string>('Rename')}
</Link> </Link>
{!isExternal && ( {!isExternal && (
<Link <Link
className='menuItem' className='menuItem'
to={`/account/derive/${address}/locked`} to={`/account/derive/${address}/locked`}
> >
{t('Derive New Account')} {t<string>('Derive New Account')}
</Link> </Link>
)} )}
<MenuDivider /> <MenuDivider />
@ -86,7 +86,7 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
isDanger isDanger
to={`/account/export/${address}`} to={`/account/export/${address}`}
> >
{t('Export Account')} {t<string>('Export Account')}
</Link> </Link>
)} )}
<Link <Link
@ -94,14 +94,14 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
isDanger isDanger
to={`/account/forget/${address}`} to={`/account/forget/${address}`}
> >
{t('Forget Account')} {t<string>('Forget Account')}
</Link> </Link>
<MenuDivider /> <MenuDivider />
<div className='menuItem'> <div className='menuItem'>
<Checkbox <Checkbox
checked={!isHidden} checked={!isHidden}
className='inputItem' className='inputItem'
label={t('Visible (always inject)')} label={t<string>('Visible (always inject)')}
onClick={_toggleVisibility} onClick={_toggleVisibility}
/> />
</div> </div>

View File

@ -36,7 +36,7 @@ function AddAccount ({ className }: Props): React.ReactElement<Props> {
<AddAccountImage onClick={_onClick}/> <AddAccountImage onClick={_onClick}/>
</div> </div>
<div className='no-accounts'> <div className='no-accounts'>
<p>{t("You currently don't have any accounts. Create your first account to get started.")}</p> <p>{t<string>("You currently don't have any accounts. Create your first account to get started.")}</p>
</div> </div>
</div> </div>
</> </>

View File

@ -24,7 +24,7 @@ export default function Accounts (): React.ReactElement {
<Header <Header
showAdd showAdd
showSettings showSettings
text={t('Accounts')} text={t<string>('Accounts')}
/> />
<AccountsArea> <AccountsArea>
{hierarchy.map((json, index): React.ReactNode => ( {hierarchy.map((json, index): React.ReactNode => (

View File

@ -6,6 +6,7 @@ import { RequestAuthorizeTab } from '@polkadot/extension-base/background/types';
import { ThemeProps } from '../../types'; import { ThemeProps } from '../../types';
import React, { useCallback, useContext } from 'react'; import React, { useCallback, useContext } from 'react';
import { Trans } from 'react-i18next';
import styled from 'styled-components'; import styled from 'styled-components';
import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../components'; import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../components';
@ -38,7 +39,6 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop
[authId, onAction] [authId, onAction]
); );
// FIXME i18next <Trans /> component to be used here
return ( return (
<div className={className}> <div className={className}>
<RequestInfo> <RequestInfo>
@ -48,20 +48,21 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop
onClick={_onReject} onClick={_onReject}
/> />
<div className='tab-info'> <div className='tab-info'>
An application, self-identifying as <span className='tab-name'>{origin}</span> is requesting access from{' '} <Trans key='accessRequest'>An application, self-identifying as <span className='tab-name'>{origin}</span> is requesting access from{' '}
<a <a
href={url} href={url}
rel='noopener noreferrer' rel='noopener noreferrer'
target='_blank' target='_blank'
> >
<span className='tab-url'>{url}</span> <span className='tab-url'>{url}</span>
</a>. </a>.
</Trans>
</div> </div>
</Info> </Info>
{isFirst && ( {isFirst && (
<> <>
<RequestWarning>{t('Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.')}</RequestWarning> <RequestWarning>{t<string>('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> <AcceptButton onClick={_onApprove}>{t<string>('Yes, allow this application access')}</AcceptButton>
</> </>
)} )}
<RejectButton> <RejectButton>

View File

@ -17,7 +17,7 @@ export default function Authorize (): React.ReactElement {
return ( return (
<> <>
<Scroll isLastRequest={requests.length === 1}> <Scroll isLastRequest={requests.length === 1}>
<Header text={t('Authorize')} /> <Header text={t<string>('Authorize')} />
{requests.map(({ id, request, url }, index): React.ReactNode => ( {requests.map(({ id, request, url }, index): React.ReactNode => (
<Request <Request
authId={id} authId={id}

View File

@ -41,7 +41,7 @@ function AccountName ({ isBusy, onBackClick, onCreate }: Props): React.ReactElem
isDisabled={!password || !name} isDisabled={!password || !name}
onClick={_onCreate} onClick={_onCreate}
> >
{t('Add the account with the generated seed')} {t<string>('Add the account with the generated seed')}
</NextStepButton> </NextStepButton>
</ButtonArea> </ButtonArea>
</> </>

View File

@ -40,11 +40,11 @@ function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
onCopy={_onCopy} onCopy={_onCopy}
seed={seed} seed={seed}
/> />
<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> <Warning>{t<string>("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 /> <VerticalSpace />
<Checkbox <Checkbox
checked={isMnemonicSaved} checked={isMnemonicSaved}
label={t('I have saved my mnemonic seed safely.')} label={t<string>('I have saved my mnemonic seed safely.')}
onChange={setIsMnemonicSaved} onChange={setIsMnemonicSaved}
/> />
<ButtonArea> <ButtonArea>
@ -52,7 +52,7 @@ function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
isDisabled={!isMnemonicSaved} isDisabled={!isMnemonicSaved}
onClick={onNextStep} onClick={onNextStep}
> >
{t('Next step')} {t<string>('Next step')}
</NextStepButton> </NextStepButton>
</ButtonArea> </ButtonArea>
</> </>

View File

@ -49,7 +49,7 @@ export default function CreateAccount (): React.ReactElement {
<> <>
<HeaderWithSteps <HeaderWithSteps
step={step} step={step}
text={t('Create an account')} text={t<string>('Create an account')}
/> />
<Loading> <Loading>
<div> <div>

View File

@ -47,7 +47,7 @@ function DerivationPath ({ className, defaultPath, isError, onChange }: Props):
: t('Derivation Path (unlock to edit)') : t('Derivation Path (unlock to edit)')
} }
onChange={_onChange} onChange={_onChange}
placeholder={t('//hard/soft')} placeholder={t<string>('//hard/soft')}
value={path} value={path}
/> />
</PathInput> </PathInput>

View File

@ -95,7 +95,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
{!isLocked && ( {!isLocked && (
<CheckboxWithSmallerMargins <CheckboxWithSmallerMargins
checked={shouldAccountBeDerived} checked={shouldAccountBeDerived}
label={t('Derive new account from existing')} label={t<string>('Derive new account from existing')}
onChange={setShouldAccountBeDerived} onChange={setShouldAccountBeDerived}
/> />
)} )}
@ -108,7 +108,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
/> />
) )
: ( : (
<Label label={t('Choose Parent Account:')}> <Label label={t<string>('Choose Parent Account:')}>
<AddressDropdown <AddressDropdown
allAddresses={allAddresses} allAddresses={allAddresses}
onSelect={_onParentChange} onSelect={_onParentChange}
@ -123,7 +123,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
data-export-password data-export-password
isError={!isProperParentPassword} isError={!isProperParentPassword}
isFocused isFocused
label={t('enter the password for the account you want to derive from')} label={t<string>('enter the password for the account you want to derive from')}
onChange={_onParentPasswordEnter} onChange={_onParentPasswordEnter}
type='password' type='password'
value={parentPassword} value={parentPassword}
@ -148,7 +148,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
isDisabled={!isProperParentPassword || !suriPath} isDisabled={!isProperParentPassword || !suriPath}
onClick={_onSubmit} onClick={_onSubmit}
> >
{t('Create a derived account')} {t<string>('Create a derived account')}
</NextStepButton> </NextStepButton>
) )
: ( : (
@ -156,7 +156,7 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress,
data-button-action='create root account' data-button-action='create root account'
onClick={_goCreate} onClick={_goCreate}
> >
{t('Create account from new seed')} {t<string>('Create account from new seed')}
</NextStepButton> </NextStepButton>
) )
} }

View File

@ -71,7 +71,7 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
<> <>
<HeaderWithSteps <HeaderWithSteps
step={account ? 2 : 1} step={account ? 2 : 1}
text={t('Add new account')} text={t<string>('Add new account')}
/> />
{!account && ( {!account && (
<SelectParent <SelectParent
@ -103,7 +103,7 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
isDisabled={!password} isDisabled={!password}
onClick={_onCreate} onClick={_onCreate}
> >
{t('Create derived account')} {t<string>('Create derived account')}
</NextStepButton> </NextStepButton>
</ButtonArea> </ButtonArea>
</> </>

View File

@ -57,17 +57,17 @@ function Export ({ match: { params: { address } } }: Props): React.ReactElement<
<> <>
<Header <Header
showBackArrow showBackArrow
text={t('Export account')} text={t<string>('Export account')}
/> />
<div> <div>
<Address address={address}> <Address address={address}>
<MovedWarning danger>{t("You are exporting your account. Keep it safe and don't share it with anyone.")}</MovedWarning> <MovedWarning danger>{t<string>("You are exporting your account. Keep it safe and don't share it with anyone.")}</MovedWarning>
<ActionArea> <ActionArea>
<InputWithLabel <InputWithLabel
data-export-password data-export-password
disabled={isBusy} disabled={isBusy}
isError={pass.length < MIN_LENGTH || wrongPasswordHighlight} isError={pass.length < MIN_LENGTH || wrongPasswordHighlight}
label={t('password for this account')} label={t<string>('password for this account')}
onChange={setPass} onChange={setPass}
type='password' type='password'
/> />
@ -79,12 +79,12 @@ function Export ({ match: { params: { address } } }: Props): React.ReactElement<
isDisabled={pass.length === 0} isDisabled={pass.length === 0}
onClick={_onExportButtonClick} onClick={_onExportButtonClick}
> >
{t('I want to export this account')} {t<string>('I want to export this account')}
</Button> </Button>
<CancelButton> <CancelButton>
<ActionText <ActionText
onClick={_goHome} onClick={_goHome}
text={t('Cancel')} text={t<string>('Cancel')}
/> />
</CancelButton> </CancelButton>
</ActionArea> </ActionArea>

View File

@ -34,22 +34,22 @@ function Forget ({ match: { params: { address } } }: Props): React.ReactElement<
<> <>
<Header <Header
showBackArrow showBackArrow
text={t('Forget account')} text={t<string>('Forget account')}
/> />
<div> <div>
<Address address={address}> <Address address={address}>
<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> <MovedWarning isDanger>{t<string>('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> <ActionArea>
<Button <Button
isDanger isDanger
onClick={_onClick} onClick={_onClick}
> >
{t('I want to forget this account')} {t<string>('I want to forget this account')}
</Button> </Button>
<CancelButton> <CancelButton>
<ActionText <ActionText
onClick={_goHome} onClick={_goHome}
text={t('Cancel')} text={t<string>('Cancel')}
/> />
</CancelButton> </CancelButton>
</ActionArea> </ActionArea>

View File

@ -29,7 +29,7 @@ export default function ImportQr (): React.ReactElement {
<> <>
<Header <Header
showBackArrow showBackArrow
text={t('Scan Address Qr')} text={t<string>('Scan Address Qr')}
/> />
{!account && ( {!account && (
<div> <div>
@ -54,7 +54,7 @@ export default function ImportQr (): React.ReactElement {
<NextStepButton <NextStepButton
onClick={_onCreate} onClick={_onCreate}
> >
{t('Add the account with identified address')} {t<string>('Add the account with identified address')}
</NextStepButton> </NextStepButton>
</ButtonArea> </ButtonArea>
)} )}

View File

@ -73,7 +73,7 @@ export default function Import (): React.ReactElement {
<> <>
<HeaderWithSmallerMargin <HeaderWithSmallerMargin
showBackArrow showBackArrow
text={t('Import account')} text={t<string>('Import account')}
/> />
<div> <div>
<Address <Address
@ -85,7 +85,7 @@ export default function Import (): React.ReactElement {
// eslint-disable-next-line @typescript-eslint/no-use-before-define // eslint-disable-next-line @typescript-eslint/no-use-before-define
component={SeedInput} component={SeedInput}
isFocused isFocused
label={t('existing 12 or 24-word mnemonic seed')} label={t<string>('existing 12 or 24-word mnemonic seed')}
onValidatedChange={_onChangeSeed} onValidatedChange={_onChangeSeed}
rowsCount={2} rowsCount={2}
validator={isSeedValid} validator={isSeedValid}
@ -104,7 +104,7 @@ export default function Import (): React.ReactElement {
isBusy={isBusy} isBusy={isBusy}
onClick={_onCreate} onClick={_onCreate}
> >
{t('Add the account with the supplied seed')} {t<string>('Add the account with the supplied seed')}
</NextStepButton> </NextStepButton>
</ButtonArea> </ButtonArea>
</> </>

View File

@ -47,44 +47,44 @@ function Request ({ className, metaId, request, url }: Props): React.ReactElemen
<div className={className}> <div className={className}>
<Table> <Table>
<tr> <tr>
<td className='label'>{t('from')}</td> <td className='label'>{t<string>('from')}</td>
<td className='data'>{url}</td> <td className='data'>{url}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('chain')}</td> <td className='label'>{t<string>('chain')}</td>
<td className='data'>{request.chain}</td> <td className='data'>{request.chain}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('icon')}</td> <td className='label'>{t<string>('icon')}</td>
<td className='data'>{request.icon}</td> <td className='data'>{request.icon}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('decimals')}</td> <td className='label'>{t<string>('decimals')}</td>
<td className='data'>{request.tokenDecimals}</td> <td className='data'>{request.tokenDecimals}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('symbol')}</td> <td className='label'>{t<string>('symbol')}</td>
<td className='data'>{request.tokenSymbol}</td> <td className='data'>{request.tokenSymbol}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('upgrade')}</td> <td className='label'>{t<string>('upgrade')}</td>
<td className='data'>{chain ? chain.specVersion : t('<unknown>')} -&gt; {request.specVersion}</td> <td className='data'>{chain ? chain.specVersion : t('<unknown>')} -&gt; {request.specVersion}</td>
</tr> </tr>
</Table> </Table>
<div className='requestInfo'> <div className='requestInfo'>
<Warning className='requestWarning'>{t('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.')}</Warning> <Warning className='requestWarning'>{t<string>('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.')}</Warning>
<Button <Button
className='btnAccept' className='btnAccept'
onClick={_onApprove} onClick={_onApprove}
> >
{t('Yes, do this metadata update')} {t<string>('Yes, do this metadata update')}
</Button> </Button>
<ActionBar className='btnReject'> <ActionBar className='btnReject'>
<Link <Link
isDanger isDanger
onClick={_onReject} onClick={_onReject}
> >
{t('Reject')} {t<string>('Reject')}
</Link> </Link>
</ActionBar> </ActionBar>
</div> </div>

View File

@ -15,7 +15,7 @@ export default function Metadata (): React.ReactElement {
return ( return (
<> <>
<Header text={t('Metadata')} /> <Header text={t<string>('Metadata')} />
{requests[0] {requests[0]
? ( ? (
<Request <Request

View File

@ -96,7 +96,7 @@ export default function Upload (): React.ReactElement {
<> <>
<HeaderWithSmallerMargin <HeaderWithSmallerMargin
showBackArrow showBackArrow
text={t('Restore from JSON')} text={t<string>('Restore from JSON')}
/> />
<div> <div>
<div> <div>
@ -109,13 +109,13 @@ export default function Upload (): React.ReactElement {
<InputFileWithLabel <InputFileWithLabel
accept={acceptedFormats} accept={acceptedFormats}
isError={!isFileValid} isError={!isFileValid}
label={t('backup file')} label={t<string>('backup file')}
onChange={_onChangeFile} onChange={_onChangeFile}
withLabel withLabel
/> />
<InputWithLabel <InputWithLabel
isError={!isPassValid} isError={!isPassValid}
label={t('Password for this file')} label={t<string>('Password for this file')}
onChange={_onChangePass} onChange={_onChangePass}
type='password' type='password'
/> />
@ -124,7 +124,7 @@ export default function Upload (): React.ReactElement {
isDisabled={!isFileValid || !isPassValid} isDisabled={!isFileValid || !isPassValid}
onClick={_onRestore} onClick={_onRestore}
> >
{t('Restore')} {t<string>('Restore')}
</Button> </Button>
</div> </div>
</> </>

View File

@ -20,11 +20,11 @@ function Bytes ({ bytes, className, url }: Props): React.ReactElement<Props> {
<table className={className}> <table className={className}>
<tbody> <tbody>
<tr> <tr>
<td className='label'>{t('from')}</td> <td className='label'>{t<string>('from')}</td>
<td className='data'>{url}</td> <td className='data'>{url}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('bytes')}</td> <td className='label'>{t<string>('bytes')}</td>
<td className='data'>{bytes}</td> <td className='data'>{bytes}</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -8,6 +8,7 @@ import { AnyJson, SignerPayloadJSON } from '@polkadot/types/types';
import BN from 'bn.js'; import BN from 'bn.js';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { TFunction } from 'i18next';
import { formatNumber, bnToBn } from '@polkadot/util'; import { formatNumber, bnToBn } from '@polkadot/util';
import { Table } from '../../components'; import { Table } from '../../components';
@ -44,11 +45,11 @@ function decodeMethod (data: string, isDecoded: boolean, chain: Chain | null, sp
return { args, method }; return { args, method };
} }
function renderMethod (data: string, { args, method }: Decoded, t: (key: string) => string): React.ReactNode { function renderMethod (data: string, { args, method }: Decoded, t: TFunction): React.ReactNode {
if (!args || !method) { if (!args || !method) {
return ( return (
<tr> <tr>
<td className='label'>{t('method data')}</td> <td className='label'>{t<string>('method data')}</td>
<td className='data'>{data}</td> <td className='data'>{data}</td>
</tr> </tr>
); );
@ -57,7 +58,7 @@ function renderMethod (data: string, { args, method }: Decoded, t: (key: string)
return ( return (
<> <>
<tr> <tr>
<td className='label'>{t('method')}</td> <td className='label'>{t<string>('method')}</td>
<td className='data'> <td className='data'>
<details> <details>
<summary>{method.sectionName}.{method.methodName}{ <summary>{method.sectionName}.{method.methodName}{
@ -71,7 +72,7 @@ function renderMethod (data: string, { args, method }: Decoded, t: (key: string)
</tr> </tr>
{method.meta && ( {method.meta && (
<tr> <tr>
<td className='label'>{t('info')}</td> <td className='label'>{t<string>('info')}</td>
<td className='data'> <td className='data'>
<details> <details>
<summary>{method.meta.documentation.map((d) => d.toString().trim()).join(' ')}</summary> <summary>{method.meta.documentation.map((d) => d.toString().trim()).join(' ')}</summary>
@ -83,15 +84,15 @@ function renderMethod (data: string, { args, method }: Decoded, t: (key: string)
); );
} }
function mortalityAsString (era: ExtrinsicEra, hexBlockNumber: string, t: (key: string, options?: { replace: Record<string, string> }) => string): string { function mortalityAsString (era: ExtrinsicEra, hexBlockNumber: string, t: TFunction): string {
if (era.isImmortalEra) { if (era.isImmortalEra) {
return t('immortal'); return t<string>('immortal');
} }
const blockNumber = bnToBn(hexBlockNumber); const blockNumber = bnToBn(hexBlockNumber);
const mortal = era.asMortalEra; const mortal = era.asMortalEra;
return t('mortal, valid from {{birth}} to {{death}}', { return t<string>('mortal, valid from {{birth}} to {{death}}', {
replace: { replace: {
birth: formatNumber(mortal.birth(blockNumber)), birth: formatNumber(mortal.birth(blockNumber)),
death: formatNumber(mortal.death(blockNumber)) death: formatNumber(mortal.death(blockNumber))
@ -115,24 +116,24 @@ function Extrinsic ({ className, isDecoded, payload: { era, nonce, tip }, reques
isFull isFull
> >
<tr> <tr>
<td className='label'>{t('from')}</td> <td className='label'>{t<string>('from')}</td>
<td className='data'>{url}</td> <td className='data'>{url}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{chain ? t('chain') : t('genesis')}</td> <td className='label'>{chain ? t<string>('chain') : t<string>('genesis')}</td>
<td className='data'>{chain ? chain.name : genesisHash}</td> <td className='data'>{chain ? chain.name : genesisHash}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('version')}</td> <td className='label'>{t<string>('version')}</td>
<td className='data'>{specVersion.toNumber()}</td> <td className='data'>{specVersion.toNumber()}</td>
</tr> </tr>
<tr> <tr>
<td className='label'>{t('nonce')}</td> <td className='label'>{t<string>('nonce')}</td>
<td className='data'>{formatNumber(nonce)}</td> <td className='data'>{formatNumber(nonce)}</td>
</tr> </tr>
{!tip.isEmpty && ( {!tip.isEmpty && (
<tr> <tr>
<td className='label'>{t('tip')}</td> <td className='label'>{t<string>('tip')}</td>
<td className='data'>{formatNumber(tip)}</td> <td className='data'>{formatNumber(tip)}</td>
</tr> </tr>
)} )}

View File

@ -51,7 +51,7 @@ function Qr ({ className, onSignature, payload, request }: Props): React.ReactEl
className='scanButton' className='scanButton'
onClick={_onShowQr} onClick={_onShowQr}
> >
{t('Scan signature via camera')} {t<string>('Scan signature via camera')}
</Button> </Button>
)} )}
</div> </div>

View File

@ -125,7 +125,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
> >
<Checkbox <Checkbox
checked={!!isSavedPass} checked={!!isSavedPass}
label={t("Don't ask me again for the next 15 minutes")} label={t<string>("Don't ask me again for the next 15 minutes")}
onChange={setIsSavedPass} onChange={setIsSavedPass}
/> />
</Unlock> </Unlock>
@ -174,7 +174,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
isDanger isDanger
onClick={_onCancel} onClick={_onCancel}
> >
{t('Cancel')} {t<string>('Cancel')}
</Link> </Link>
</CancelButton> </CancelButton>
</SignArea> </SignArea>
@ -200,7 +200,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
isDanger isDanger
onClick={_onCancel} onClick={_onCancel}
> >
{t('Reject')} {t<string>('Reject')}
</Link> </Link>
</CancelButton> </CancelButton>
</SignArea> </SignArea>

View File

@ -44,7 +44,7 @@ function Unlock ({ buttonText, children, className, error, isBusy, onSign }: Pro
disabled={isBusy} disabled={isBusy}
isError={!password || !!ownError} isError={!password || !!ownError}
isFocused isFocused
label={t('Password for this account')} label={t<string>('Password for this account')}
onChange={_onChangePassword} onChange={_onChangePassword}
onEnter={_onClick} onEnter={_onClick}
type='password' type='password'

View File

@ -48,7 +48,7 @@ export default function Signing (): React.ReactElement {
return request return request
? ( ? (
<> <>
<Header text={isTransaction ? t('Transaction') : t('Sign message')}> <Header text={isTransaction ? t<string>('Transaction') : t<string>('Sign message')}>
{requests.length > 1 && ( {requests.length > 1 && (
<TransactionIndex <TransactionIndex
index={requestIndex} index={requestIndex}

View File

@ -22,19 +22,19 @@ export default function Welcome (): React.ReactElement<Props> {
return ( return (
<> <>
<Header text={t('Welcome')} /> <Header text={t<string>('Welcome')} />
<Note>{t('Before we start, just a couple of notes regarding use:')}</Note> <Note>{t<string>('Before we start, just a couple of notes regarding use:')}</Note>
<TextBox> <TextBox>
<List> <List>
<li>{t('We do not send any clicks, pageviews or events to a central server')}</li> <li>{t<string>('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<string>('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> <li>{t<string>("We don't collect keys, addresses or any information - your information never leaves this machine")}</li>
</List> </List>
</TextBox> </TextBox>
<Note>{t('... we are not in the information collection business (even anonymized).')}</Note> <Note>{t<string>('... we are not in the information collection business (even anonymized).')}</Note>
<VerticalSpace /> <VerticalSpace />
<ButtonArea> <ButtonArea>
<Button onClick={_onClick}>{t('Understood, let me continue')}</Button> <Button onClick={_onClick}>{t<string>('Understood, let me continue')}</Button>
</ButtonArea> </ButtonArea>
</> </>
); );

View File

@ -15,7 +15,7 @@ export default function Loading ({ children }: Props): React.ReactElement<Props>
if (!children) { if (!children) {
return ( return (
<div>{t('... loading ...')}</div> <div>{t<string>('... loading ...')}</div>
); );
} }

View File

@ -26,7 +26,7 @@ function MnemonicSeed ({ className, onCopy, seed }: Props): React.ReactElement<P
<TextAreaWithLabel <TextAreaWithLabel
className='mnemonicDisplay' className='mnemonicDisplay'
isReadOnly isReadOnly
label={t('Generated 12-word mnemonic seed:')} label={t<string>('Generated 12-word mnemonic seed:')}
value={seed} value={seed}
/> />
<div className='buttonsRow'> <div className='buttonsRow'>
@ -35,7 +35,7 @@ function MnemonicSeed ({ className, onCopy, seed }: Props): React.ReactElement<P
data-seed-action='copy' data-seed-action='copy'
icon={copy} icon={copy}
onClick={onCopy} onClick={onCopy}
text={t('Copy to clipboard')} text={t<string>('Copy to clipboard')}
/> />
</div> </div>
</div> </div>

View File

@ -8,6 +8,8 @@ import { HashRouter } from 'react-router-dom';
import { Fonts, View } from './components'; import { Fonts, View } from './components';
import './i18n';
export default function createView (Entry: React.ComponentType, rootId = 'root'): void { export default function createView (Entry: React.ComponentType, rootId = 'root'): void {
const rootElement = document.getElementById(rootId); const rootElement = document.getElementById(rootId);

View File

@ -2,19 +2,6 @@
// This software may be modified and distributed under the terms // This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details. // of the Apache-2.0 license. See the LICENSE file for details.
interface TranslationOptions { import { useTranslation } from 'react-i18next';
replace: Record<string, string | number>
}
interface Translation { export default useTranslation;
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;
}

View File

@ -0,0 +1,30 @@
// 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.
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
backend: {},
debug: false,
fallbackLng: 'en',
interpolation: {
escapeValue: false
},
keySeparator: false,
load: 'languageOnly',
nsSeparator: false,
react: {
wait: true
},
returnEmptyString: false,
returnNull: false
})
.catch(console.error);
export default i18n;

View File

@ -23,7 +23,7 @@ function Back ({ className, to = '/' }: Props): React.ReactElement<Props> {
<div className={className}> <div className={className}>
<BackLink to={to}> <BackLink to={to}>
<ArrowLeft /> <ArrowLeft />
{t('Back')} {t<string>('Back')}
</BackLink> </BackLink>
</div> </div>
); );

View File

@ -50,7 +50,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
<MenuItem className='menuItem'> <MenuItem className='menuItem'>
<Link to='/account/import-seed'> <Link to='/account/import-seed'>
<Svg src={seedIcon} /> <Svg src={seedIcon} />
<span>{t('Import account from pre-existing seed')}</span> <span>{t<string>('Import account from pre-existing seed')}</span>
</Link> </Link>
</MenuItem> </MenuItem>
<MenuItem className='menuItem'> <MenuItem className='menuItem'>
@ -59,7 +59,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
to={isPopup ? undefined : '/account/restore-json'} to={isPopup ? undefined : '/account/restore-json'}
> >
<Svg src={fileIcon} /> <Svg src={fileIcon} />
<span>{t('Restore account from backup JSON file')}</span> <span>{t<string>('Restore account from backup JSON file')}</span>
</Link> </Link>
</MenuItem> </MenuItem>
<MenuDivider /> <MenuDivider />
@ -69,7 +69,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
to='/account/import-qr' to='/account/import-qr'
> >
<Svg src={qrIcon} /> <Svg src={qrIcon} />
<span>{t('Attach external QR-signer account')}</span> <span>{t<string>('Attach external QR-signer account')}</span>
</Link> </Link>
</MenuItem> </MenuItem>
</Menu> </Menu>

View File

@ -71,19 +71,19 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
className='setting' className='setting'
title={t('External QR accounts and Access')} title={t<string>('External QR accounts and Access')}
> >
<Checkbox <Checkbox
checked={camera} checked={camera}
className='checkbox' className='checkbox'
label={t('Allow Camera Access')} label={t<string>('Allow Camera Access')}
onChange={setCamera} onChange={setCamera}
/> />
</MenuItem> </MenuItem>
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
className='setting' className='setting'
title={t('Display address format For')} title={t<string>('Display address format For')}
> >
<Dropdown <Dropdown
className='dropdown' className='dropdown'
@ -101,7 +101,7 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
className='openWindow' className='openWindow'
icon={FullScreenIcon} icon={FullScreenIcon}
onClick={windowOpen} onClick={windowOpen}
text={t('Open extension in new window')} text={t<string>('Open extension in new window')}
/> />
</MenuItem> </MenuItem>
</> </>

View File

@ -34,7 +34,7 @@ export default function Name ({ address, className, isFocused, label, onBlur, on
data-input-name data-input-name
defaultValue={startValue} defaultValue={startValue}
isFocused={isFocused} isFocused={isFocused}
label={label || t('A descriptive name for your account')} label={label || t<string>('A descriptive name for your account')}
onBlur={onBlur} onBlur={onBlur}
onValidatedChange={onChange} onValidatedChange={onChange}
type='text' type='text'

View File

@ -37,7 +37,7 @@ export default function Password ({ isFocussed, onChange }: Props): React.ReactE
component={InputWithLabel} component={InputWithLabel}
data-input-password data-input-password
isFocused={isFocussed} isFocused={isFocussed}
label={t('A new password for this account')} label={t<string>('A new password for this account')}
onValidatedChange={setPass1} onValidatedChange={setPass1}
type='password' type='password'
validator={isFirstPasswordValid} validator={isFirstPasswordValid}
@ -46,7 +46,7 @@ export default function Password ({ isFocussed, onChange }: Props): React.ReactE
<ValidatedInput <ValidatedInput
component={InputWithLabel} component={InputWithLabel}
data-input-repeat-password data-input-repeat-password
label={t('Repeat password for verification')} label={t<string>('Repeat password for verification')}
onValidatedChange={setPass2} onValidatedChange={setPass2}
type='password' type='password'
validator={isSecondPasswordValid(pass1)} validator={isSecondPasswordValid(pass1)}

View File

@ -1400,7 +1400,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@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": "@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.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4":
version: 7.11.2 version: 7.11.2
resolution: "@babel/runtime@npm:7.11.2" resolution: "@babel/runtime@npm:7.11.2"
dependencies: dependencies:
@ -3093,9 +3093,12 @@ __metadata:
"@types/styled-components": ^5.1.3 "@types/styled-components": ^5.1.3
enzyme: ^3.11.0 enzyme: ^3.11.0
enzyme-adapter-react-16: ^1.15.4 enzyme-adapter-react-16: ^1.15.4
i18next: ^19.7.0
i18next-browser-languagedetector: ^5.0.1
react: ^16.13.1 react: ^16.13.1
react-dom: ^16.13.1 react-dom: ^16.13.1
react-dropzone: ^11.0.3 react-dropzone: ^11.0.3
react-i18next: ^11.7.2
react-is: ^16.13.1 react-is: ^16.13.1
react-router: ^5.2.0 react-router: ^5.2.0
react-router-dom: ^5.2.0 react-router-dom: ^5.2.0
@ -11920,6 +11923,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"html-parse-stringify2@npm:2.0.1":
version: 2.0.1
resolution: "html-parse-stringify2@npm:2.0.1"
dependencies:
void-elements: ^2.0.1
checksum: 71159c76d54e7e8c1dad808b7ed3f3410e7846b496e3910f0c14f78ade875477b3f6b320ee1259ba5a7689a7ba61bee0b23f9f50ae1d33189de73bf436fb2a52
languageName: node
linkType: hard
"html-tags@npm:^2.0.0": "html-tags@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "html-tags@npm:2.0.0" resolution: "html-tags@npm:2.0.0"
@ -12116,6 +12128,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"i18next-browser-languagedetector@npm:^5.0.1":
version: 5.0.1
resolution: "i18next-browser-languagedetector@npm:5.0.1"
dependencies:
"@babel/runtime": ^7.5.5
checksum: 4e80cc054cb1d1600c421d53153b7752aaca07a3ca9f394d64962aa34f901c3e61d2a409d76436c1d76ea8bb1e6ab99a4888dcfbfc3a8de0a489abcd2bb6563d
languageName: node
linkType: hard
"i18next-scanner@npm:^2.11.0": "i18next-scanner@npm:^2.11.0":
version: 2.11.0 version: 2.11.0
resolution: "i18next-scanner@npm:2.11.0" resolution: "i18next-scanner@npm:2.11.0"
@ -12146,7 +12167,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"i18next@npm:*": "i18next@npm:*, i18next@npm:^19.7.0":
version: 19.7.0 version: 19.7.0
resolution: "i18next@npm:19.7.0" resolution: "i18next@npm:19.7.0"
dependencies: dependencies:
@ -18253,6 +18274,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-i18next@npm:^11.7.2":
version: 11.7.2
resolution: "react-i18next@npm:11.7.2"
dependencies:
"@babel/runtime": ^7.3.1
html-parse-stringify2: 2.0.1
peerDependencies:
i18next: ">= 19.0.0"
react: ">= 16.8.0"
checksum: ea5c989d1b129d74b2d4b4d90cd33709f883cf89df90bcfbb78e3c97ac0c052cbf3369554e9e5beb405f1dbab5d7f7b77aa29fc0e13716d78d456f4f8408771c
languageName: node
linkType: hard
"react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.6": "react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.6":
version: 16.13.1 version: 16.13.1
resolution: "react-is@npm:16.13.1" resolution: "react-is@npm:16.13.1"
@ -22395,6 +22429,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"void-elements@npm:^2.0.1":
version: 2.0.1
resolution: "void-elements@npm:2.0.1"
checksum: 768d7e08534922455d0e87c66426123920f4d26d88e9c3dc430469e4ce1c11920ca8e2aea7794e52916524d55c3c7820efc20d3f5ce6b749036d989adef1cdd9
languageName: node
linkType: hard
"vue-hot-reload-api@npm:^2.3.0": "vue-hot-reload-api@npm:^2.3.0":
version: 2.3.4 version: 2.3.4
resolution: "vue-hot-reload-api@npm:2.3.4" resolution: "vue-hot-reload-api@npm:2.3.4"