Plumbing for i18n (key -> key) (#429)
* Plumbing for i18n (key -> key) * Update test text * Apply suggestions from code review * i18next-scanner for extraction
This commit is contained in:
parent
a3d5fc449a
commit
270930db9e
@ -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
|
||||
|
||||
34
i18next-scanner.config.js
Normal file
34
i18next-scanner.config.js
Normal file
@ -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: './'
|
||||
};
|
||||
10
package.json
10
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",
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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}>
|
||||
|
||||
@ -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'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>
|
||||
</>
|
||||
|
||||
@ -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 => (
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -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:'
|
||||
}))`
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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: '
|
||||
text={t('Create an account')}
|
||||
/>
|
||||
<Loading>
|
||||
<div>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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: '
|
||||
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>
|
||||
</>
|
||||
|
||||
@ -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'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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
)}
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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'} -> {request.specVersion}</td>
|
||||
<td className='label'>{t('upgrade')}</td>
|
||||
<td className='data'>{chain ? chain.specVersion : t('<unknown>')} -> {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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
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>
|
||||
</>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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'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>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
20
packages/extension-ui/src/hooks/useTranslation.ts
Normal file
20
packages/extension-ui/src/hooks/useTranslation.ts
Normal file
@ -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;
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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)}
|
||||
|
||||
98
packages/extension/public/locales/en/translation.json
Normal file
98
packages/extension/public/locales/en/translation.json
Normal file
@ -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'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'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": ""
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user