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:
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) {