taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit bf45f9633deb51aeb5ff813e42501d6a804403e8
parent 5b8d82b462d878087b8fbac4beabb0d813723015
Author: Florian Dold <dold@taler.net>
Date:   Sun, 13 Sep 2026 17:16:19 +0200

wallet web UI: explain demo and story labels to translators

Describe fictional payment scenarios, component examples and developer
story labels in source comments.

Diffstat:
Mpackages/wallet-webui/src/screens/StorybookScreen.tsx | 194+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mpackages/wallet-webui/src/stories/catalog.ts | 218++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mpackages/wallet-webui/src/testing/demo-wallet.ts | 44++++++++++++++++++++++++++++----------------
3 files changed, 319 insertions(+), 137 deletions(-)

diff --git a/packages/wallet-webui/src/screens/StorybookScreen.tsx b/packages/wallet-webui/src/screens/StorybookScreen.tsx @@ -172,8 +172,12 @@ export function StorybookScreen(props: { <div class="mx-auto flex max-w-[100rem] items-center gap-5 px-5 py-4"> <WalletBrand /> <div class="border-l border-outlineVariant pl-5"> - <p class="font-semibold">{i18n.str`UI catalogue`}</p> - <p class="text-sm text-secondary">{i18n.str`Fixture-only wallet screens`}</p> + <p class="font-semibold">{ + /* Translators: This text belongs to the developer screen catalogue and describes a preview, not a live financial operation. */ i18n.str`UI catalogue` + }</p> + <p class="text-sm text-secondary">{ + /* Translators: This text belongs to the developer screen catalogue and describes a preview, not a live financial operation. */ i18n.str`Fixture-only wallet screens` + }</p> </div> </div> </header> @@ -181,17 +185,23 @@ export function StorybookScreen(props: { <aside class="min-w-0 overflow-hidden border-b border-outlineVariant bg-surface p-4 md:min-h-[calc(100vh-5rem)] md:border-b-0 md:border-r md:p-6"> <div class="min-w-0 md:sticky md:top-6"> <label class="mb-5 block"> - <span class="mb-1 block text-sm font-semibold">{i18n.str`Find a story`}</span> + <span class="mb-1 block text-sm font-semibold">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Find a story` + }</span> <input type="search" value={filter} onInput={(event) => setFilter(event.currentTarget.value)} - placeholder={i18n.str`Payment, withdrawal…`} + placeholder={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Payment, withdrawal…` + } class="w-full rounded-xl border border-outline bg-surface px-3 py-2" /> </label> <nav - aria-label={i18n.str`Component stories`} + aria-label={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Component stories` + } class="flex w-full min-w-0 max-w-full gap-3 overflow-x-auto md:block md:space-y-6" > {visibleGroups.map((group) => ( @@ -223,10 +233,9 @@ export function StorybookScreen(props: { ))} </nav> {visibleGroups.length === 0 && ( - <p - role="status" - class="mt-4 text-sm text-secondary" - >{i18n.str`No matching stories.`}</p> + <p role="status" class="mt-4 text-sm text-secondary">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`No matching stories.` + }</p> )} </div> </aside> @@ -234,14 +243,18 @@ export function StorybookScreen(props: { <div class="mb-8 flex flex-wrap items-end justify-between gap-3"> <div> <h1 class="text-2xl font-bold">{i18n.str`Component stories`}</h1> - <p class="text-secondary">{i18n.str`Isolated Taler wallet visual states without wallet navigation or live host state`}</p> + <p class="text-secondary">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Isolated Taler wallet visual states without wallet navigation or live host state` + }</p> </div> {activeStory && ( <button type="button" onClick={() => props.onSelect?.("")} class="rounded-full border border-outline px-4 py-2 text-sm font-semibold hover:bg-secondaryContainer" - >{i18n.str`Show all`}</button> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Show all` + }</button> )} </div> <StoryCatalogue activeStory={activeStory}> @@ -254,9 +267,15 @@ export function StorybookScreen(props: { <div class="flex flex-wrap gap-2"> {/* Translators: The following four labels name example button visual tones and states. */} <Button>{i18n.str`Primary`}</Button> - <Button tone="secondary">{i18n.str`Secondary`}</Button> - <Button tone="danger">{i18n.str`Danger`}</Button> - <Button disabled>{i18n.str`Disabled`}</Button> + <Button tone="secondary">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Secondary` + }</Button> + <Button tone="danger">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Danger` + }</Button> + <Button disabled>{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Disabled` + }</Button> </div> </Card> </section> @@ -269,7 +288,9 @@ export function StorybookScreen(props: { <Card class="flex min-h-52 flex-col justify-between"> <div> <h3 class="font-semibold">{i18n.str`Welcome to Taler Wallet!`}</h3> - <p class="mt-3">{i18n.str`To make your first payment, withdraw digital cash.`}</p> + <p class="mt-3">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`To make your first payment, withdraw digital cash.` + }</p> </div> <Button>{i18n.str`Withdraw digital cash`}</Button> </Card> @@ -280,10 +301,9 @@ export function StorybookScreen(props: { </div> </section> <section aria-labelledby="balances-story"> - <h2 - id="balances-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`Penpot populated balances`}</h2> + <h2 id="balances-story" class="mb-3 text-xl font-semibold">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Penpot populated balances` + }</h2> <BalanceScreen loading={false} selectedScopeId={"exchange:CHF:https://exchange.example/"} @@ -379,7 +399,7 @@ export function StorybookScreen(props: { pendingTransactions={[ { id: "txn:withdrawal:2", - title: i18n.str`Bank transfer`, + title: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Bank transfer`, typeLabel: i18n.str`Withdrawal`, icon: "↓", amount: "CHF:5", @@ -429,7 +449,7 @@ export function StorybookScreen(props: { <BalanceScreen loading={false} error={localError( - i18n.str`The wallet host is temporarily unavailable.`, + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`The wallet host is temporarily unavailable.`, )} onWithdraw={() => {}} onGetDemoCash={() => {}} @@ -458,7 +478,9 @@ export function StorybookScreen(props: { <h2 id="request-stalled-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Stalled request with retryable error`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Stalled request with retryable error` + }</h2> <RequestProgressPanel embedded progress={{ @@ -531,7 +553,9 @@ export function StorybookScreen(props: { <ActionScreen state="ready" actionKind={`withdrawal-transfer-result`} - source={i18n.str`bank app`} + source={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`bank app` + } detail={i18n.str`Your bank reports that the transfer succeeded. The wallet will verify the withdrawal independently.`} continueLabel={i18n.str`View withdrawal`} onContinue={() => {}} @@ -539,13 +563,14 @@ export function StorybookScreen(props: { /> </section> <section aria-labelledby="scanner-error-story"> - <h2 - id="scanner-error-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`QR scanner unavailable`}</h2> + <h2 id="scanner-error-story" class="mb-3 text-xl font-semibold">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`QR scanner unavailable` + }</h2> <QrScannerScreen videoRef={() => {}} - error={i18n.str`Camera access is unavailable in this browser.`} + error={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Camera access is unavailable in this browser.` + } onEnterLink={() => {}} onCancel={() => {}} /> @@ -560,7 +585,9 @@ export function StorybookScreen(props: { merchantName={"BFH Mensa"} merchantWebsite={"https://merchant.example/"} merchantAddress={"Höheweg 80, Biel, Switzerland"} - summary={i18n.str`Digital article access and some fruits`} + summary={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Digital article access and some fruits` + } order={{ summary: "Digital article access and some fruits", products: [ @@ -645,10 +672,9 @@ export function StorybookScreen(props: { /> </section> <section aria-labelledby="payment-done-story"> - <h2 - id="payment-done-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`Completed point-of-sale payment`}</h2> + <h2 id="payment-done-story" class="mb-3 text-xl font-semibold">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Completed point-of-sale payment` + }</h2> <PaymentScreen state="done" merchantName={"City Museum"} @@ -679,7 +705,7 @@ export function StorybookScreen(props: { claimUrl: "https://merchant.example/orders/ORDER-2026-08-13/claim", }, - i18n.str`The payment could not be prepared.`, + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`The payment could not be prepared.`, )} onSelectChoice={() => {}} onConfirm={() => {}} @@ -693,14 +719,18 @@ export function StorybookScreen(props: { <h2 id="payment-template-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Fixed-order payment template`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Fixed-order payment template` + }</h2> <PaymentTemplateScreen loading={false} working={false} currencies={["CHF", "EUR"]} initialCurrency={"CHF"} initialAmount={"5"} - initialSummary={i18n.str`Museum admission`} + initialSummary={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Museum admission` + } amountEditable currencyEditable summaryEditable @@ -840,7 +870,9 @@ export function StorybookScreen(props: { <h2 id="settings-storage-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Settings during SQLite conversion`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Settings during SQLite conversion` + }</h2> <SettingsScreen languagePreference="auto" languageOptions={[{ value: "en", label: "English" }]} @@ -859,7 +891,9 @@ export function StorybookScreen(props: { }} persistenceBusy={false} busy={false} - message={i18n.str`Diagnostics saved.`} + message={ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Diagnostics saved.` + } sqliteAvailable storageBusy storageStage={`verifying`} @@ -891,7 +925,9 @@ export function StorybookScreen(props: { <h2 id="import-recovery-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Database import recovery`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Database import recovery` + }</h2> <ImportRecoveryScreen stage={`failure`} filename={"wallet-backup.json"} @@ -900,7 +936,7 @@ export function StorybookScreen(props: { collectionCount={24} recordCount={842} error={localError( - i18n.str`The imported database failed wallet-core verification.`, + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`The imported database failed wallet-core verification.`, )} recoveryAvailable onImport={() => {}} @@ -913,9 +949,13 @@ export function StorybookScreen(props: { <h2 id="storage-recovery-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Authoritative storage recovery`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Authoritative storage recovery` + }</h2> <RecoveryScreen - reason={i18n.str`The active SQLite generation failed its reopen verification after activation.`} + reason={ + /* Translators: A storage generation is a complete version of the local wallet database, not a separate account. */ i18n.str`The active SQLite generation failed its reopen verification after activation.` + } backend="sqlite" generation={3} stage={`verifying`} @@ -928,9 +968,13 @@ export function StorybookScreen(props: { <h2 id="storage-control-loss-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Storage control metadata loss`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Storage control metadata loss` + }</h2> <RecoveryScreen - reason={i18n.str`Wallet storage control metadata is missing. Found two physical wallet generations; none was opened automatically.`} + reason={ + /* Translators: A storage generation is a complete version of the local wallet database, not a separate account. */ i18n.str`Wallet storage control metadata is missing. Found two physical wallet generations; none was opened automatically.` + } recoveryCandidates={[ { backend: "indexeddb", generation: 3 }, { backend: "sqlite", generation: 4 }, @@ -967,10 +1011,9 @@ export function StorybookScreen(props: { /> </section> <section aria-labelledby="deposit-error-story"> - <h2 - id="deposit-error-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`Deposit error returned by wallet-core`}</h2> + <h2 id="deposit-error-story" class="mb-3 text-xl font-semibold">{ + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Deposit error returned by wallet-core` + }</h2> <DepositScreen currency="CHF" loading={false} @@ -1026,10 +1069,9 @@ export function StorybookScreen(props: { /> </section> <section aria-labelledby="peer-error-story"> - <h2 - id="peer-error-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`Peer payment error returned by wallet-core`}</h2> + <h2 id="peer-error-story" class="mb-3 text-xl font-semibold">{ + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Peer payment error returned by wallet-core` + }</h2> <PeerReceiveScreen kind="receive" loading={false} @@ -1047,10 +1089,9 @@ export function StorybookScreen(props: { /> </section> <section aria-labelledby="peer-receive-story"> - <h2 - id="peer-receive-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`Peer payment acceptance`}</h2> + <h2 id="peer-receive-story" class="mb-3 text-xl font-semibold">{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Peer payment acceptance` + }</h2> <PeerReceiveScreen kind="pay" loading={false} @@ -1171,9 +1212,13 @@ export function StorybookScreen(props: { /> ) : ( <Card> - <p class="text-secondary">{i18n.str`This modal fixture opens only when selected.`}</p> + <p class="text-secondary">{ + /* Translators: This text belongs to the developer screen catalogue and describes a preview, not a live financial operation. */ i18n.str`This modal fixture opens only when selected.` + }</p> <Button onClick={() => props.onSelect?.("exchange-delete")}> - {i18n.str`View force-removal dialog`} + { + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`View force-removal dialog` + } </Button> </Card> )} @@ -1324,7 +1369,7 @@ Example exchange terms.`} id: "abort", label: i18n.str`Abort`, dangerous: true, - confirmation: i18n.str`Abort this transaction?`, + confirmation: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Abort this transaction?`, }, ], }} @@ -1337,7 +1382,9 @@ Example exchange terms.`} <h2 id="transaction-payment-order-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Payment transaction order`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Payment transaction order` + }</h2> <TransactionDetailScreen loading={false} transaction={{ @@ -1404,10 +1451,9 @@ Example exchange terms.`} /> </section> <section aria-labelledby="withdraw-error-story"> - <h2 - id="withdraw-error-story" - class="mb-3 text-xl font-semibold" - >{i18n.str`Withdrawal error returned by wallet-core`}</h2> + <h2 id="withdraw-error-story" class="mb-3 text-xl font-semibold">{ + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Withdrawal error returned by wallet-core` + }</h2> <WithdrawalScreen providers={[]} loading={false} @@ -1456,7 +1502,9 @@ Example exchange terms.`} <h2 id="bank-confirmation-story" class="mb-3 text-xl font-semibold" - >{i18n.str`Integrated bank confirmation`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Integrated bank confirmation` + }</h2> <BankConfirmationScreen amount={"CHF:10"} exchange={"https://taler-ops.example/"} @@ -1513,7 +1561,9 @@ Example exchange terms.`} <h2 id="extension-popup-story" class="mb-3 text-xl font-semibold" - >{i18n.str`WebExtension popup`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`WebExtension popup` + }</h2> <PopupStoryFrame balances={popupBalances.slice(0, 1)} hasCurrentPageAction @@ -1523,21 +1573,27 @@ Example exchange terms.`} <h2 id="extension-popup-five-currencies-story" class="mb-3 text-xl font-semibold" - >{i18n.str`WebExtension popup — five currencies`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`WebExtension popup — five currencies` + }</h2> <PopupStoryFrame balances={popupBalances.slice(0, 5)} /> </section> <section aria-labelledby="extension-popup-ten-currencies-story"> <h2 id="extension-popup-ten-currencies-story" class="mb-3 text-xl font-semibold" - >{i18n.str`WebExtension popup — ten currencies`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`WebExtension popup — ten currencies` + }</h2> <PopupStoryFrame balances={popupBalances} hasCurrentPageAction /> </section> <section aria-labelledby="extension-popup-error-story"> <h2 id="extension-popup-error-story" class="mb-3 text-xl font-semibold" - >{i18n.str`WebExtension popup — wallet unavailable`}</h2> + >{ + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`WebExtension popup — wallet unavailable` + }</h2> <PopupStoryFrame error={localError( i18n.str`The wallet is temporarily unavailable. Open the full wallet to reconnect.`, diff --git a/packages/wallet-webui/src/stories/catalog.ts b/packages/wallet-webui/src/stories/catalog.ts @@ -29,39 +29,108 @@ function makeStoryGroups(): readonly StoryGroup[] { { label: i18n.str`Foundations`, stories: [ - ["buttons", i18n.str`Buttons`], - ["empty-wallet", i18n.str`Empty wallet`], - ["balances", i18n.str`Populated balances`], - ["balances-loading", i18n.str`Balances loading`], - ["balances-error", i18n.str`Balances error`], - ["request-delayed", i18n.str`Request delayed`], - ["request-stalled", i18n.str`Request stalled`], - ["request-cancelling", i18n.str`Request cancelling`], - ["request-retrying", i18n.str`Request retrying`], + [ + "buttons", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Buttons`, + ], + [ + "empty-wallet", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Empty wallet`, + ], + [ + "balances", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Populated balances`, + ], + [ + "balances-loading", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Balances loading`, + ], + [ + "balances-error", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Balances error`, + ], + [ + "request-delayed", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Request delayed`, + ], + [ + "request-stalled", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Request stalled`, + ], + [ + "request-cancelling", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Request cancelling`, + ], + [ + "request-retrying", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Request retrying`, + ], ], }, { label: i18n.str`Actions`, stories: [ - ["uri-entry", i18n.str`Taler link entry`], - ["bank-return", i18n.str`Withdrawal bank return`], - ["scanner-error", i18n.str`Scanner unavailable`], - ["payment", i18n.str`Payment review`], + [ + "uri-entry", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Taler link entry`, + ], + [ + "bank-return", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Withdrawal bank return`, + ], + [ + "scanner-error", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Scanner unavailable`, + ], + [ + "payment", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Payment review`, + ], ["payment-done", i18n.str`Payment complete`], - ["payment-error", i18n.str`Payment error`], + [ + "payment-error", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Payment error`, + ], ["payment-template", i18n.str`Payment template`], // Translators: Developer preview story title for the deposit flow. ["deposit", i18n.ctx("story title")`Deposit`], ["deposit-error", i18n.str`Deposit error`], - ["peer-create", i18n.str`Create peer transfer`], - ["peer-error", i18n.str`Peer transfer error`], - ["peer-receive", i18n.str`Receive peer transfer`], - ["peer-share", i18n.str`Share peer transfer`], - ["withdraw-review", i18n.str`Withdrawal review`], - ["withdraw-error", i18n.str`Withdrawal error`], - ["integrated-withdrawal", i18n.str`Integrated withdrawal`], - ["bank-confirmation", i18n.str`Bank confirmation`], - ["wire", i18n.str`Wire instructions`], + [ + "peer-create", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Create peer transfer`, + ], + [ + "peer-error", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Peer transfer error`, + ], + [ + "peer-receive", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Receive peer transfer`, + ], + [ + "peer-share", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Share peer transfer`, + ], + [ + "withdraw-review", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Withdrawal review`, + ], + [ + "withdraw-error", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Withdrawal error`, + ], + [ + "integrated-withdrawal", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Integrated withdrawal`, + ], + [ + "bank-confirmation", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Bank confirmation`, + ], + [ + "wire", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Wire instructions`, + ], ], }, { @@ -69,43 +138,67 @@ function makeStoryGroups(): readonly StoryGroup[] { stories: [ ["history", i18n.str`History`], ["transaction-detail", i18n.str`Transaction details`], - ["transaction-payment-order", i18n.str`Payment order details`], + [ + "transaction-payment-order", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Payment order details`, + ], ["discounts", i18n.str`Discounts`], ["passes", i18n.str`Passes`], ["shopping", i18n.str`Shop discovery`], - ["donau-settings", i18n.str`Donation settings`], + [ + "donau-settings", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Donation settings`, + ], ["donau-statement", i18n.str`Donation statement`], - ["settings-storage", i18n.str`Settings and storage`], - ["import-recovery", i18n.str`Import recovery`], - ["storage-recovery", i18n.str`Storage recovery`], - ["storage-control-loss", i18n.str`Storage control loss`], + [ + "settings-storage", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Settings and storage`, + ], + [ + "import-recovery", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Import recovery`, + ], + [ + "storage-recovery", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Storage recovery`, + ], + [ + "storage-control-loss", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Storage control loss`, + ], ], }, { - label: i18n.str`Management`, + label: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Management`, stories: [ ["exchange-list", i18n.str`Exchanges`], - ["exchange-key", i18n.str`Exchange key change`], + [ + "exchange-key", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Exchange key change`, + ], ["exchange-delete", i18n.str`Force-remove exchange`], ["terms", i18n.str`Terms of service`], ["accounts", i18n.str`Bank accounts`], ], }, { - label: i18n.str`Platform`, + label: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Platform`, stories: [ - ["extension-popup", i18n.str`Extension popup`], + [ + "extension-popup", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Extension popup`, + ], [ "extension-popup-five-currencies", - i18n.str`Extension popup — five currencies`, + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Extension popup — five currencies`, ], [ "extension-popup-ten-currencies", - i18n.str`Extension popup — ten currencies`, + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Extension popup — ten currencies`, ], [ "extension-popup-error", - i18n.str`Extension popup — wallet unavailable`, + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Extension popup — wallet unavailable`, ], ], }, @@ -121,13 +214,34 @@ export const storyIds: ReadonlySet<string> = new Set( function makeReviewedStories(): readonly StoryDefinition[] { return [ ...[ - ["request-delayed", i18n.str`Delayed request`], - ["request-stalled", i18n.str`Stalled request`], - ["request-cancelling", i18n.str`Cancelling request`], - ["request-retrying", i18n.str`Retrying request`], - ["deposit-error", i18n.str`Wallet-core deposit error`], - ["peer-error", i18n.str`Wallet-core peer-payment error`], - ["withdraw-error", i18n.str`Wallet-core withdrawal error`], + [ + "request-delayed", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Delayed request`, + ], + [ + "request-stalled", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Stalled request`, + ], + [ + "request-cancelling", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Cancelling request`, + ], + [ + "request-retrying", + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Retrying request`, + ], + [ + "deposit-error", + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Wallet-core deposit error`, + ], + [ + "peer-error", + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Wallet-core peer-payment error`, + ], + [ + "withdraw-error", + /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Wallet-core withdrawal error`, + ], ].map(([id, title]) => ({ id, title, @@ -136,7 +250,7 @@ function makeReviewedStories(): readonly StoryDefinition[] { })), { id: "empty-wallet", - title: i18n.str`Empty balances`, + title: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Empty balances`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, @@ -148,7 +262,7 @@ function makeReviewedStories(): readonly StoryDefinition[] { }, { id: "bank-return", - title: i18n.str`Action confirmation`, + title: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Action confirmation`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, @@ -160,7 +274,7 @@ function makeReviewedStories(): readonly StoryDefinition[] { }, { id: "storage-control-loss", - title: i18n.str`Storage generation selection`, + title: /* Translators: A storage generation is a complete version of the local wallet database, not a separate account. */ i18n.str`Storage generation selection`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, @@ -172,25 +286,25 @@ function makeReviewedStories(): readonly StoryDefinition[] { }, { id: "donau-statement", - title: i18n.str`DD 68 donation receipts`, + title: /* Translators: DD and LSD numbers refer to Taler design/specification documents; preserve these identifiers. */ i18n.str`DD 68 donation receipts`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, { id: "wire", - title: i18n.str`DD 88 wire instructions`, + title: /* Translators: DD and LSD numbers refer to Taler design/specification documents; preserve these identifiers. */ i18n.str`DD 88 wire instructions`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, { id: "payment-done", - title: i18n.str`Completed POS payment`, + title: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Completed POS payment`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, { id: "payment-error", - title: i18n.str`Wallet-core payment error`, + title: /* Translators: wallet-core is the background wallet engine; keep the component name unchanged. */ i18n.str`Wallet-core payment error`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, @@ -220,13 +334,13 @@ function makeReviewedStories(): readonly StoryDefinition[] { }, { id: "exchange-delete", - title: i18n.str`Exchange removal warning`, + title: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Exchange removal warning`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, { id: "accounts", - title: i18n.str`Bank account disclosure`, + title: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Bank account disclosure`, themes: ["light", "dark"], widths: ["narrow", "desktop"], }, diff --git a/packages/wallet-webui/src/testing/demo-wallet.ts b/packages/wallet-webui/src/testing/demo-wallet.ts @@ -165,7 +165,7 @@ function paymentTerms() { order_id: "demo-order-2026", merchant_base_url: "https://merchant.demo.invalid/", merchant: { - name: i18n.str`City Museum`, + name: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`City Museum`, website: "https://merchant.demo.invalid/", address: { country: "CH", @@ -183,7 +183,7 @@ function paymentTerms() { }, { product_id: "guide", - description: i18n.str`Digital exhibition guide`, + description: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Digital exhibition guide`, quantity: 1, unit: "item", price: "CHF:1.10", @@ -314,12 +314,12 @@ function seedState(nowMs: number): DemoState { txActions: [TransactionAction.Retry, TransactionAction.Delete], exchangeBaseUrl, info: { - summary: i18n.str`Dinner share`, + summary: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Dinner share`, expiration: timestamp(now + 86_400), }, error: { code: 1, - hint: i18n.str`The recipient did not claim this demo transfer.`, + hint: /* Translators: Claiming a peer transfer means receiving money from its share link; this is fictional demo data. */ i18n.str`The recipient did not claim this demo transfer.`, }, } as unknown as Transaction; return { @@ -369,7 +369,7 @@ function seedState(nowMs: number): DemoState { paytoUri: ibanPayto, kycCompleted: false, currencies: ["CHF"], - label: i18n.str`Everyday account`, + label: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Everyday account`, }, ], exchanges: [ @@ -469,7 +469,9 @@ export class DemoWalletConnection implements WalletConnection { hintTransactionId: "demo-reset", }); this.emitTransaction(this.state.transactions[0]); - this.notice(i18n.str`Demo data restored.`); + this.notice( + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Demo data restored.`, + ); } snapshot(): Readonly<DemoState> { @@ -746,7 +748,7 @@ export class DemoWalletConnection implements WalletConnection { talerUri: "taler://withdraw-exchange/exchange.demo.invalid/", currency: "CHF", currencySpec: { - name: i18n.str`Swiss franc`, + name: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Swiss franc`, numFractionalInputDigits: 2, numFractionalNormalDigits: 2, numFractionalTrailingZeroDigits: 2, @@ -1116,7 +1118,7 @@ export class DemoWalletConnection implements WalletConnection { ), exchangeBaseUrl, info: { - summary: i18n.str`Shared lunch`, + summary: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Shared lunch`, expiration: timestamp(Math.floor(this.now() / 1000) + 86_400), }, } as unknown as Transaction; @@ -1230,8 +1232,8 @@ export class DemoWalletConnection implements WalletConnection { tokenIssuePubHash: "demo-issue", merchantBaseUrl: "https://merchant.demo.invalid/", merchantInfo: { name: i18n.str`City Museum` }, - name: i18n.str`Member price`, - description: i18n.str`Reduced admission for wallet holders`, + name: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Member price`, + description: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Reduced admission for wallet holders`, validityStart: timestamp(Math.floor(this.now() / 1000) - 86_400), validityEnd: timestamp(Math.floor(this.now() / 1000) + 2_592_000), tokensAvailable: 3, @@ -1246,8 +1248,8 @@ export class DemoWalletConnection implements WalletConnection { tokenIssuePubHash: "demo-pass-issue", merchantBaseUrl: "https://merchant.demo.invalid/", merchantInfo: { name: i18n.str`City Museum` }, - name: i18n.str`Museum pass`, - description: i18n.str`Admission during the demo period`, + name: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Museum pass`, + description: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Admission during the demo period`, validityStart: timestamp(Math.floor(this.now() / 1000) - 86_400), validityEnd: timestamp(Math.floor(this.now() / 1000) + 2_592_000), }, @@ -1326,10 +1328,14 @@ export class DemoWalletConnection implements WalletConnection { transaction.transactionId, ); this.emitTransaction(transaction, oldMajor); - this.notice(i18n.str`The demo bank confirmed the withdrawal.`); + this.notice( + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`The demo bank confirmed the withdrawal.`, + ); return; } - this.notice(i18n.str`External navigation is simulated in demo mode.`); + this.notice( + /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`External navigation is simulated in demo mode.`, + ); } } @@ -1388,8 +1394,14 @@ export function createDemoServices( }; const demo: DemoModeController = { scenarios: [ - { id: "payment", label: i18n.str`Sample payment` }, - { id: "withdrawal", label: i18n.str`Bank withdrawal` }, + { + id: "payment", + label: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Sample payment`, + }, + { + id: "withdrawal", + label: /* Translators: Text for a fictional demo or developer screen preview; not a live wallet action or real merchant data. */ i18n.str`Bank withdrawal`, + }, { id: "peer-receive", label: i18n.str`Receive money` }, ], async startScenario(id) {