diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src')
9 files changed, 2477 insertions, 402 deletions
diff --git a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx index 69a2c0675..a77a69fa6 100644 --- a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx +++ b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx @@ -22,42 +22,80 @@ import { TalerErrorDetail, TaskProgressNotification, WalletNotification, - assertUnreachable, + assertUnreachable } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { Fragment, JSX, VNode, h } from "preact"; +import { Fragment, VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { Pages } from "../NavigationBar.js"; import { useBackendContext } from "../context/backend.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useSettings } from "../hooks/useSettings.js"; import { Button } from "../mui/Button.js"; +import { TextField } from "../mui/TextField.js"; +import { SafeHandler } from "../mui/handlers.js"; import { WxApiType } from "../wxApi.js"; +import { WalletActivityTrack } from "../wxBackend.js"; import { Modal } from "./Modal.js"; import { Time } from "./Time.js"; -interface Props extends JSX.HTMLAttributes {} +const OPEN_ACTIVITY_HEIGHT_PX = 250; +const CLOSE_ACTIVITY_HEIGHT_PX = 40; -export function WalletActivity({}: Props): VNode { +export function WalletActivity(): VNode { const { i18n } = useTranslationContext(); - const [settings, updateSettings] = useSettings(); - const api = useBackendContext(); + const [, updateSettings] = useSettings(); + + const [collapsed, setCollcapsed] = useState(true); + useEffect(() => { - document.body.style.marginBottom = "250px"; + document.body.style.marginBottom = `${ + collapsed ? CLOSE_ACTIVITY_HEIGHT_PX : OPEN_ACTIVITY_HEIGHT_PX + }px`; return () => { document.body.style.marginBottom = "0px"; }; - }); - const [table, setTable] = useState<"tasks" | "events">("tasks"); + }, [collapsed]); + + const [table, setTable] = useState<"tasks" | "events">("events"); + if (collapsed) { + return ( + <div + style={{ + position: "fixed", + bottom: 0, + background: "lightgrey", + zIndex: 1, + height: CLOSE_ACTIVITY_HEIGHT_PX, + overflowY: "scroll", + width: "100%", + }} + onClick={() => { + setCollcapsed(!collapsed); + }} + > + <div + style={{ + display: "flex", + justifyContent: "space-around", + marginTop: 10, + cursor: "pointer", + }} + > + click here to open + </div> + </div> + ); + } return ( <div style={{ position: "fixed", bottom: 0, - background: "white", + background: "lightgrey", zIndex: 1, - height: 250, + height: OPEN_ACTIVITY_HEIGHT_PX, overflowY: "scroll", width: "100%", }} @@ -65,23 +103,22 @@ export function WalletActivity({}: Props): VNode { <div style={{ display: "flex", - justifyContent: "space-between", - float: "right", + justifyContent: "space-around", + cursor: "pointer", + }} + onClick={() => { + setCollcapsed(!collapsed); }} > - <div /> - <div> - <div - style={{ padding: 4, margin: 2, border: "solid 1px black" }} - onClick={() => { - updateSettings("showWalletActivity", false); - }} - > - close - </div> - </div> - </div> - <div style={{ display: "flex", justifyContent: "space-around" }}> + <Button + variant={table === "events" ? "contained" : "outlined"} + style={{ margin: 4 }} + onClick={async () => { + setTable("events"); + }} + > + <i18n.Translate>Events</i18n.Translate> + </Button> <Button variant={table === "tasks" ? "contained" : "outlined"} style={{ margin: 4 }} @@ -89,31 +126,38 @@ export function WalletActivity({}: Props): VNode { setTable("tasks"); }} > - <i18n.Translate>Tasks</i18n.Translate> + <i18n.Translate>Active tasks</i18n.Translate> </Button> + <Button - variant={table === "events" ? "contained" : "outlined"} + variant="outlined" style={{ margin: 4 }} onClick={async () => { - setTable("events"); + updateSettings("showWalletActivity", false); }} > - <i18n.Translate>Events</i18n.Translate> + <i18n.Translate>Close</i18n.Translate> </Button> </div> - {(function (): VNode { - switch (table) { - case "events": { - return <ObservabilityEventsTable />; - } - case "tasks": { - return <ActiveTasksTable />; - } - default: { - assertUnreachable(table); + <div + style={{ + backgroundColor: "white", + }} + > + {(function (): VNode { + switch (table) { + case "events": { + return <ObservabilityEventsTable />; + } + case "tasks": { + return <ActiveTasksTable />; + } + default: { + assertUnreachable(table); + } } - } - })()} + })()} + </div> </div> ); } @@ -122,21 +166,6 @@ interface MoreInfoPRops { events: (WalletNotification & { when: AbsoluteTime })[]; onClick: (content: VNode) => void; } -type Notif = { - id: string; - events: (WalletNotification & { when: AbsoluteTime })[]; - description: string; - start: AbsoluteTime; - end: AbsoluteTime; - reference: - | { - eventType: NotificationType; - referenceType: "task" | "transaction" | "operation" | "exchange"; - id: string; - } - | undefined; - MoreInfo: (p: MoreInfoPRops) => VNode; -}; function ShowBalanceChange({ events }: MoreInfoPRops): VNode { if (!events.length) return <Fragment />; @@ -267,10 +296,7 @@ function ShowTransactionStateTransition({ </Fragment> ); } -function ShowExchangeStateTransition({ - events, - onClick, -}: MoreInfoPRops): VNode { +function ShowExchangeStateTransition({ events }: MoreInfoPRops): VNode { if (!events.length) return <Fragment />; const not = events[0]; if (not.type !== NotificationType.ExchangeStateTransition) @@ -323,7 +349,7 @@ type ObservaNotifWithTime = ( }; function ShowObservabilityEvent({ events, onClick }: MoreInfoPRops): VNode { // let prev: ObservaNotifWithTime; - const asd = events.map((not) => { + const asd = events.map((not, idx) => { if ( not.type !== NotificationType.RequestObservabilityEvent && not.type !== NotificationType.TaskObservabilityEvent @@ -364,7 +390,12 @@ function ShowObservabilityEvent({ events, onClick }: MoreInfoPRops): VNode { })(); return ( - <ShowObervavilityDetails title={title} notif={not} onClick={onClick} /> + <ShowObervavilityDetails + key={idx} + title={title} + notif={not} + onClick={onClick} + /> ); }); return ( @@ -673,235 +704,64 @@ function ShowObervavilityDetails({ } } -function getNotificationFor( - id: string, - event: WalletNotification, - start: AbsoluteTime, - list: Notif[], -): Notif | undefined { - const eventWithTime = { ...event, when: start }; - switch (event.type) { - case NotificationType.BalanceChange: { - return { - id, - events: [eventWithTime], - reference: { - eventType: event.type, - referenceType: "transaction", - id: event.hintTransactionId, - }, - description: "Balance change", - start, - end: AbsoluteTime.never(), - MoreInfo: ShowBalanceChange, - }; - } - case NotificationType.BackupOperationError: { - return { - id, - events: [eventWithTime], - reference: undefined, - description: "Backup error", - start, - end: AbsoluteTime.never(), - MoreInfo: ShowBackupOperationError, - }; - } - case NotificationType.TransactionStateTransition: { - const found = list.find( - (a) => - a.reference?.eventType === event.type && - a.reference.id === event.transactionId, - ); - if (found) { - found.end = start; - found.events.unshift(eventWithTime); - return undefined; - } - return { - id, - events: [eventWithTime], - reference: { - eventType: event.type, - referenceType: "transaction", - id: event.transactionId, - }, - description: event.type, - start, - end: AbsoluteTime.never(), - MoreInfo: ShowTransactionStateTransition, - }; - } - case NotificationType.ExchangeStateTransition: { - const found = list.find( - (a) => - a.reference?.eventType === event.type && - a.reference.id === event.exchangeBaseUrl, - ); - if (found) { - found.end = start; - found.events.unshift(eventWithTime); - return undefined; - } - return { - id, - events: [eventWithTime], - description: "Exchange update", - reference: { - eventType: event.type, - referenceType: "exchange", - id: event.exchangeBaseUrl, - }, - start, - end: AbsoluteTime.never(), - MoreInfo: ShowExchangeStateTransition, - }; - } - case NotificationType.TaskObservabilityEvent: { - const found = list.find( - (a) => - a.reference?.eventType === event.type && - a.reference.id === event.taskId, - ); - if (found) { - found.end = start; - found.events.unshift(eventWithTime); - return undefined; - } - return { - id, - events: [eventWithTime], - reference: { - eventType: event.type, - referenceType: "task", - id: event.taskId, - }, - description: `Task update ${event.taskId}`, - start, - end: AbsoluteTime.never(), - MoreInfo: ShowObservabilityEvent, - }; - } - case NotificationType.WithdrawalOperationTransition: { - const found = list.find( - (a) => - a.reference?.eventType === event.type && a.reference.id === event.uri, - ); - if (found) { - found.end = start; - found.events.unshift(eventWithTime); - return undefined; - } - return { - id, - events: [eventWithTime], - reference: { - eventType: event.type, - referenceType: "task", - id: event.uri, - }, - description: `Withdrawal operation updated`, - start, - end: AbsoluteTime.never(), - MoreInfo: ShowObservabilityEvent, - }; - } - case NotificationType.RequestObservabilityEvent: { - const found = list.find( - (a) => - a.reference?.eventType === event.type && - a.reference.id === event.requestId, - ); - if (found) { - found.end = start; - found.events.unshift(eventWithTime); - return undefined; - } - return { - id, - events: [eventWithTime], - reference: { - eventType: event.type, - referenceType: "operation", - id: event.requestId, - }, - description: `wallet.${event.operation}(${event.requestId})`, - start, - end: AbsoluteTime.never(), - MoreInfo: ShowObservabilityEvent, - }; - } - case NotificationType.Idle: - return undefined; - default: { - assertUnreachable(event); - } - } -} - -function refresh(api: WxApiType, onUpdate: (list: Notif[]) => void) { +function refresh( + api: WxApiType, + onUpdate: (list: WalletActivityTrack[]) => void, + filter: string, +) { api.background - .call("getNotifications", undefined) + .call("getNotifications", { filter }) .then((notif) => { - const list: Notif[] = []; - for (const n of notif) { - if ( - n.notification.type === NotificationType.RequestObservabilityEvent && - n.notification.operation === "getActiveTasks" - ) { - //ignore monitor request - continue; - } - const event = getNotificationFor( - String(list.length), - n.notification, - n.when, - list, - ); - // pepe. - if (event) { - list.unshift(event); - } - } - onUpdate(list); + onUpdate(notif); }) .catch((error) => { console.log(error); }); } -export function ObservabilityEventsTable({}: {}): VNode { +export function ObservabilityEventsTable(): VNode { const { i18n } = useTranslationContext(); const api = useBackendContext(); - const [notifications, setNotifications] = useState<Notif[]>([]); + const [notifications, setNotifications] = useState<WalletActivityTrack[]>([]); const [showDetails, setShowDetails] = useState<VNode>(); + const [filter, onChangeFilter] = useState(""); useEffect(() => { let lastTimeout: ReturnType<typeof setTimeout>; function periodicRefresh() { - refresh(api, setNotifications); + refresh(api, setNotifications, filter); lastTimeout = setTimeout(() => { periodicRefresh(); }, 1000); - //clear on unload return () => { clearTimeout(lastTimeout); }; } return periodicRefresh(); - }, [1]); + }, [filter]); return ( <div> <div style={{ display: "flex", justifyContent: "space-between" }}> + <TextField + label="Filter" + variant="outlined" + value={filter} + onChange={onChangeFilter} + /> <div - style={{ padding: 4, margin: 2, border: "solid 1px black" }} + style={{ + padding: 4, + margin: 2, + border: "solid 1px black", + alignSelf: "center", + }} onClick={() => { - api.background.call("clearNotifications", undefined).then((d) => { - refresh(api, setNotifications); + api.background.call("clearNotifications", undefined).then(() => { + refresh(api, setNotifications, filter); }); }} > @@ -914,7 +774,7 @@ export function ObservabilityEventsTable({}: {}): VNode { onClose={{ onClick: (async () => { setShowDetails(undefined); - }) as any, + }) as SafeHandler<void>, }} > {showDetails} @@ -932,7 +792,40 @@ export function ObservabilityEventsTable({}: {}): VNode { padding: 4, }} > - <div style={{ padding: 4 }}>{not.description}</div> + <div style={{ padding: 4 }}> + {(() => { + switch (not.type) { + case NotificationType.BalanceChange: + return i18n.str`Balance change`; + case NotificationType.BackupOperationError: + return i18n.str`Backup failed`; + case NotificationType.TransactionStateTransition: + return i18n.str`Transaction updated`; + case NotificationType.ExchangeStateTransition: + return i18n.str`Exchange updated`; + case NotificationType.Idle: + return i18n.str`Idle`; + case NotificationType.TaskObservabilityEvent: + return i18n.str`task.${ + (not.events[0] as TaskProgressNotification).taskId + }`; + case NotificationType.RequestObservabilityEvent: + return i18n.str`wallet.${ + (not.events[0] as RequestProgressNotification) + .operation + }(${ + (not.events[0] as RequestProgressNotification) + .requestId + })`; + case NotificationType.WithdrawalOperationTransition: { + return `---`; + } + default: { + assertUnreachable(not.type); + } + } + })()} + </div> <div style={{ padding: 4 }}> <Time timestamp={not.start} format="yyyy/MM/dd HH:mm:ss" /> </div> @@ -941,12 +834,76 @@ export function ObservabilityEventsTable({}: {}): VNode { </div> </div> </summary> - <not.MoreInfo - events={not.events} - onClick={(details) => { - setShowDetails(details); - }} - /> + {(() => { + switch (not.type) { + case NotificationType.BalanceChange: { + return ( + <ShowBalanceChange + events={not.events} + onClick={(details) => { + setShowDetails(details); + }} + /> + ); + } + case NotificationType.BackupOperationError: { + return ( + <ShowBackupOperationError + events={not.events} + onClick={(details) => { + setShowDetails(details); + }} + /> + ); + } + case NotificationType.TransactionStateTransition: { + return ( + <ShowTransactionStateTransition + events={not.events} + onClick={(details) => { + setShowDetails(details); + }} + /> + ); + } + case NotificationType.ExchangeStateTransition: { + return ( + <ShowExchangeStateTransition + events={not.events} + onClick={(details) => { + setShowDetails(details); + }} + /> + ); + } + case NotificationType.Idle: { + return <div>not implemented</div>; + } + case NotificationType.TaskObservabilityEvent: { + return ( + <ShowObservabilityEvent + events={not.events} + onClick={(details) => { + setShowDetails(details); + }} + /> + ); + } + case NotificationType.RequestObservabilityEvent: { + return ( + <ShowObservabilityEvent + events={not.events} + onClick={(details) => { + setShowDetails(details); + }} + /> + ); + } + case NotificationType.WithdrawalOperationTransition: { + return <div>not implemented</div>; + } + } + })()} </details> ); })} @@ -965,7 +922,7 @@ function ErroDetailModal({ <Modal title="Full detail" onClose={{ - onClick: onClose as any, + onClick: onClose as SafeHandler<void>, }} > <dl> @@ -987,7 +944,7 @@ function ErroDetailModal({ ); } -export function ActiveTasksTable({}: {}): VNode { +export function ActiveTasksTable(): VNode { const { i18n } = useTranslationContext(); const api = useBackendContext(); const state = useAsyncAsHook(() => { @@ -1006,13 +963,6 @@ export function ActiveTasksTable({}: {}): VNode { }; }, [tasks]); - // const listenAllEvents = Array.from<NotificationType>({ length: 1 }); - // listenAllEvents.includes = () => true - // useEffect(() => { - // return api.listener.onUpdateNotification(listenAllEvents, (notif) => { - // state?.retry() - // }); - // }); return ( <Fragment> {showError && ( @@ -1051,7 +1001,7 @@ export function ActiveTasksTable({}: {}): VNode { {tasks.map((task) => { const [type, id] = task.taskId.split(":"); return ( - <tr> + <tr key={id}> <td>{type}</td> <td title={id}>{id.substring(0, 10)}</td> <td> diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index f2fa04902..65c000741 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -16,6 +16,7 @@ import { AmountJson, + AmountString, Amounts, ExchangeFullDetails, ExchangeListItem, @@ -55,7 +56,6 @@ export function useComponentStateFromParams({ if (exchangeByTalerUri) { await api.wallet.call(WalletApiOperation.AddExchange, { exchangeBaseUrl: exchangeByTalerUri, - masterPub: uri.exchangePub, }); const info = await api.wallet.call( WalletApiOperation.GetExchangeDetailedInfo, @@ -157,6 +157,7 @@ export function useComponentStateFromParams({ async function doManualWithdraw( exchange: string, ageRestricted: number | undefined, + amount: AmountString, ): Promise<{ transactionId: string; confirmTransferUrl: string | undefined; @@ -165,7 +166,7 @@ export function useComponentStateFromParams({ WalletApiOperation.AcceptManualWithdrawal, { exchangeBaseUrl: exchange, - amount: Amounts.stringify(chosenAmount), + amount, restrictAge: ageRestricted, }, ); @@ -204,10 +205,9 @@ export function useComponentStateFromURI({ : maybeTalerUri; const uriInfo = await api.wallet.call( - WalletApiOperation.GetWithdrawalDetailsForUri, + WalletApiOperation.PrepareBankIntegratedWithdrawal, { talerWithdrawUri, - notifyChangeFromPendingTimeoutMs: 30 * 1000, }, ); const { @@ -217,16 +217,12 @@ export function useComponentStateFromURI({ operationId, confirmTransferUrl, status, - } = uriInfo; - const transaction = await api.wallet.call( - WalletApiOperation.GetWithdrawalTransactionByUri, - { talerWithdrawUri }, - ); + } = uriInfo.info; return { talerWithdrawUri, operationId, status, - transaction, + transactionId: uriInfo.transactionId, confirmTransferUrl, amount: Amounts.parseOrThrow(amount), thisExchange: defaultExchangeBaseUrl, @@ -260,6 +256,7 @@ export function useComponentStateFromURI({ } const uri = uriInfoHook.response.talerWithdrawUri; + const txId = uriInfoHook.response.transactionId; const chosenAmount = uriInfoHook.response.amount; const defaultExchange = uriInfoHook.response.thisExchange; const exchangeList = uriInfoHook.response.exchanges; @@ -267,16 +264,18 @@ export function useComponentStateFromURI({ async function doManagedWithdraw( exchange: string, ageRestricted: number | undefined, + amount: AmountString, ): Promise<{ transactionId: string; confirmTransferUrl: string | undefined; }> { const res = await api.wallet.call( - WalletApiOperation.AcceptBankIntegratedWithdrawal, + WalletApiOperation.ConfirmWithdrawal, { exchangeBaseUrl: exchange, - talerWithdrawUri: uri, + amount, restrictAge: ageRestricted, + transactionId: txId, }, ); return { @@ -286,9 +285,9 @@ export function useComponentStateFromURI({ } if (uriInfoHook.response.status !== "pending") { - if (uriInfoHook.response.transaction) { - onSuccess(uriInfoHook.response.transaction.transactionId); - } + // if (uriInfoHook.response.transactionId) { + // onSuccess(uriInfoHook.response.transactionId); + // } return { status: "already-completed", operationState: uriInfoHook.response.status, @@ -313,6 +312,7 @@ export function useComponentStateFromURI({ type ManualOrManagedWithdrawFunction = ( exchange: string, ageRestricted: number | undefined, + amount: AmountString, ) => Promise<{ transactionId: string; confirmTransferUrl: string | undefined }>; function exchangeSelectionState( @@ -381,6 +381,7 @@ function exchangeSelectionState( const res = await doWithdraw( currentExchange.exchangeBaseUrl, !ageRestricted ? undefined : ageRestricted, + Amounts.stringify(Amounts.zeroOfCurrency(selectedCurrency)), ); if (res.confirmTransferUrl) { document.location.href = res.confirmTransferUrl; diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts index f90f7bed7..70d40ec1c 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts @@ -108,22 +108,18 @@ describe("Withdraw CTA states", () => { }; handler.addWalletCallResponse( - WalletApiOperation.GetWithdrawalDetailsForUri, + WalletApiOperation.PrepareBankIntegratedWithdrawal, undefined, { - status: "pending", - operationId: "123", - amount: "EUR:2" as AmountString, - possibleExchanges: [], + transactionId: "123", + info: { + status: "pending", + operationId: "123", + amount: "EUR:2" as AmountString, + possibleExchanges: [], + } }, ); - handler.addWalletCallResponse( - WalletApiOperation.GetWithdrawalTransactionByUri, - undefined, - { - transactionId: "123" - } as any, - ); const hookBehavior = await tests.hookBehaveLikeThis( useComponentStateFromURI, @@ -153,24 +149,20 @@ describe("Withdraw CTA states", () => { }; handler.addWalletCallResponse( - WalletApiOperation.GetWithdrawalDetailsForUri, + WalletApiOperation.PrepareBankIntegratedWithdrawal, undefined, { - status: "pending", - operationId: "123", - amount: "ARS:2" as AmountString, - possibleExchanges: exchanges, - defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, + transactionId: "123", + info: { + status: "pending", + operationId: "123", + amount: "ARS:2" as AmountString, + possibleExchanges: exchanges, + defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, + } }, ); handler.addWalletCallResponse( - WalletApiOperation.GetWithdrawalTransactionByUri, - undefined, - { - transactionId: "123" - } as any, - ); - handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForAmount, undefined, { diff --git a/packages/taler-wallet-webextension/src/i18n/de.po b/packages/taler-wallet-webextension/src/i18n/de.po index 1a285499c..bc66f2136 100644 --- a/packages/taler-wallet-webextension/src/i18n/de.po +++ b/packages/taler-wallet-webextension/src/i18n/de.po @@ -17,7 +17,7 @@ msgstr "" "Project-Id-Version: Taler Wallet\n" "Report-Msgid-Bugs-To: languages@taler.net\n" "POT-Creation-Date: 2016-11-23 00:00+0100\n" -"PO-Revision-Date: 2023-11-25 17:24+0000\n" +"PO-Revision-Date: 2024-05-07 14:32+0000\n" "Last-Translator: Stefan Kügel <skuegel@web.de>\n" "Language-Team: German <https://weblate.taler.net/projects/gnu-taler/" "webextensions/de/>\n" @@ -26,7 +26,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.2.1\n" +"X-Generator: Weblate 5.4.3\n" #: src/NavigationBar.tsx:139 #, c-format @@ -56,7 +56,7 @@ msgstr "Dev" #: src/mui/Typography.tsx:122 #, c-format msgid "%1$s" -msgstr "" +msgstr "%1$s" #: src/components/PendingTransactions.tsx:74 #, c-format @@ -215,7 +215,7 @@ msgstr "" #: src/wallet/AddNewActionView.tsx:57 #, c-format msgid "Cancel" -msgstr "Abbrechen" +msgstr "Zurück" #: src/wallet/AddNewActionView.tsx:68 #, c-format @@ -325,7 +325,7 @@ msgstr "" #: src/components/ShowFullContractTermPopup.tsx:189 #, c-format msgid "Summary" -msgstr "" +msgstr "Zusammenfassung" #: src/components/ShowFullContractTermPopup.tsx:195 #, c-format @@ -370,7 +370,7 @@ msgstr "" #: src/components/ShowFullContractTermPopup.tsx:256 #, c-format msgid "Delivery date" -msgstr "" +msgstr "Lieferdatum" #: src/components/ShowFullContractTermPopup.tsx:271 #, c-format @@ -405,7 +405,7 @@ msgstr "" #: src/components/ShowFullContractTermPopup.tsx:354 #, c-format msgid "Fulfillment URL" -msgstr "" +msgstr "Adresse digitaler Dienstleistung (Fulfillment-URL)" #: src/components/ShowFullContractTermPopup.tsx:360 #, c-format @@ -1061,7 +1061,7 @@ msgstr "Konnte die Umsatzanzeige nicht laden" #: src/wallet/ExchangeSelection/views.tsx:131 #, c-format msgid "Close" -msgstr "" +msgstr "Schließen" #: src/wallet/ExchangeSelection/views.tsx:160 #, fuzzy, c-format diff --git a/packages/taler-wallet-webextension/src/i18n/ru.po b/packages/taler-wallet-webextension/src/i18n/ru.po new file mode 100644 index 000000000..aa002c984 --- /dev/null +++ b/packages/taler-wallet-webextension/src/i18n/ru.po @@ -0,0 +1,1977 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: languages@taler.net\n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: 2024-05-10 00:13+0000\n" +"Last-Translator: Lily Ponomareva <lilyponomareva2017@gmail.com>\n" +"Language-Team: Russian <https://weblate.taler.net/projects/gnu-taler/" +"webextensions/ru/>\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 5.4.3\n" + +#: src/NavigationBar.tsx:139 +#, c-format +msgid "Balance" +msgstr "Баланс" + +#: src/NavigationBar.tsx:142 +#, c-format +msgid "Backup" +msgstr "Резервная копия" + +#: src/NavigationBar.tsx:147 +#, c-format +msgid "QR Reader and Taler URI" +msgstr "Считыватель QR-кодов и URI Taler" + +#: src/NavigationBar.tsx:154 +#, c-format +msgid "Settings" +msgstr "Настройки" + +#: src/NavigationBar.tsx:184 +#, c-format +msgid "Dev" +msgstr "Dev" + +#: src/mui/Typography.tsx:122 +#, c-format +msgid "%1$s" +msgstr "%1$s" + +#: src/components/PendingTransactions.tsx:74 +#, c-format +msgid "PENDING OPERATIONS" +msgstr "ОЖИДАЮЩИЕ ОПЕРАЦИИ" + +#: src/components/Loading.tsx:36 +#, c-format +msgid "Loading" +msgstr "Загружаются" + +#: src/wallet/BackupPage.tsx:123 +#, c-format +msgid "Could not load backup providers" +msgstr "Не удалось загрузить поставщиков резервного копирования" + +#: src/wallet/BackupPage.tsx:202 +#, c-format +msgid "No backup providers configured" +msgstr "Поставщики резервного копирования не настроены" + +#: src/wallet/BackupPage.tsx:205 +#, c-format +msgid "Add provider" +msgstr "Добавить сервис" + +#: src/wallet/BackupPage.tsx:219 +#, c-format +msgid "Sync all backups" +msgstr "Синхронизация всех резервных копий" + +#: src/wallet/BackupPage.tsx:221 +#, c-format +msgid "Sync now" +msgstr "Синхронизировать сейчас" + +#: src/wallet/BackupPage.tsx:264 +#, c-format +msgid "Last synced" +msgstr "Последняя синхронизация" + +#: src/wallet/BackupPage.tsx:269 +#, c-format +msgid "Not synced" +msgstr "Не синхронизировано" + +#: src/wallet/BackupPage.tsx:289 +#, c-format +msgid "Expires in" +msgstr "Срок действия истекает в" + +#: src/wallet/ProviderDetailPage.tsx:60 +#, c-format +msgid "There was an error loading the provider detail for " %1$s"" +msgstr "" +"Произошла ошибка при загрузке сведений о поставщике для " %1$s"" + +#: src/wallet/ProviderDetailPage.tsx:108 +#, c-format +msgid "There is not known provider with url "%1$s"." +msgstr "Нет провайдера с url "%1$s"." + +#: src/wallet/ProviderDetailPage.tsx:115 +#, c-format +msgid "See providers" +msgstr "Посмотреть провайдеров" + +#: src/wallet/ProviderDetailPage.tsx:143 +#, c-format +msgid "Last backup" +msgstr "Последняя резервная копия" + +#: src/wallet/ProviderDetailPage.tsx:148 +#, c-format +msgid "Back up" +msgstr "Создать резервную копию" + +#: src/wallet/ProviderDetailPage.tsx:154 +#, c-format +msgid "Provider fee" +msgstr "Комиссия провайдера" + +#: src/wallet/ProviderDetailPage.tsx:157 +#, c-format +msgid "per year" +msgstr "в год" + +#: src/wallet/ProviderDetailPage.tsx:163 +#, c-format +msgid "Extend" +msgstr "Расширить" + +#: src/wallet/ProviderDetailPage.tsx:169 +#, c-format +msgid "" +"terms has changed, extending the service will imply accepting the new terms of " +"service" +msgstr "" +"изменились условия, продление сервиса будет означать принятие новых условий " +"предоставления услуг" + +#: src/wallet/ProviderDetailPage.tsx:179 +#, c-format +msgid "old" +msgstr "старый" + +#: src/wallet/ProviderDetailPage.tsx:183 +#, c-format +msgid "new" +msgstr "новый" + +#: src/wallet/ProviderDetailPage.tsx:190 +#, c-format +msgid "fee" +msgstr "комиссия" + +#: src/wallet/ProviderDetailPage.tsx:198 +#, c-format +msgid "storage" +msgstr "хранение" + +#: src/wallet/ProviderDetailPage.tsx:215 +#, c-format +msgid "Remove provider" +msgstr "Удалить провадер" + +#: src/wallet/ProviderDetailPage.tsx:228 +#, c-format +msgid "This provider has reported an error" +msgstr "Этот провайдер сообщил об ошибке" + +#: src/wallet/ProviderDetailPage.tsx:242 +#, c-format +msgid "There is conflict with another backup from %1$s" +msgstr "Возник конфликт с другой резервной копией из %1$s" + +#: src/wallet/ProviderDetailPage.tsx:253 +#, c-format +msgid "Backup is not readable" +msgstr "Резервная копия не читается" + +#: src/wallet/ProviderDetailPage.tsx:261 +#, c-format +msgid "Unknown backup problem: %1$s" +msgstr "Неизвестная проблема резервного копирования: %1$s" + +#: src/wallet/ProviderDetailPage.tsx:283 +#, c-format +msgid "service paid" +msgstr "Услуга платная" + +#: src/wallet/ProviderDetailPage.tsx:290 +#, c-format +msgid "Backup valid until" +msgstr "Резервная копия действительна до" + +#: src/wallet/AddNewActionView.tsx:57 +#, c-format +msgid "Cancel" +msgstr "Отмена" + +#: src/wallet/AddNewActionView.tsx:68 +#, c-format +msgid "Open reserve page" +msgstr "Открыть резервную страницу" + +#: src/wallet/AddNewActionView.tsx:70 +#, c-format +msgid "Open pay page" +msgstr "Открыть страницу оплаты" + +#: src/wallet/AddNewActionView.tsx:72 +#, c-format +msgid "Open refund page" +msgstr "Открыть страницу возврата средств" + +#: src/wallet/AddNewActionView.tsx:74 +#, c-format +msgid "Open tip page" +msgstr "Открыть страницу чаевых" + +#: src/wallet/AddNewActionView.tsx:76 +#, c-format +msgid "Open withdraw page" +msgstr "Открыть страницу вывода средств" + +#: src/popup/NoBalanceHelp.tsx:43 +#, c-format +msgid "Get digital cash" +msgstr "Получите цифровую наличность" + +#: src/popup/BalancePage.tsx:138 +#, c-format +msgid "Could not load balance page" +msgstr "Не удалось загрузить страницу баланса" + +#: src/popup/BalancePage.tsx:175 +#, c-format +msgid "Add" +msgstr "Добавить" + +#: src/popup/BalancePage.tsx:179 +#, c-format +msgid "Send %1$s" +msgstr "Отправить %1$s" + +#: src/popup/TalerActionFound.tsx:44 +#, c-format +msgid "Taler Action" +msgstr "Действие Талер" + +#: src/popup/TalerActionFound.tsx:49 +#, c-format +msgid "This page has pay action." +msgstr "На этой странице есть платное действие." + +#: src/popup/TalerActionFound.tsx:63 +#, c-format +msgid "This page has a withdrawal action." +msgstr "На этой странице есть действие по выводу средств." + +#: src/popup/TalerActionFound.tsx:79 +#, c-format +msgid "This page has a tip action." +msgstr "На этой странице есть действие чаевых." + +#: src/popup/TalerActionFound.tsx:93 +#, c-format +msgid "This page has a notify reserve action." +msgstr "На этой странице есть действие уведомить о резерве." + +#: src/popup/TalerActionFound.tsx:102 +#, c-format +msgid "Notify" +msgstr "Уведомить" + +#: src/popup/TalerActionFound.tsx:109 +#, c-format +msgid "This page has a refund action." +msgstr "На этой странице есть действие по возврату средств." + +#: src/popup/TalerActionFound.tsx:123 +#, c-format +msgid "This page has a malformed taler uri." +msgstr "На этой странице неправильно сформирован Taler URI." + +#: src/popup/TalerActionFound.tsx:134 +#, c-format +msgid "Dismiss" +msgstr "Закрыть" + +#: src/popup/Application.tsx:177 +#, c-format +msgid "this popup is being closed and you are being redirected to %1$s" +msgstr "Это всплывающее окно закрывается и вы перенаправляетесь на %1$s" + +#: src/components/ShowFullContractTermPopup.tsx:158 +#, c-format +msgid "Could not load purchase proposal details" +msgstr "Не удалось загрузить сведения о предложении покупки" + +#: src/components/ShowFullContractTermPopup.tsx:183 +#, c-format +msgid "Order Id" +msgstr "Номер заказа" + +#: src/components/ShowFullContractTermPopup.tsx:189 +#, c-format +msgid "Summary" +msgstr "Вкратце" + +#: src/components/ShowFullContractTermPopup.tsx:195 +#, c-format +msgid "Amount" +msgstr "Сумма" + +#: src/components/ShowFullContractTermPopup.tsx:203 +#, c-format +msgid "Merchant name" +msgstr "Название продавца" + +#: src/components/ShowFullContractTermPopup.tsx:209 +#, c-format +msgid "Merchant jurisdiction" +msgstr "Юрисдикция продавца" + +#: src/components/ShowFullContractTermPopup.tsx:215 +#, c-format +msgid "Merchant address" +msgstr "Адрес продавца" + +#: src/components/ShowFullContractTermPopup.tsx:221 +#, c-format +msgid "Merchant logo" +msgstr "Логотип продавца" + +#: src/components/ShowFullContractTermPopup.tsx:234 +#, c-format +msgid "Merchant website" +msgstr "Сайт продавца" + +#: src/components/ShowFullContractTermPopup.tsx:240 +#, c-format +msgid "Merchant email" +msgstr "Email продавца" + +#: src/components/ShowFullContractTermPopup.tsx:246 +#, c-format +msgid "Merchant public key" +msgstr "Публичный ключ продавца" + +#: src/components/ShowFullContractTermPopup.tsx:256 +#, c-format +msgid "Delivery date" +msgstr "Дата поставки" + +#: src/components/ShowFullContractTermPopup.tsx:271 +#, c-format +msgid "Delivery location" +msgstr "Адрес доставки" + +#: src/components/ShowFullContractTermPopup.tsx:277 +#, c-format +msgid "Products" +msgstr "Продукты" + +#: src/components/ShowFullContractTermPopup.tsx:289 +#, c-format +msgid "Created at" +msgstr "Создано в" + +#: src/components/ShowFullContractTermPopup.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "Крайний срок возврата средств" + +#: src/components/ShowFullContractTermPopup.tsx:319 +#, c-format +msgid "Auto refund" +msgstr "Автоматический возврат средств" + +#: src/components/ShowFullContractTermPopup.tsx:339 +#, c-format +msgid "Pay deadline" +msgstr "Крайний срок оплаты" + +#: src/components/ShowFullContractTermPopup.tsx:354 +#, c-format +msgid "Fulfillment URL" +msgstr "URL-адрес выполнения" + +#: src/components/ShowFullContractTermPopup.tsx:360 +#, c-format +msgid "Fulfillment message" +msgstr "Сообщение о выполнении" + +#: src/components/ShowFullContractTermPopup.tsx:370 +#, c-format +msgid "Max deposit fee" +msgstr "Максимальная комиссия за депозит" + +#: src/components/ShowFullContractTermPopup.tsx:378 +#, c-format +msgid "Max fee" +msgstr "максимальная комиссия" + +#: src/components/ShowFullContractTermPopup.tsx:386 +#, c-format +msgid "Minimum age" +msgstr "Минимальный возраст" + +#: src/components/ShowFullContractTermPopup.tsx:398 +#, c-format +msgid "Wire fee amortization" +msgstr "Комиссия за банковский перевод" + +#: src/components/ShowFullContractTermPopup.tsx:404 +#, c-format +msgid "Auditors" +msgstr "Аудиторы" + +#: src/components/ShowFullContractTermPopup.tsx:419 +#, c-format +msgid "Exchanges" +msgstr "Обменники" + +#: src/components/Part.tsx:148 +#, c-format +msgid "Bank account" +msgstr "Баковский счёт" + +#: src/components/Part.tsx:160 +#, c-format +msgid "Bitcoin address" +msgstr "Биткоин адрес" + +#: src/components/Part.tsx:163 +#, c-format +msgid "IBAN" +msgstr "IBAN" + +#: src/cta/Deposit/views.tsx:38 +#, c-format +msgid "Could not load deposit status" +msgstr "Не удалось загрузить статус депозита" + +#: src/cta/Deposit/views.tsx:52 +#, c-format +msgid "Digital cash deposit" +msgstr "Депозит цифровой налички" + +#: src/cta/Deposit/views.tsx:58 +#, c-format +msgid "Cost" +msgstr "Стоимость" + +#: src/cta/Deposit/views.tsx:66 +#, c-format +msgid "Fee" +msgstr "Комиссия" + +#: src/cta/Deposit/views.tsx:73 +#, c-format +msgid "To be received" +msgstr "К получению" + +#: src/cta/Deposit/views.tsx:84 +#, c-format +msgid "Send %1$s" +msgstr "Отправить %1$s" + +#: src/components/BankDetailsByPaytoType.tsx:63 +#, c-format +msgid "Bitcoin transfer details" +msgstr "Подробности перевода биткоина" + +#: src/components/BankDetailsByPaytoType.tsx:66 +#, c-format +msgid "" +"The exchange need a transaction with 3 output, one output is the exchange " +"account and the other two are segwit fake address for metadata with an minimum " +"amount." +msgstr "" +"Обменнику нужна транзакция с 3 выходами, один выход - это счёт обменника, а " +"два других - это сегвит фейк адрес для метаданных с минимальной суммой." + +#: src/components/BankDetailsByPaytoType.tsx:74 +#, c-format +msgid "" +"In bitcoincore wallet use 'Add Recipient' button to add two additional " +"recipient and copy addresses and amounts" +msgstr "" + +#: src/components/BankDetailsByPaytoType.tsx:98 +#, c-format +msgid "Make sure the amount show %1$s BTC, else you have to change the base unit to BTC" +msgstr "" +"Убедитесь что сумма показывает %1$s BTC, в противном случае вам придётся " +"изменить базовую единицу на BTC" + +#: src/components/BankDetailsByPaytoType.tsx:110 +#, c-format +msgid "Account" +msgstr "Счёт" + +#: src/components/BankDetailsByPaytoType.tsx:116 +#, c-format +msgid "Bank host" +msgstr "Хост банка" + +#: src/components/BankDetailsByPaytoType.tsx:139 +#, c-format +msgid "Bank transfer details" +msgstr "Подробности банковского перевода" + +#: src/components/BankDetailsByPaytoType.tsx:148 +#, c-format +msgid "Subject" +msgstr "Причина" + +#: src/components/BankDetailsByPaytoType.tsx:154 +#, c-format +msgid "Receiver name" +msgstr "Имя получателя" + +#: src/wallet/Transaction.tsx:98 +#, c-format +msgid "Could not load the transaction information" +msgstr "Не удалось загрузить информацию о транзакции" + +#: src/wallet/Transaction.tsx:191 +#, c-format +msgid "There was an error trying to complete the transaction" +msgstr "При попытке завершить транзакцию произошла ошибка" + +#: src/wallet/Transaction.tsx:200 +#, c-format +msgid "This transaction is not completed" +msgstr "Эта транзакция не завершена" + +#: src/wallet/Transaction.tsx:209 +#, c-format +msgid "Send" +msgstr "Отправить" + +#: src/wallet/Transaction.tsx:216 +#, c-format +msgid "Retry" +msgstr "Повторить попытку" + +#: src/wallet/Transaction.tsx:224 +#, c-format +msgid "Forget" +msgstr "Забыть" + +#: src/wallet/Transaction.tsx:241 +#, c-format +msgid "Caution!" +msgstr "Внимание!" + +#: src/wallet/Transaction.tsx:244 +#, c-format +msgid "" +"If you have already wired money to the exchange you will loose the chance to get " +"the coins form it." +msgstr "" +"Если вы уже перевели деньги на обменник вы потеряете шанс получить монеты с " +"нее." + +#: src/wallet/Transaction.tsx:259 +#, c-format +msgid "Confirm" +msgstr "Подтвердить" + +#: src/wallet/Transaction.tsx:267 +#, c-format +msgid "Withdrawal" +msgstr "Вывод" + +#: src/wallet/Transaction.tsx:286 +#, c-format +msgid "" +"Make sure to use the correct subject, otherwise the money will not arrive in " +"this wallet." +msgstr "" +"Убедитесь что вы указали правильное назначение, иначе деньги не поступят на " +"этот кошелек." + +#: src/wallet/Transaction.tsx:298 +#, c-format +msgid "" +"The bank did not yet confirmed the wire transfer. Go to the %1$s %2$s and check " +"there is no pending step." +msgstr "" +"Банк пока не подтвердил перевод. Перейдите к %1$s %2$s и проверьте нет ли " +"ожидающих шагов." + +#: src/wallet/Transaction.tsx:316 +#, c-format +msgid "Bank has confirmed the wire transfer. Waiting for the exchange to send the coins" +msgstr "Банк подтвердил перевод. Ожидание пока обменик отправит монеты" + +#: src/wallet/Transaction.tsx:325 +#, c-format +msgid "Details" +msgstr "Подробности" + +#: src/wallet/Transaction.tsx:360 +#, c-format +msgid "Payment" +msgstr "Платёж" + +#: src/wallet/Transaction.tsx:378 +#, c-format +msgid "Refunds" +msgstr "Возвраты" + +#: src/wallet/Transaction.tsx:385 +#, c-format +msgid "%1$s %2$s on %3$s" +msgstr "%1$s %2$s на %3$s" + +#: src/wallet/Transaction.tsx:415 +#, c-format +msgid "Merchant created a refund for this order but was not automatically picked up." +msgstr "" +"Продавец создал возврат средств за этот заказ, но не был автоматически " +"забран." + +#: src/wallet/Transaction.tsx:420 +#, c-format +msgid "Offer" +msgstr "Предложение" + +#: src/wallet/Transaction.tsx:431 +#, c-format +msgid "Accept" +msgstr "Принять" + +#: src/wallet/Transaction.tsx:438 +#, c-format +msgid "Merchant" +msgstr "Продавец" + +#: src/wallet/Transaction.tsx:443 +#, c-format +msgid "Invoice ID" +msgstr "№ счёта-фактуры" + +#: src/wallet/Transaction.tsx:470 +#, c-format +msgid "Deposit" +msgstr "Депозит" + +#: src/wallet/Transaction.tsx:496 +#, c-format +msgid "Refresh" +msgstr "Обновить" + +#: src/wallet/Transaction.tsx:517 +#, c-format +msgid "Tip" +msgstr "Чаевые" + +#: src/wallet/Transaction.tsx:542 +#, c-format +msgid "Refund" +msgstr "Возврат" + +#: src/wallet/Transaction.tsx:555 +#, c-format +msgid "Original order ID" +msgstr "№ исходного заказа" + +#: src/wallet/Transaction.tsx:568 +#, c-format +msgid "Purchase summary" +msgstr "Сводка о покупке" + +#: src/wallet/Transaction.tsx:593 +#, c-format +msgid "copy" +msgstr "копировать" + +#: src/wallet/Transaction.tsx:596 +#, c-format +msgid "hide qr" +msgstr "спрятать qr" + +#: src/wallet/Transaction.tsx:608 +#, c-format +msgid "show qr" +msgstr "показать qr" + +#: src/wallet/Transaction.tsx:620 +#, c-format +msgid "Credit" +msgstr "Кредит" + +#: src/wallet/Transaction.tsx:624 +#, c-format +msgid "Invoice" +msgstr "Счёт-фактура" + +#: src/wallet/Transaction.tsx:635 +#, c-format +msgid "Exchange" +msgstr "Обменник" + +#: src/wallet/Transaction.tsx:641 +#, c-format +msgid "URI" +msgstr "URI" + +#: src/wallet/Transaction.tsx:667 +#, c-format +msgid "Debit" +msgstr "Дебит" + +#: src/wallet/Transaction.tsx:710 +#, c-format +msgid "Transfer" +msgstr "Перевести" + +#: src/wallet/Transaction.tsx:844 +#, c-format +msgid "Country" +msgstr "Страна" + +#: src/wallet/Transaction.tsx:852 +#, c-format +msgid "Address lines" +msgstr "Строки адреса" + +#: src/wallet/Transaction.tsx:860 +#, c-format +msgid "Building number" +msgstr "Номер дома" + +#: src/wallet/Transaction.tsx:868 +#, c-format +msgid "Building name" +msgstr "Название дома" + +#: src/wallet/Transaction.tsx:876 +#, c-format +msgid "Street" +msgstr "Улица" + +#: src/wallet/Transaction.tsx:884 +#, c-format +msgid "Post code" +msgstr "Почтовый индекс" + +#: src/wallet/Transaction.tsx:892 +#, c-format +msgid "Town location" +msgstr "Область города" + +#: src/wallet/Transaction.tsx:900 +#, c-format +msgid "Town" +msgstr "Город" + +#: src/wallet/Transaction.tsx:908 +#, c-format +msgid "District" +msgstr "Район" + +#: src/wallet/Transaction.tsx:916 +#, c-format +msgid "Country subdivision" +msgstr "Регион страны" + +#: src/wallet/Transaction.tsx:935 +#, c-format +msgid "Date" +msgstr "Дата" + +#: src/wallet/Transaction.tsx:990 +#, c-format +msgid "Transaction fees" +msgstr "Комиссия транзакции" + +#: src/wallet/Transaction.tsx:1004 +#, c-format +msgid "Total" +msgstr "Всего" + +#: src/wallet/Transaction.tsx:1074 +#, c-format +msgid "Withdraw" +msgstr "Снять средства" + +#: src/wallet/Transaction.tsx:1146 +#, c-format +msgid "Price" +msgstr "Цена" + +#: src/wallet/Transaction.tsx:1156 +#, c-format +msgid "Refunded" +msgstr "Возвращено на счёт" + +#: src/wallet/Transaction.tsx:1220 +#, c-format +msgid "Delivery" +msgstr "Поставка" + +#: src/wallet/Transaction.tsx:1335 +#, c-format +msgid "Total transfer" +msgstr "Итого перевод" + +#: src/cta/Payment/views.tsx:57 +#, c-format +msgid "Could not load pay status" +msgstr "Не удалось загрузить статус оплаты" + +#: src/cta/Payment/views.tsx:87 +#, c-format +msgid "Digital cash payment" +msgstr "Оплата цифровой наличкой" + +#: src/cta/Payment/views.tsx:119 +#, c-format +msgid "Purchase" +msgstr "Покупка" + +#: src/cta/Payment/views.tsx:149 +#, c-format +msgid "Receipt" +msgstr "Чек" + +#: src/cta/Payment/views.tsx:156 +#, c-format +msgid "Valid until" +msgstr "Действительно до" + +#: src/cta/Payment/views.tsx:191 +#, c-format +msgid "List of products" +msgstr "Список продуктов" + +#: src/cta/Payment/views.tsx:242 +#, c-format +msgid "free" +msgstr "комиссия" + +#: src/cta/Payment/views.tsx:263 +#, c-format +msgid "Already paid, you are going to be redirected to %1$s" +msgstr "Уже оплачено, вы будете перенаправлены на %1$s" + +#: src/cta/Payment/views.tsx:274 +#, c-format +msgid "Already paid" +msgstr "Уже оплачено" + +#: src/cta/Payment/views.tsx:280 +#, c-format +msgid "Already claimed" +msgstr "Уже заявлено" + +#: src/cta/Payment/views.tsx:296 +#, c-format +msgid "Pay with a mobile phone" +msgstr "Оплата с помощью мобильного телефона" + +#: src/cta/Payment/views.tsx:298 +#, c-format +msgid "Hide QR" +msgstr "Скрыть QR" + +#: src/cta/Payment/views.tsx:305 +#, c-format +msgid "Scan the QR code or %1$s" +msgstr "Отсканируйте QR код или %1$s" + +#: src/cta/Payment/views.tsx:346 +#, c-format +msgid "Pay %1$s" +msgstr "Заплатить %1$s" + +#: src/cta/Payment/views.tsx:360 +#, c-format +msgid "You have no balance for this currency. Withdraw digital cash first." +msgstr "У вас нет баланса в этой валюте. Сначала снимите цифровые деньги." + +#: src/cta/Payment/views.tsx:364 +#, c-format +msgid "" +"Could not find enough coins to pay. Even if you have enough %1$s some " +"restriction may apply." +msgstr "" +"Не удалось найти достаточно монет для оплаты. Даже если у вас достаточно %1$" +"s, могут применяться некоторые ограничения." + +#: src/cta/Payment/views.tsx:366 +#, c-format +msgid "Your current balance is not enough." +msgstr "Недостаточно средств на балансе." + +#: src/cta/Payment/views.tsx:395 +#, c-format +msgid "Merchant message" +msgstr "Сообщение продавца" + +#: src/cta/Refund/views.tsx:34 +#, c-format +msgid "Could not load refund status" +msgstr "Не удалось загрузить статус возврата" + +#: src/cta/Refund/views.tsx:48 +#, c-format +msgid "Digital cash refund" +msgstr "Возврат цифровой налички" + +#: src/cta/Refund/views.tsx:52 +#, c-format +msgid "You've ignored the tip." +msgstr "Вы проигнорировали чаевые." + +#: src/cta/Refund/views.tsx:70 +#, c-format +msgid "The refund is in progress." +msgstr "Возврат средств в выполняется." + +#: src/cta/Refund/views.tsx:76 +#, c-format +msgid "Total to refund" +msgstr "Всего к возврату" + +#: src/cta/Refund/views.tsx:106 +#, c-format +msgid "The merchant "%1$s" is offering you a refund." +msgstr "Продавец «%1$s» предлагает вам возврат средств." + +#: src/cta/Refund/views.tsx:115 +#, c-format +msgid "Order amount" +msgstr "Сумма заказа" + +#: src/cta/Refund/views.tsx:122 +#, c-format +msgid "Already refunded" +msgstr "Уже возвращено" + +#: src/cta/Refund/views.tsx:129 +#, c-format +msgid "Refund offered" +msgstr "Предложен возврат средств" + +#: src/cta/Refund/views.tsx:145 +#, c-format +msgid "Accept %1$s" +msgstr "Принять %1$s" + +#: src/cta/Tip/views.tsx:32 +#, c-format +msgid "Could not load tip status" +msgstr "Не удалось загрузить статус чаевых" + +#: src/cta/Tip/views.tsx:45 +#, c-format +msgid "Digital cash tip" +msgstr "Чаевые цифровой налички" + +#: src/cta/Tip/views.tsx:66 +#, c-format +msgid "The merchant is offering you a tip" +msgstr "Продавец предлагает вам чаевые" + +#: src/cta/Tip/views.tsx:74 +#, c-format +msgid "Merchant URL" +msgstr "URL-адрес продавца" + +#: src/cta/Tip/views.tsx:90 +#, c-format +msgid "Receive %1$s" +msgstr "Получить %1$s" + +#: src/cta/Tip/views.tsx:114 +#, c-format +msgid "Tip from %1$s accepted. Check your transactions list for more details." +msgstr "Чаевые от %1$s приняты. Проверьте список транзакций для подробностей." + +#: src/components/SelectList.tsx:66 +#, c-format +msgid "Select one option" +msgstr "Выберете одну опцию" + +#: src/components/TermsOfService/views.tsx:39 +#, c-format +msgid "Could not load" +msgstr "Невозможно загрузить" + +#: src/components/TermsOfService/views.tsx:73 +#, c-format +msgid "Show terms of service" +msgstr "Показать Условия использования" + +#: src/components/TermsOfService/views.tsx:81 +#, c-format +msgid "I accept the exchange terms of service" +msgstr "Я принимаю эти Условия использования" + +#: src/components/TermsOfService/views.tsx:107 +#, c-format +msgid "Exchange doesn't have terms of service" +msgstr "Обменник не имеет условий использования" + +#: src/components/TermsOfService/views.tsx:135 +#, c-format +msgid "Review exchange terms of service" +msgstr "Ознакомиться с Условиями использования" + +#: src/components/TermsOfService/views.tsx:146 +#, c-format +msgid "Review new version of terms of service" +msgstr "Ознакомиться с новой версией Условий использования" + +#: src/components/TermsOfService/views.tsx:170 +#, c-format +msgid "The exchange reply with a empty terms of service" +msgstr "Биржа ответитила с пустыми условиями использования" + +#: src/components/TermsOfService/views.tsx:193 +#, c-format +msgid "Download Terms of Service" +msgstr "Скачать Условия использования" + +#: src/components/TermsOfService/views.tsx:204 +#, c-format +msgid "Hide terms of service" +msgstr "Скрыть Условия использования" + +#: src/wallet/ExchangeSelection/views.tsx:117 +#, c-format +msgid "Could not load exchange fees" +msgstr "Не удалось загрузить комиссию за обмен" + +#: src/wallet/ExchangeSelection/views.tsx:131 +#, c-format +msgid "Close" +msgstr "Закрыть" + +#: src/wallet/ExchangeSelection/views.tsx:160 +#, c-format +msgid "could not find any exchange" +msgstr "Не удалось найти ни одного обменника" + +#: src/wallet/ExchangeSelection/views.tsx:166 +#, c-format +msgid "could not find any exchange for the currency %1$s" +msgstr "Не удалось найти ни одного обменника для валюты %1$s" + +#: src/wallet/ExchangeSelection/views.tsx:186 +#, c-format +msgid "Service fee description" +msgstr "Описание комиссии за услугу" + +#: src/wallet/ExchangeSelection/views.tsx:201 +#, c-format +msgid "Select %1$s exchange" +msgstr "Выберите %1$s обменник" + +#: src/wallet/ExchangeSelection/views.tsx:215 +#, c-format +msgid "Reset" +msgstr "Сбросить" + +#: src/wallet/ExchangeSelection/views.tsx:218 +#, c-format +msgid "Use this exchange" +msgstr "Использовать этот обменник" + +#: src/wallet/ExchangeSelection/views.tsx:230 +#, c-format +msgid "Doesn't have auditors" +msgstr "Не имеет аудиторов" + +#: src/wallet/ExchangeSelection/views.tsx:241 +#, c-format +msgid "currency" +msgstr "валюта" + +#: src/wallet/ExchangeSelection/views.tsx:249 +#, c-format +msgid "Operations" +msgstr "Операции" + +#: src/wallet/ExchangeSelection/views.tsx:252 +#, c-format +msgid "Deposits" +msgstr "Депозиты" + +#: src/wallet/ExchangeSelection/views.tsx:259 +#, c-format +msgid "Denomination" +msgstr "Деноминация" + +#: src/wallet/ExchangeSelection/views.tsx:265 +#, c-format +msgid "Until" +msgstr "до" + +#: src/wallet/ExchangeSelection/views.tsx:274 +#, c-format +msgid "Withdrawals" +msgstr "Выводы средств" + +#: src/wallet/ExchangeSelection/views.tsx:423 +#, c-format +msgid "Currency" +msgstr "Валюта" + +#: src/wallet/ExchangeSelection/views.tsx:433 +#, c-format +msgid "Coin operations" +msgstr "Операции моент" + +#: src/wallet/ExchangeSelection/views.tsx:436 +#, c-format +msgid "" +"Every operation in this section may be different by denomination value and is " +"valid for a period of time. The exchange will charge the indicated amount every " +"time a coin is used in such operation." +msgstr "" +"Каждая операция в этом разделе может отличаться номиналом и действительна в " +"течение определенного периода времени. Биржа будет взимать указанную сумму " +"каждый раз, когда монета используется в такой операции." + +#: src/wallet/ExchangeSelection/views.tsx:545 +#, c-format +msgid "Transfer operations" +msgstr "Операции переводов" + +#: src/wallet/ExchangeSelection/views.tsx:548 +#, c-format +msgid "" +"Every operation in this section may be different by transfer type and is valid " +"for a period of time. The exchange will charge the indicated amount every time a " +"transfer is made." +msgstr "" +"Каждая операция в этом разделе может отличаться в зависимости от типа " +"перевода и действительна в течение определенного периода времени. Обменник " +"будет взимать указанную сумму каждый раз при совершении перевода." + +#: src/wallet/ExchangeSelection/views.tsx:563 +#, c-format +msgid "Operation" +msgstr "Операция" + +#: src/wallet/ExchangeSelection/views.tsx:583 +#, c-format +msgid "Wallet operations" +msgstr "Операции кошелька" + +#: src/wallet/ExchangeSelection/views.tsx:597 +#, c-format +msgid "Feature" +msgstr "Возможность" + +#: src/cta/Withdraw/views.tsx:47 +#, c-format +msgid "Could not get the info from the URI" +msgstr "Не удалось получить информацию из URI" + +#: src/cta/Withdraw/views.tsx:60 +#, c-format +msgid "Could not get info of withdrawal" +msgstr "Не удалось получить информацию о выводе средств" + +#: src/cta/Withdraw/views.tsx:74 +#, c-format +msgid "Digital cash withdrawal" +msgstr "Вывод цифровых наличных" + +#: src/cta/Withdraw/views.tsx:79 +#, c-format +msgid "Could not finish the withdrawal operation" +msgstr "" + +#: src/cta/Withdraw/views.tsx:127 +#, c-format +msgid "Age restriction" +msgstr "Ограничения возраста" + +#: src/cta/Withdraw/views.tsx:145 +#, c-format +msgid "Withdraw %1$s" +msgstr "Вывести %1$s" + +#: src/cta/Withdraw/views.tsx:179 +#, c-format +msgid "Withdraw to a mobile phone" +msgstr "Вывести на мобильный телефон" + +#: src/cta/InvoiceCreate/views.tsx:65 +#, c-format +msgid "Digital invoice" +msgstr "Цифровой счёт-фактура" + +#: src/cta/InvoiceCreate/views.tsx:69 +#, c-format +msgid "Could not finish the invoice creation" +msgstr "" + +#: src/cta/InvoiceCreate/views.tsx:130 +#, c-format +msgid "Create" +msgstr "Создать" + +#: src/cta/InvoicePay/views.tsx:63 +#, c-format +msgid "Could not finish the payment operation" +msgstr "" + +#: src/cta/TransferCreate/views.tsx:55 +#, c-format +msgid "Digital cash transfer" +msgstr "" + +#: src/cta/TransferCreate/views.tsx:59 +#, c-format +msgid "Could not finish the transfer creation" +msgstr "" + +#: src/cta/TransferPickup/views.tsx:57 +#, c-format +msgid "Could not finish the pickup operation" +msgstr "" + +#: src/wallet/CreateManualWithdraw.tsx:149 +#, c-format +msgid "Manual Withdrawal for %1$s" +msgstr "" + +#: src/wallet/CreateManualWithdraw.tsx:154 +#, c-format +msgid "" +"Choose a exchange from where the coins will be withdrawn. The exchange will send " +"the coins to this wallet after receiving a wire transfer with the correct " +"subject." +msgstr "" + +#: src/wallet/CreateManualWithdraw.tsx:162 +#, c-format +msgid "No exchange found for %1$s" +msgstr "" + +#: src/wallet/CreateManualWithdraw.tsx:170 +#, c-format +msgid "Add Exchange" +msgstr "Добавить Обменник" + +#: src/wallet/CreateManualWithdraw.tsx:192 +#, c-format +msgid "No exchange configured" +msgstr "" + +#: src/wallet/CreateManualWithdraw.tsx:210 +#, c-format +msgid "Can't create the reserve" +msgstr "" + +#: src/wallet/CreateManualWithdraw.tsx:277 +#, c-format +msgid "Start withdrawal" +msgstr "Начать вывод" + +#: src/wallet/DepositPage/views.tsx:38 +#, c-format +msgid "Could not load deposit balance" +msgstr "" + +#: src/wallet/DepositPage/views.tsx:51 +#, c-format +msgid "A currency or an amount should be indicated" +msgstr "" + +#: src/wallet/DepositPage/views.tsx:67 +#, c-format +msgid "There is no enough balance to make a deposit for currency %1$s" +msgstr "" + +#: src/wallet/DepositPage/views.tsx:117 +#, c-format +msgid "Send %1$s to your account" +msgstr "" + +#: src/wallet/DepositPage/views.tsx:121 +#, c-format +msgid "There is no account to make a deposit for currency %1$s" +msgstr "" + +#: src/wallet/DepositPage/views.tsx:127 +#, c-format +msgid "Add account" +msgstr "Добавить Счёт" + +#: src/wallet/DepositPage/views.tsx:151 +#, c-format +msgid "Select account" +msgstr "Выберете счёт" + +#: src/wallet/DepositPage/views.tsx:163 +#, c-format +msgid "Add another account" +msgstr "Добавить другой счёт" + +#: src/wallet/DepositPage/views.tsx:191 +#, c-format +msgid "Deposit fee" +msgstr "Комиссия депозита" + +#: src/wallet/DepositPage/views.tsx:205 +#, c-format +msgid "Total deposit" +msgstr "Всего к депозиту" + +#: src/wallet/DepositPage/views.tsx:233 +#, c-format +msgid "Deposit %1$s %2$s" +msgstr "" + +#: src/wallet/AddAccount/views.tsx:56 +#, c-format +msgid "Add bank account for %1$s" +msgstr "" + +#: src/wallet/AddAccount/views.tsx:59 +#, c-format +msgid "Enter the URL of an exchange you trust." +msgstr "" + +#: src/wallet/AddAccount/views.tsx:66 +#, c-format +msgid "Unable add this account" +msgstr "" + +#: src/wallet/AddAccount/views.tsx:73 +#, c-format +msgid "Select account type" +msgstr "Выберете тип счёта" + +#: src/wallet/ExchangeAddConfirm.tsx:42 +#, c-format +msgid "Review terms of service" +msgstr "" + +#: src/wallet/ExchangeAddConfirm.tsx:45 +#, c-format +msgid "Exchange URL" +msgstr "URL обменника" + +#: src/wallet/ExchangeAddConfirm.tsx:70 +#, c-format +msgid "Add exchange" +msgstr "Добавить Обменник" + +#: src/wallet/ExchangeSetUrl.tsx:112 +#, c-format +msgid "Add new exchange" +msgstr "Добавить новый Обменник" + +#: src/wallet/ExchangeSetUrl.tsx:116 +#, c-format +msgid "Add exchange for %1$s" +msgstr "" + +#: src/wallet/ExchangeSetUrl.tsx:128 +#, c-format +msgid "An exchange has been found! Review the information and click next" +msgstr "" + +#: src/wallet/ExchangeSetUrl.tsx:135 +#, c-format +msgid "This exchange doesn't match the expected currency %1$s" +msgstr "" + +#: src/wallet/ExchangeSetUrl.tsx:143 +#, c-format +msgid "Unable to verify this exchange" +msgstr "" + +#: src/wallet/ExchangeSetUrl.tsx:151 +#, c-format +msgid "Unable to add this exchange" +msgstr "" + +#: src/wallet/ExchangeSetUrl.tsx:167 +#, c-format +msgid "loading" +msgstr "загрузка" + +#: src/wallet/ExchangeSetUrl.tsx:174 +#, c-format +msgid "Version" +msgstr "Версия" + +#: src/wallet/ExchangeSetUrl.tsx:206 +#, c-format +msgid "Next" +msgstr "Далее" + +#: src/components/TransactionItem.tsx:201 +#, c-format +msgid "Waiting for confirmation" +msgstr "Ожидание подтверждения" + +#: src/components/TransactionItem.tsx:266 +#, c-format +msgid "PENDING" +msgstr "ОЖИДАЕТ" + +#: src/wallet/History.tsx:75 +#, c-format +msgid "Could not load the list of transactions" +msgstr "" + +#: src/wallet/History.tsx:233 +#, c-format +msgid "Your transaction history is empty for this currency." +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:127 +#, c-format +msgid "Add backup provider" +msgstr "Добавить провайдера резервной копии" + +#: src/wallet/ProviderAddPage.tsx:131 +#, c-format +msgid "Could not get provider information" +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:140 +#, c-format +msgid "Backup providers may charge for their service" +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:147 +#, c-format +msgid "URL" +msgstr "URL" + +#: src/wallet/ProviderAddPage.tsx:158 +#, c-format +msgid "Name" +msgstr "Название" + +#: src/wallet/ProviderAddPage.tsx:212 +#, c-format +msgid "Provider URL" +msgstr "URL провайдера" + +#: src/wallet/ProviderAddPage.tsx:218 +#, c-format +msgid "Please review and accept this provider's terms of service" +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:223 +#, c-format +msgid "Pricing" +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:226 +#, c-format +msgid "free of charge" +msgstr "комиссия за пополнение" + +#: src/wallet/ProviderAddPage.tsx:228 +#, c-format +msgid "%1$s per year of service" +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:235 +#, c-format +msgid "Storage" +msgstr "Хранилище" + +#: src/wallet/ProviderAddPage.tsx:238 +#, c-format +msgid "%1$s megabytes of storage per year of service" +msgstr "" + +#: src/wallet/ProviderAddPage.tsx:244 +#, c-format +msgid "Accept terms of service" +msgstr "" + +#: src/wallet/ReserveCreated.tsx:44 +#, c-format +msgid "Could not parse the payto URI" +msgstr "" + +#: src/wallet/ReserveCreated.tsx:45 +#, c-format +msgid "Please check the uri" +msgstr "" + +#: src/wallet/ReserveCreated.tsx:75 +#, c-format +msgid "Exchange is ready for withdrawal" +msgstr "" + +#: src/wallet/ReserveCreated.tsx:78 +#, c-format +msgid "To complete the process you need to wire%1$s %2$s to the exchange bank account" +msgstr "" + +#: src/wallet/ReserveCreated.tsx:87 +#, c-format +msgid "" +"Alternative, you can also scan this QR code or open %1$s if you have a banking " +"app installed that supports RFC 8905" +msgstr "" + +#: src/wallet/ReserveCreated.tsx:98 +#, c-format +msgid "Cancel withdrawal" +msgstr "" + +#: src/wallet/Settings.tsx:115 +#, c-format +msgid "Could not toggle auto-open" +msgstr "" + +#: src/wallet/Settings.tsx:121 +#, c-format +msgid "Could not toggle clipboard" +msgstr "" + +#: src/wallet/Settings.tsx:126 +#, c-format +msgid "Navigator" +msgstr "Навигатор" + +#: src/wallet/Settings.tsx:129 +#, c-format +msgid "Automatically open wallet based on page content" +msgstr "" + +#: src/wallet/Settings.tsx:135 +#, c-format +msgid "" +"Enabling this option below will make using the wallet faster, but requires more " +"permissions from your browser." +msgstr "" + +#: src/wallet/Settings.tsx:145 +#, c-format +msgid "Automatically check clipboard for Taler URI" +msgstr "" + +#: src/wallet/Settings.tsx:162 +#, c-format +msgid "Trust" +msgstr "Доверять" + +#: src/wallet/Settings.tsx:166 +#, c-format +msgid "No exchange yet" +msgstr "" + +#: src/wallet/Settings.tsx:180 +#, c-format +msgid "Term of Service" +msgstr "Условия использования" + +#: src/wallet/Settings.tsx:191 +#, c-format +msgid "ok" +msgstr "ok" + +#: src/wallet/Settings.tsx:197 +#, c-format +msgid "changed" +msgstr "изменено" + +#: src/wallet/Settings.tsx:204 +#, c-format +msgid "not accepted" +msgstr "не принято" + +#: src/wallet/Settings.tsx:210 +#, c-format +msgid "unknown (exchange status should be updated)" +msgstr "" + +#: src/wallet/Settings.tsx:236 +#, c-format +msgid "Add an exchange" +msgstr "" + +#: src/wallet/Settings.tsx:241 +#, c-format +msgid "Troubleshooting" +msgstr "Исправление проблем" + +#: src/wallet/Settings.tsx:244 +#, c-format +msgid "Developer mode" +msgstr "Режим разработчика" + +#: src/wallet/Settings.tsx:246 +#, c-format +msgid "More options and information useful for debugging" +msgstr "" + +#: src/wallet/Settings.tsx:257 +#, c-format +msgid "Display" +msgstr "Отбражение" + +#: src/wallet/Settings.tsx:261 +#, c-format +msgid "Current Language" +msgstr "" + +#: src/wallet/Settings.tsx:274 +#, c-format +msgid "Wallet Core" +msgstr "" + +#: src/wallet/Settings.tsx:284 +#, c-format +msgid "Web Extension" +msgstr "Расширение браузера" + +#: src/wallet/Settings.tsx:295 +#, c-format +msgid "Exchange compatibility" +msgstr "" + +#: src/wallet/Settings.tsx:299 +#, c-format +msgid "Merchant compatibility" +msgstr "" + +#: src/wallet/Settings.tsx:303 +#, c-format +msgid "Bank compatibility" +msgstr "" + +#: src/wallet/Welcome.tsx:59 +#, c-format +msgid "Browser Extension Installed!" +msgstr "" + +#: src/wallet/Welcome.tsx:63 +#, c-format +msgid "You can open the GNU Taler Wallet using the combination %1$s ." +msgstr "" + +#: src/wallet/Welcome.tsx:72 +#, c-format +msgid "" +"Also pinning the GNU Taler Wallet to your Chrome browser allows you to quick " +"access without keyboard:" +msgstr "" + +#: src/wallet/Welcome.tsx:79 +#, c-format +msgid "Click the puzzle icon" +msgstr "" + +#: src/wallet/Welcome.tsx:82 +#, c-format +msgid "Search for GNU Taler Wallet" +msgstr "" + +#: src/wallet/Welcome.tsx:85 +#, c-format +msgid "Click the pin icon" +msgstr "" + +#: src/wallet/Welcome.tsx:91 +#, c-format +msgid "Permissions" +msgstr "Разрешения" + +#: src/wallet/Welcome.tsx:100 +#, c-format +msgid "" +"(Enabling this option below will make using the wallet faster, but requires more " +"permissions from your browser.)" +msgstr "" + +#: src/wallet/Welcome.tsx:110 +#, c-format +msgid "Next Steps" +msgstr "Следующий шаг" + +#: src/wallet/Welcome.tsx:113 +#, c-format +msgid "Try the demo" +msgstr "Попробовать демо" + +#: src/wallet/Welcome.tsx:116 +#, c-format +msgid "Learn how to top up your wallet balance" +msgstr "Узнайте как пополнить ваш баланс на кошельке" + +#: src/components/Diagnostics.tsx:31 +#, c-format +msgid "Diagnostics timed out. Could not talk to the wallet backend." +msgstr "" + +#: src/components/Diagnostics.tsx:52 +#, c-format +msgid "Problems detected:" +msgstr "" + +#: src/components/Diagnostics.tsx:61 +#, c-format +msgid "" +"Please check in your %1$s settings that you have IndexedDB enabled (check the " +"preference name %2$s)." +msgstr "" + +#: src/components/Diagnostics.tsx:70 +#, c-format +msgid "" +"Your wallet database is outdated. Currently automatic migration is not " +"supported. Please go %1$s to reset the wallet database." +msgstr "" + +#: src/components/Diagnostics.tsx:83 +#, c-format +msgid "Running diagnostics" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:163 +#, c-format +msgid "Debug tools" +msgstr "Инструменты отладки" + +#: src/wallet/DeveloperPage.tsx:170 +#, c-format +msgid "" +"Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL " +"YOUR COINS?" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:176 +#, c-format +msgid "reset" +msgstr "сбросить" + +#: src/wallet/DeveloperPage.tsx:183 +#, c-format +msgid "TESTING: This may delete all your coin, proceed with caution" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:189 +#, c-format +msgid "run gc" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:197 +#, c-format +msgid "import database" +msgstr "импортировать базу данных" + +#: src/wallet/DeveloperPage.tsx:219 +#, c-format +msgid "export database" +msgstr "экспортировать базу данных" + +#: src/wallet/DeveloperPage.tsx:225 +#, c-format +msgid "Database exported at %1$s %2$s to download" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:248 +#, c-format +msgid "Coins" +msgstr "Монеты" + +#: src/wallet/DeveloperPage.tsx:282 +#, c-format +msgid "Pending operations" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:328 +#, c-format +msgid "usable coins" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:337 +#, c-format +msgid "id" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:340 +#, c-format +msgid "denom" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:343 +#, c-format +msgid "value" +msgstr "значение" + +#: src/wallet/DeveloperPage.tsx:346 +#, c-format +msgid "status" +msgstr "статус" + +#: src/wallet/DeveloperPage.tsx:349 +#, c-format +msgid "from refresh?" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:352 +#, c-format +msgid "age key count" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:369 +#, c-format +msgid "spent coins" +msgstr "" + +#: src/wallet/DeveloperPage.tsx:373 +#, c-format +msgid "click to show" +msgstr "кликите чтобы показать" + +#: src/wallet/QrReader.tsx:108 +#, c-format +msgid "Scan a QR code or enter taler:// URI below" +msgstr "" + +#: src/wallet/QrReader.tsx:122 +#, c-format +msgid "Open" +msgstr "Открыть" + +#: src/wallet/QrReader.tsx:128 +#, c-format +msgid "URI is not valid. Taler URI should start with `taler://`" +msgstr "" + +#: src/wallet/QrReader.tsx:133 +#, c-format +msgid "Try another" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:183 +#, c-format +msgid "Could not load list of exchange" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:209 +#, c-format +msgid "Choose a currency to proceed or add another exchange" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:217 +#, c-format +msgid "Known currencies" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:318 +#, c-format +msgid "Specify the amount and the origin" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:336 +#, c-format +msgid "Change currency" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:344 +#, c-format +msgid "Use previous origins:" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:364 +#, c-format +msgid "Or specify the origin of the money" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:372 +#, c-format +msgid "Specify the origin of the money" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:380 +#, c-format +msgid "From my bank account" +msgstr "Из моего банковского счёта" + +#: src/wallet/DestinationSelection.tsx:395 +#, c-format +msgid "From another wallet" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:449 +#, c-format +msgid "currency not provided" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:459 +#, c-format +msgid "Specify the amount and the destination" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:483 +#, c-format +msgid "Use previous destinations:" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:503 +#, c-format +msgid "Or specify the destination of the money" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:511 +#, c-format +msgid "Specify the destination of the money" +msgstr "" + +#: src/wallet/DestinationSelection.tsx:521 +#, c-format +msgid "To my bank account" +msgstr "На мой банковский счёт" + +#: src/wallet/DestinationSelection.tsx:534 +#, c-format +msgid "To another wallet" +msgstr "На другой кошелек" + +#: src/cta/Recovery/views.tsx:30 +#, c-format +msgid "Could not load backup recovery information" +msgstr "" + +#: src/cta/Recovery/views.tsx:47 +#, c-format +msgid "Digital wallet recovery" +msgstr "" + +#: src/cta/Recovery/views.tsx:52 +#, c-format +msgid "Import backup, show info" +msgstr "Импорт резервной копии, отображение информации" + +#: src/wallet/Application.tsx:189 +#, c-format +msgid "All done, your transaction is in progress" +msgstr "Все готово, ваша транзакция выполняется" + +#: src/components/EditableText.tsx:45 +#, c-format +msgid "Edit" +msgstr "Изменить" + +#: src/wallet/ManualWithdrawPage.tsx:102 +#, c-format +msgid "Could not load the list of known exchanges" +msgstr "Не удалось загрузить список известных обменников" diff --git a/packages/taler-wallet-webextension/src/mui/Button.tsx b/packages/taler-wallet-webextension/src/mui/Button.tsx index 1af281d42..12a4d91ea 100644 --- a/packages/taler-wallet-webextension/src/mui/Button.tsx +++ b/packages/taler-wallet-webextension/src/mui/Button.tsx @@ -371,7 +371,11 @@ function ButtonBase({ ); } return ( - <button onClick={doClick} class={classNames} {...rest}> + <button onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + doClick(); + }} class={classNames} {...rest}> {children} </button> ); diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx index 53380e263..7b6ac8895 100644 --- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx @@ -19,12 +19,10 @@ import { Amounts, CoinDumpJson, CoinStatus, - ExchangeListItem, ExchangeTosStatus, LogLevel, NotificationType, ScopeType, - parseWithdrawUri, stringifyWithdrawExchange, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -32,10 +30,19 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; import { Fragment, VNode, h } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; +import { Pages } from "../NavigationBar.js"; import { Checkbox } from "../components/Checkbox.js"; import { SelectList } from "../components/SelectList.js"; import { Time } from "../components/Time.js"; -import { DestructiveText, LinkPrimary, NotifyUpdateFadeOut, SubTitle, SuccessText, WarningText } from "../components/styled/index.js"; +import { ActiveTasksTable } from "../components/WalletActivity.js"; +import { + DestructiveText, + LinkPrimary, + NotifyUpdateFadeOut, + SubTitle, + SuccessText, + WarningText, +} from "../components/styled/index.js"; import { useAlertContext } from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; @@ -44,9 +51,6 @@ import { Button } from "../mui/Button.js"; import { Grid } from "../mui/Grid.js"; import { Paper } from "../mui/Paper.js"; import { TextField } from "../mui/TextField.js"; -import { Pages } from "../NavigationBar.js"; -import { CoinInfo } from "@gnu-taler/taler-wallet-core/dbless"; -import { ActiveTasksTable } from "../components/WalletActivity.js"; type CoinsInfo = CoinDumpJson["coins"]; type CalculatedCoinfInfo = { @@ -72,7 +76,7 @@ function hashObjectId(o: any): string { return JSON.stringify(o); } -export function DeveloperPage({ }: Props): VNode { +export function DeveloperPage({}: Props): VNode { const { i18n } = useTranslationContext(); const [downloadedDatabase, setDownloadedDatabase] = useState< { time: Date; content: string } | undefined @@ -110,8 +114,8 @@ export function DeveloperPage({ }: Props): VNode { useEffect(() => { return api.listener.onUpdateNotification(listenAllEvents, (ev) => { - console.log("event", ev) - return hook?.retry() + console.log("event", ev); + return hook?.retry(); }); }); @@ -275,7 +279,6 @@ export function DeveloperPage({ }: Props): VNode { })} /> - <SubTitle> <i18n.Translate>Exchange Entries</i18n.Translate> </SubTitle> @@ -336,19 +339,31 @@ export function DeveloperPage({ }: Props): VNode { ); } } - const uri = !e.masterPub ? undefined : stringifyWithdrawExchange({ - exchangeBaseUrl: e.exchangeBaseUrl, - exchangePub: e.masterPub, - }); + const uri = !e.masterPub + ? undefined + : stringifyWithdrawExchange({ + exchangeBaseUrl: e.exchangeBaseUrl, + }); return ( <tr key={idx}> <td> <a href={!uri ? undefined : Pages.defaultCta({ uri })}> - {e.scopeInfo ? `${e.scopeInfo.currency} (${e.scopeInfo.type === ScopeType.Global ? "global" : "regional"})` : e.currency} + {e.scopeInfo + ? `${e.scopeInfo.currency} (${ + e.scopeInfo.type === ScopeType.Global + ? "global" + : "regional" + })` + : e.currency} </a> </td> <td> - <a href={new URL(`/keys`, e.exchangeBaseUrl).href} target="_blank">{e.exchangeBaseUrl}</a> + <a + href={new URL(`/keys`, e.exchangeBaseUrl).href} + target="_blank" + > + {e.exchangeBaseUrl} + </a> </td> <td> {e.exchangeEntryStatus} / {e.exchangeUpdateStatus} @@ -359,10 +374,10 @@ export function DeveloperPage({ }: Props): VNode { <td> {e.lastUpdateTimestamp ? AbsoluteTime.toIsoString( - AbsoluteTime.fromPreciseTimestamp( - e.lastUpdateTimestamp, - ), - ) + AbsoluteTime.fromPreciseTimestamp( + e.lastUpdateTimestamp, + ), + ) : "never"} </td> <td> @@ -381,31 +396,25 @@ export function DeveloperPage({ }: Props): VNode { </button> <button onClick={() => { - api.wallet.call( - WalletApiOperation.DeleteExchange, - { - exchangeBaseUrl: e.exchangeBaseUrl, - }, - ); + api.wallet.call(WalletApiOperation.DeleteExchange, { + exchangeBaseUrl: e.exchangeBaseUrl, + }); }} > Delete </button> <button onClick={() => { - api.wallet.call( - WalletApiOperation.DeleteExchange, - { - exchangeBaseUrl: e.exchangeBaseUrl, - purge: true, - }, - ); + api.wallet.call(WalletApiOperation.DeleteExchange, { + exchangeBaseUrl: e.exchangeBaseUrl, + purge: true, + }); }} > Purge </button> - {e.scopeInfo && e.masterPub && e.currency ? - (e.scopeInfo.type === ScopeType.Global ? + {e.scopeInfo && e.masterPub && e.currency ? ( + e.scopeInfo.type === ScopeType.Global ? ( <button onClick={() => { api.wallet.call( @@ -418,30 +427,27 @@ export function DeveloperPage({ }: Props): VNode { ); }} > - Make regional </button> - : e.scopeInfo.type === ScopeType.Auditor ? - undefined - - : e.scopeInfo.type === ScopeType.Exchange ? - <button - onClick={() => { - api.wallet.call( - WalletApiOperation.AddGlobalCurrencyExchange, - { - exchangeBaseUrl: e.exchangeBaseUrl, - currency: e.currency!, - exchangeMasterPub: e.masterPub!, - }, - ); - }} - > - - Make global - </button> - : undefined) : undefined - } + ) : e.scopeInfo.type === + ScopeType.Auditor ? undefined : e.scopeInfo.type === + ScopeType.Exchange ? ( + <button + onClick={() => { + api.wallet.call( + WalletApiOperation.AddGlobalCurrencyExchange, + { + exchangeBaseUrl: e.exchangeBaseUrl, + currency: e.currency!, + exchangeMasterPub: e.masterPub!, + }, + ); + }} + > + Make global + </button> + ) : undefined + ) : undefined} <button onClick={() => { api.wallet.call( @@ -469,7 +475,6 @@ export function DeveloperPage({ }: Props): VNode { </LinkPrimary> </div> - <Paper style={{ padding: 10, margin: 10 }}> <h3>Logging</h3> <div> diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 195efecd4..4394a982f 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -46,6 +46,7 @@ import { MessageFromFrontendWallet, } from "./platform/api.js"; import { platform } from "./platform/foreground.js"; +import { WalletActivityTrack } from "./wxBackend.js"; /** * @@ -74,8 +75,10 @@ export interface BackgroundOperations { response: void; }; getNotifications: { - request: void; - response: WalletEvent[]; + request: { + filter: string; + }; + response: WalletActivityTrack[]; }; clearNotifications: { request: void; diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index bf70f68df..5fa255f5d 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -25,7 +25,6 @@ */ import { AbsoluteTime, - BalanceFlag, LogLevel, Logger, NotificationType, @@ -34,14 +33,13 @@ import { TalerError, TalerErrorCode, TalerErrorDetail, - TransactionMajorState, TransactionMinorState, WalletNotification, getErrorDetailFromException, makeErrorDetail, openPromise, setGlobalLogLevelFromString, - setLogLevelFromString, + setLogLevelFromString } from "@gnu-taler/taler-util"; import { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; import { @@ -55,11 +53,11 @@ import { exportDb, importDb, } from "@gnu-taler/taler-wallet-core"; +import { BrowserFetchHttpLib } from "@gnu-taler/web-util/browser"; import { MessageFromFrontend, MessageResponse } from "./platform/api.js"; import { platform } from "./platform/background.js"; import { ExtensionOperations } from "./taler-wallet-interaction-loader.js"; -import { BackgroundOperations, WalletEvent } from "./wxApi.js"; -import { BrowserFetchHttpLib } from "@gnu-taler/web-util/browser"; +import { BackgroundOperations } from "./wxApi.js"; /** * Currently active wallet instance. Might be unloaded and @@ -92,14 +90,162 @@ async function resetDb(): Promise<void> { await reinitWallet(); } +export type WalletActivityTrack = { + id: number; + events: (WalletNotification & {when: AbsoluteTime})[]; + start: AbsoluteTime; + type: NotificationType; + end: AbsoluteTime; + groupId: string; +}; + +let counter = 0; +function getUniqueId(): number { + return counter++; +} + //FIXME: maybe circular buffer -const notifications: WalletEvent[] = []; -async function getNotifications(): Promise<WalletEvent[]> { - return notifications; +const activity: WalletActivityTrack[] = []; + +function addNewWalletActivityNotification(list: WalletActivityTrack[], n: WalletNotification) { + const start = AbsoluteTime.now(); + const ev = {...n, when:start}; + switch (n.type) { + case NotificationType.BalanceChange: { + const groupId = `${n.type}:${n.hintTransactionId}`; + const found = list.find((a)=>a.groupId === groupId) + if (found) { + found.end = start; + found.events.unshift(ev) + return; + } + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + case NotificationType.BackupOperationError: { + const groupId = ""; + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + case NotificationType.TransactionStateTransition: { + const groupId = `${n.type}:${n.transactionId}`; + const found = list.find((a)=>a.groupId === groupId) + if (found) { + found.end = start; + found.events.unshift(ev) + return; + } + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + case NotificationType.WithdrawalOperationTransition: { + return; + } + case NotificationType.ExchangeStateTransition: { + const groupId = `${n.type}:${n.exchangeBaseUrl}`; + const found = list.find((a)=>a.groupId === groupId) + if (found) { + found.end = start; + found.events.unshift(ev) + return; + } + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + case NotificationType.Idle: { + const groupId = ""; + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + case NotificationType.TaskObservabilityEvent: { + const groupId = `${n.type}:${n.taskId}`; + const found = list.find((a)=>a.groupId === groupId) + if (found) { + found.end = start; + found.events.unshift(ev) + return; + } + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + case NotificationType.RequestObservabilityEvent: { + const groupId = `${n.type}:${n.operation}:${n.requestId}`; + const found = list.find((a)=>a.groupId === groupId) + if (found) { + found.end = start; + found.events.unshift(ev) + return; + } + list.push({ + id: getUniqueId(), + type: n.type, + start, + end: AbsoluteTime.never(), + events: [ev], + groupId, + }); + return; + } + } +} + +async function getNotifications({ + filter, +}: { + filter: string; +}): Promise<WalletActivityTrack[]> { + if (!filter) return activity; + + const rg = new RegExp(`.*${filter}.*`); + return activity.filter((event) => { + return rg.test(event.groupId.toLowerCase()); + }); } async function clearNotifications(): Promise<void> { - notifications.splice(0, notifications.length); + activity.splice(0, activity.length); } async function runGarbageCollector(): Promise<void> { @@ -275,7 +421,7 @@ async function dispatch< async function reinitWallet(): Promise<void> { if (currentWallet) { - currentWallet.stop(); + await currentWallet.client.call(WalletApiOperation.Shutdown, {}); currentWallet = undefined; } currentDatabase = undefined; @@ -327,10 +473,7 @@ async function reinitWallet(): Promise<void> { } wallet.addNotificationListener((message) => { if (settings.showWalletActivity) { - notifications.push({ - notification: message, - when: AbsoluteTime.now(), - }); + addNewWalletActivityNotification(activity, message); } processWalletNotification(message); @@ -394,7 +537,7 @@ async function updateIconBasedOnBalance() { let showAlert = false; for (const b of balance.balances) { if (b.flags.length > 0) { - console.log("b.flags", JSON.stringify(b.flags)) + console.log("b.flags", JSON.stringify(b.flags)); showAlert = true; break; } |