gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (e38a96702 -> 2609c7359)


From: gnunet
Subject: [taler-wallet-core] branch master updated (e38a96702 -> 2609c7359)
Date: Fri, 12 Jul 2024 18:30:44 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from e38a96702 -reverse arrow
     new 4f3f4d303 fixing i18n strings
     new d52c21396 fixing i18n strings
     new c9c6425f9 remove some any types
     new 2609c7359 type guard: promise catch wont use any type

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/pages/regional/ConversionConfig.tsx        |  40 +--
 .../src/type-override.d.ts}                        |  29 +-
 packages/bank-ui/src/utils.ts                      |   2 +-
 packages/merchant-backoffice-ui/dev.mjs            |   2 +-
 packages/merchant-backoffice-ui/src/Routing.tsx    |  45 +---
 .../src/components/ErrorLoadingMerchant.tsx        | 243 +++++++++++------
 .../src/components/form/InputArray.tsx             |  12 +-
 .../src/components/form/InputDate.tsx              |  10 +-
 .../src/components/form/InputDuration.tsx          |  10 +-
 .../src/components/form/InputImage.tsx             |   4 +-
 .../src/components/form/InputPaytoForm.tsx         |  47 ++--
 .../src/components/form/InputSearchOnList.tsx      |   2 +-
 .../src/components/form/InputSelector.tsx          |  16 +-
 .../src/components/form/InputStock.tsx             |  14 +-
 .../src/components/form/InputTaxes.tsx             |  10 +-
 .../src/components/form/JumpToElementById.tsx      |  91 ++++---
 .../src/components/menu/SideBar.tsx                |  26 +-
 .../src/components/modal/index.tsx                 |  56 ++--
 .../components/product/InventoryProductForm.tsx    |   2 +-
 .../components/product/NonInventoryProductForm.tsx |  36 ++-
 .../src/components/product/ProductForm.tsx         |  32 +--
 .../src/components/product/ProductList.tsx         |  10 +-
 .../src/components/tokenfamily/TokenFamilyForm.tsx |  26 +-
 packages/merchant-backoffice-ui/src/hooks/async.ts |  10 +-
 .../merchant-backoffice-ui/src/hooks/preference.ts |   3 +
 .../src/paths/admin/create/CreatePage.tsx          |  67 ++---
 .../src/paths/admin/create/index.tsx               |  24 +-
 .../src/paths/admin/list/TableActive.tsx           |   2 +-
 .../paths/instance/accounts/create/CreatePage.tsx  |  25 +-
 .../src/paths/instance/accounts/create/index.tsx   |   6 +-
 .../src/paths/instance/accounts/list/Table.tsx     |  26 +-
 .../src/paths/instance/accounts/list/index.tsx     |   8 +-
 .../paths/instance/accounts/update/UpdatePage.tsx  |  26 +-
 .../src/paths/instance/accounts/update/index.tsx   |  22 +-
 .../paths/instance/orders/create/CreatePage.tsx    |  55 ++--
 .../src/paths/instance/orders/create/index.tsx     |   6 +-
 .../paths/instance/orders/details/DetailPage.tsx   |  48 ++--
 .../src/paths/instance/orders/details/index.tsx    |   8 +-
 .../src/paths/instance/orders/list/ListPage.tsx    |  18 +-
 .../src/paths/instance/orders/list/Table.tsx       |  36 +--
 .../src/paths/instance/orders/list/index.tsx       |  12 +-
 .../instance/otp_devices/create/CreatePage.tsx     |  37 ++-
 .../paths/instance/otp_devices/create/index.tsx    |   8 +-
 .../src/paths/instance/otp_devices/list/Table.tsx  |  12 +-
 .../src/paths/instance/otp_devices/list/index.tsx  |   8 +-
 .../instance/otp_devices/update/UpdatePage.tsx     |  50 ++--
 .../paths/instance/otp_devices/update/index.tsx    |   4 +-
 .../src/paths/instance/products/create/index.tsx   |   8 +-
 .../src/paths/instance/products/list/Table.tsx     |  30 +--
 .../src/paths/instance/products/list/index.tsx     |  16 +-
 .../src/paths/instance/products/update/index.tsx   |   6 +-
 .../paths/instance/templates/create/CreatePage.tsx |  22 +-
 .../src/paths/instance/templates/create/index.tsx  |   8 +-
 .../src/paths/instance/templates/list/Table.tsx    |  20 +-
 .../src/paths/instance/templates/list/index.tsx    |   6 +-
 .../src/paths/instance/templates/qr/QrPage.tsx     |  39 +--
 .../paths/instance/templates/update/UpdatePage.tsx |  18 +-
 .../src/paths/instance/templates/update/index.tsx  |   6 +-
 .../src/paths/instance/templates/use/index.tsx     |   6 +-
 .../src/paths/instance/token/DetailPage.tsx        |  25 +-
 .../src/paths/instance/token/index.tsx             |  20 +-
 .../paths/instance/tokenfamilies/create/index.tsx  |   8 +-
 .../paths/instance/tokenfamilies/list/Table.tsx    |   6 +-
 .../paths/instance/tokenfamilies/list/index.tsx    |   8 +-
 .../instance/tokenfamilies/update/UpdatePage.tsx   |  31 ++-
 .../paths/instance/tokenfamilies/update/index.tsx  |   6 +-
 .../paths/instance/transfers/create/CreatePage.tsx |  33 ++-
 .../src/paths/instance/transfers/create/index.tsx  |   8 +-
 .../src/paths/instance/transfers/list/ListPage.tsx |   8 +-
 .../src/paths/instance/transfers/list/Table.tsx    |  12 +-
 .../src/paths/instance/update/UpdatePage.tsx       |  29 +-
 .../src/paths/instance/update/index.tsx            |   4 +-
 .../paths/instance/webhooks/create/CreatePage.tsx  | 114 +++++---
 .../src/paths/instance/webhooks/create/index.tsx   |   8 +-
 .../src/paths/instance/webhooks/list/Table.tsx     |  21 +-
 .../src/paths/instance/webhooks/list/index.tsx     |   8 +-
 .../paths/instance/webhooks/update/UpdatePage.tsx  |  25 +-
 .../src/paths/instance/webhooks/update/index.tsx   |  12 +-
 .../src/paths/login/index.tsx                      |  11 +-
 .../src/paths/settings/index.tsx                   |  12 +-
 .../merchant-backoffice-ui/src/schemas/index.ts    | 293 ---------------------
 packages/merchant-backoffice-ui/src/settings.json  |   4 +
 .../src/type-override.d.ts}                        |  29 +-
 .../taler-util/src/http-client/officer-account.ts  |   4 +-
 packages/taler-util/src/http-common.ts             |  34 +--
 .../src/type-override.d.ts}                        |  29 +-
 86 files changed, 1002 insertions(+), 1271 deletions(-)
 copy packages/{auditor-backoffice-ui/src/scss/_footer.scss => 
bank-ui/src/type-override.d.ts} (61%)
 delete mode 100644 packages/merchant-backoffice-ui/src/schemas/index.ts
 create mode 100644 packages/merchant-backoffice-ui/src/settings.json
 copy packages/{auditor-backoffice-ui/src/scss/_footer.scss => 
merchant-backoffice-ui/src/type-override.d.ts} (61%)
 copy packages/{auditor-backoffice-ui/src/scss/_footer.scss => 
taler-util/src/type-override.d.ts} (61%)

diff --git a/packages/bank-ui/src/pages/regional/ConversionConfig.tsx 
b/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
index 485ef5490..3bde7a30a 100644
--- a/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
+++ b/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
@@ -612,60 +612,60 @@ function createFormValidator(
     const errors = undefinedIfEmpty<FormErrors<FormType>>({
       conv: undefinedIfEmpty<FormErrors<FormType["conv"]>>({
         cashin_min_amount: !state.conv.cashin_min_amount
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : !cashin_min_amount
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
         cashin_tiny_amount: !state.conv.cashin_tiny_amount
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : !cashin_tiny_amount
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
         cashin_fee: !state.conv.cashin_fee
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : !cashin_fee
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
 
         cashout_min_amount: !state.conv.cashout_min_amount
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : !cashout_min_amount
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
         cashout_tiny_amount: !state.conv.cashin_tiny_amount
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : !cashout_tiny_amount
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
         cashout_fee: !state.conv.cashin_fee
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : !cashout_fee
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
 
         cashin_rounding_mode: !state.conv.cashin_rounding_mode
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : undefined,
         cashout_rounding_mode: !state.conv.cashout_rounding_mode
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : undefined,
 
         cashin_ratio: !state.conv.cashin_ratio
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : Number.isNaN(cashin_ratio)
-            ? i18n.str`invalid`
+            ? i18n.str`Invalid`
             : undefined,
         cashout_ratio: !state.conv.cashout_ratio
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : Number.isNaN(cashout_ratio)
-            ? i18n.str`invalid`
+            ? i18n.str`Rnvalid`
             : undefined,
       }),
 
       amount: !state.amount
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : !am
-          ? i18n.str`invalid`
+          ? i18n.str`Invalid`
           : undefined,
     });
 
diff --git a/packages/auditor-backoffice-ui/src/scss/_footer.scss 
b/packages/bank-ui/src/type-override.d.ts
similarity index 61%
copy from packages/auditor-backoffice-ui/src/scss/_footer.scss
copy to packages/bank-ui/src/type-override.d.ts
index 7e90c40cc..d820aff57 100644
--- a/packages/auditor-backoffice-ui/src/scss/_footer.scss
+++ b/packages/bank-ui/src/type-override.d.ts
@@ -15,21 +15,18 @@
  */
 
 /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
+ * define unkown type of catch function
  */
-
-footer.footer {
-  .logo {
-    img {
-      width: auto;
-      height: $footer-logo-height;
-    }
-  }
-}
-
-@include mobile {
-  .footer-copyright {
-    text-align: center;
-  }
+interface Promise<T> {
+  /**
+   * Attaches a callback for only the rejection of the Promise.
+   * @param onrejected The callback to execute when the Promise is rejected.
+   * @returns A Promise for the completion of the callback.
+   */
+  catch<TResult = never>(
+    onrejected?:
+      | ((reason: unknown) => TResult | PromiseLike<TResult>)
+      | undefined
+      | null,
+  ): Promise<T | TResult>;
 }
diff --git a/packages/bank-ui/src/utils.ts b/packages/bank-ui/src/utils.ts
index 2cc502416..64413b4d6 100644
--- a/packages/bank-ui/src/utils.ts
+++ b/packages/bank-ui/src/utils.ts
@@ -134,7 +134,7 @@ export async function withRuntimeErrorHandling<T>(
 ): Promise<void> {
   try {
     await cb();
-  } catch (error: unknown) {
+  } catch (error) {
     if (error instanceof TalerError) {
       notify(buildRequestErrorMessage(i18n, error));
     } else {
diff --git a/packages/merchant-backoffice-ui/dev.mjs 
b/packages/merchant-backoffice-ui/dev.mjs
index 6cc99add0..278d11a52 100755
--- a/packages/merchant-backoffice-ui/dev.mjs
+++ b/packages/merchant-backoffice-ui/dev.mjs
@@ -24,7 +24,7 @@ const build = initializeDev({
   type: "development",
   source: {
     js: devEntryPoints,
-    assets: [{base:"src",files:["src/index.html"]}],
+    assets: [{base:"src",files:["src/index.html","src/settings.json"]}],
   },
   css: "sass",
   destination: "./dist/dev",
diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx 
b/packages/merchant-backoffice-ui/src/Routing.tsx
index aeaa262d7..04524dbf2 100644
--- a/packages/merchant-backoffice-ui/src/Routing.tsx
+++ b/packages/merchant-backoffice-ui/src/Routing.tsx
@@ -119,9 +119,6 @@ export enum InstancePaths {
   interface = "/interface",
 }
 
-// eslint-disable-next-line @typescript-eslint/no-empty-function
-// const noop = () => { };
-
 export enum AdminPaths {
   list_instances = "/instances",
   new_instance = "/instance/new",
@@ -596,52 +593,12 @@ function AdminInstanceUpdatePage({
   id,
   ...rest
 }: { id: string } & InstanceUpdatePageProps): VNode {
-  // const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
       <InstanceAdminUpdatePage
         {...rest}
         instanceId={id}
-        // onLoadError={(error: HttpError<TalerErrorDetail>) => {
-        //   const notif =
-        //     error.type === ErrorType.TIMEOUT
-        //       ? {
-        //         message: i18n.str`The request to the backend take too long 
and was cancelled`,
-        //         description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-        //         type: "ERROR" as const,
-        //       }
-        //       : {
-        //         message: i18n.str`The backend reported a problem: HTTP 
status #${error.status}`,
-        //         description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-        //         details:
-        //           error.type === ErrorType.CLIENT ||
-        //             error.type === ErrorType.SERVER
-        //             ? error.payload.hint
-        //             : undefined,
-        //         type: "ERROR" as const,
-        //       };
-        //   return (
-        //     <Fragment>
-        //       <NotificationCard notification={notif} />
-        //       <LoginPage />
-        //     </Fragment>
-        //   );
-        // }}
-        // onUnauthorized={() => {
-        //   return (
-        //     <Fragment>
-        //       <NotificationCard
-        //         notification={{
-        //           message: i18n.str`Access denied`,
-        //           description: i18n.str`The access token provided is 
invalid`,
-        //           type: "ERROR",
-        //         }}
-        //       />
-        //       <LoginPage />
-        //     </Fragment>
-        //   );
-        // }}
       />
     </Fragment>
   );
@@ -707,7 +664,7 @@ function KycBanner(): VNode {
     <NotificationCard
       notification={{
         type: "WARN",
-        message: "KYC verification needed",
+        message: i18n.str`KYC verification needed`,
         description: (
           <div>
             <p>
diff --git 
a/packages/merchant-backoffice-ui/src/components/ErrorLoadingMerchant.tsx 
b/packages/merchant-backoffice-ui/src/components/ErrorLoadingMerchant.tsx
index b1d1cac66..c1a1bcf2e 100644
--- a/packages/merchant-backoffice-ui/src/components/ErrorLoadingMerchant.tsx
+++ b/packages/merchant-backoffice-ui/src/components/ErrorLoadingMerchant.tsx
@@ -1,7 +1,6 @@
-/*
 /*
  This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
 
  GNU Taler is free software; you can redistribute it and/or modify it under the
  terms of the GNU General Public License as published by the Free Software
@@ -15,115 +14,175 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { TalerError, TalerErrorCode, assertUnreachable } from 
"@gnu-taler/taler-util";
+import { TalerError, TalerErrorCode } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
+import { VNode, h } from "preact";
 import { NotificationCard } from "./menu/index.js";
 
 /**
  * equivalent to ErrorLoading for merchant-backoffice which uses 
notification-card
- * @param param0 
- * @returns 
+ * @param param0
+ * @returns
  */
-export function ErrorLoadingMerchant({ error, showDetail }: { error: 
TalerError, showDetail?: boolean }): VNode {
-  const { i18n } = useTranslationContext()
+export function ErrorLoadingMerchant({
+  error,
+}: {
+  error: TalerError;
+  showDetail?: boolean;
+}): VNode {
+  const { i18n } = useTranslationContext();
   switch (error.errorDetail.code) {
     //////////////////
     // Every error that can be produce in a Http Request
     //////////////////
     case TalerErrorCode.GENERIC_TIMEOUT: {
       if (error.hasErrorCode(TalerErrorCode.GENERIC_TIMEOUT)) {
-        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`The request reached a timeout, check your 
connection.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl, timeoutMs }, 
undefined, 2)
-          }} />
+        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`The request reached a timeout, check your 
connection.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl, timeoutMs },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     case TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR: {
       if (error.hasErrorCode(TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR)) {
-        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`The request was cancelled.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl, timeoutMs }, 
undefined, 2)
-          }} />
+        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`The request was cancelled.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl, timeoutMs },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: {
-      if 
(error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT)) {
-        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`The request reached a timeout, check your 
connection.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl, timeoutMs }, 
undefined, 2)
-          }} />
+      if (
+        error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT)
+      ) {
+        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`The request reached a timeout, check your 
connection.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl, timeoutMs },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: {
       if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) {
-        const { requestMethod, requestUrl, throttleStats } = error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`A lot of request were made to the same server 
and this action was throttled.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl, throttleStats 
}, undefined, 2)
-          }} />
+        const { requestMethod, requestUrl, throttleStats } = error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`A lot of request were made to the same server 
and this action was throttled.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl, throttleStats },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: {
-      if 
(error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE)) {
-        const { requestMethod, requestUrl, httpStatusCode, validationError } = 
error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`The response of the request is malformed.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl, 
httpStatusCode, validationError }, undefined, 2)
-          }} />
+      if (
+        error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE)
+      ) {
+        const { requestMethod, requestUrl, httpStatusCode, validationError } =
+          error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`The response of the request is malformed.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl, httpStatusCode, validationError },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     case TalerErrorCode.WALLET_NETWORK_ERROR: {
       if (error.hasErrorCode(TalerErrorCode.WALLET_NETWORK_ERROR)) {
-        const { requestMethod, requestUrl } = error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`Could not complete the request due to a network 
problem.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl }, undefined, 
2)
-          }} />
+        const { requestMethod, requestUrl } = error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`Could not complete the request due to a 
network problem.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: {
       if (error.hasErrorCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR)) {
-        const { requestMethod, requestUrl, httpStatusCode, errorResponse } = 
error.errorDetail
-        return <NotificationCard
-          notification={{
-            type: "ERROR",
-            message: i18n.str`Unexpected request error.`,
-            description: error.message,
-            details: JSON.stringify({ requestMethod, requestUrl, 
httpStatusCode, errorResponse }, undefined, 2)
-          }} />
+        const { requestMethod, requestUrl, httpStatusCode, errorResponse } =
+          error.errorDetail;
+        return (
+          <NotificationCard
+            notification={{
+              type: "ERROR",
+              message: i18n.str`Unexpected request error.`,
+              description: error.message,
+              details: JSON.stringify(
+                { requestMethod, requestUrl, httpStatusCode, errorResponse },
+                undefined,
+                2,
+              ),
+            }}
+          />
+        );
       }
-      assertUnreachable(1 as never)
+      break;
     }
     //////////////////
-    // Every other error 
+    // Every other error
     //////////////////
     // case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: {
     //   return <Attention type="danger" title={i18n.str``}>
@@ -133,14 +192,34 @@ export function ErrorLoadingMerchant({ error, showDetail 
}: { error: TalerError,
     // Default message for unhandled case
     //////////////////
     default: {
-      return <NotificationCard
-        notification={{
-          type: "ERROR",
-          message: i18n.str`Unexpected error.`,
-          description: error.message,
-          details: JSON.stringify(error.errorDetail, undefined, 2)
-        }} />
+      return (
+        <NotificationCard
+          notification={{
+            type: "ERROR",
+            message: i18n.str`Unexpected error.`,
+            description: error.message,
+            details: JSON.stringify(error.errorDetail, undefined, 2),
+          }}
+        />
+      );
     }
   }
+  /**
+   * This should not happen
+   *
+   * The only reason why this is possible is because the case statement
+   * follows and if `if (error.hasErrorCode` which returned false.
+   *
+   * TODO: add a better check
+   */
+  return (
+    <NotificationCard
+      notification={{
+        type: "ERROR",
+        message: i18n.str`Unexpected error.`,
+        description: error.message,
+        details: JSON.stringify(error.errorDetail, undefined, 2),
+      }}
+    />
+  );
 }
-
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
index b0b9eaefc..3ae19dded 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
@@ -50,7 +50,7 @@ export function InputArray<T>({
 
   const error = localError || formError;
 
-  const array: any[] = (value ? value! : []) as any;
+  const array: T[keyof T][] = (value ? value! : []);
   const [currentValue, setCurrentValue] = useState("");
   const { i18n } = useTranslationContext();
 
@@ -104,12 +104,12 @@ export function InputArray<T>({
                     return;
                   }
                   setLocalError(null);
-                  onChange([v, ...array] as any);
+                  onChange([v, ...array] as T[keyof T]);
                   setCurrentValue("");
                 }}
-                data-tooltip={i18n.str`add element to the list`}
+                data-tooltip={i18n.str`Add element to the list`}
               >
-                <i18n.Translate>add</i18n.Translate>
+                <i18n.Translate>Add</i18n.Translate>
               </button>
             </p>
           </div>
@@ -121,12 +121,12 @@ export function InputArray<T>({
                 class="tag is-medium is-info mb-0"
                 style={{ maxWidth: "90%" }}
               >
-                {v}
+                {String(v)}
               </span>
               <a
                 class="tag is-medium is-danger is-delete mb-0"
                 onClick={() => {
-                  onChange(array.filter((f) => f !== v) as any);
+                  onChange(array.filter((f) => f !== v) as T[keyof T]);
                   setCurrentValue(toStr(v));
                 }}
               />
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
index 812505f6a..36785671b 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
@@ -124,25 +124,25 @@ export function InputDate<T>({
           <span
             data-tooltip={
               withTimestampSupport
-                ? i18n.str`change value to unknown date`
-                : i18n.str`change value to empty`
+                ? i18n.str`Change value to unknown date`
+                : i18n.str`Change value to empty`
             }
           >
             <button
               class="button is-info mr-3"
               onClick={() => onChange(undefined as any)}
             >
-              <i18n.Translate>clear</i18n.Translate>
+              <i18n.Translate>Clear</i18n.Translate>
             </button>
           </span>
         )}
         {withTimestampSupport && (
-          <span data-tooltip={i18n.str`change value to never`}>
+          <span data-tooltip={i18n.str`Change value to never`}>
             <button
               class="button is-info"
               onClick={() => onChange({ t_s: "never" } as any)}
             >
-              <i18n.Translate>never</i18n.Translate>
+              <i18n.Translate>Never</i18n.Translate>
             </button>
           </span>
         )}
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
index 080b9508e..d51aa84c4 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
@@ -56,7 +56,7 @@ export function InputDuration<T>({
   if (!value) {
     strValue = "";
   } else if (value.d_ms === "forever") {
-    strValue = i18n.str`forever`;
+    strValue = i18n.str`Forever`;
   } else {
     if (value.d_ms === undefined) {
       throw Error(
@@ -149,22 +149,22 @@ export function InputDuration<T>({
         </div>
 
         {withForever && (
-          <span data-tooltip={i18n.str`change value to never`}>
+          <span data-tooltip={i18n.str`Change value to never`}>
             <button
               class="button is-info mr-3"
               onClick={() => onChange({ d_ms: "forever" } as any)}
             >
-              <i18n.Translate>forever</i18n.Translate>
+              <i18n.Translate>Forever</i18n.Translate>
             </button>
           </span>
         )}
         {!readonly && !withoutClear && (
-          <span data-tooltip={i18n.str`change value to empty`}>
+          <span data-tooltip={i18n.str`Change value to empty`}>
             <button
               class="button is-info "
               onClick={() => onChange(undefined as any)}
             >
-              <i18n.Translate>clear</i18n.Translate>
+              <i18n.Translate>Clear</i18n.Translate>
             </button>
           </span>
         )}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
index d284b476f..22526f947 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
@@ -92,7 +92,7 @@ export function InputImage<T>({
                       "",
                     ),
                   );
-                  return onChange(`data:${f[0].type};base64,${b64}` as any);
+                  return onChange(`data:${f[0].type};base64,${b64}` as T[keyof 
T]);
                 });
               }}
             />
@@ -102,7 +102,7 @@ export function InputImage<T>({
           {error && <p class="help is-danger">{error}</p>}
           {sizeError && (
             <p class="help is-danger">
-              <i18n.Translate>Image should be smaller than 1 
MB</i18n.Translate>
+              <i18n.Translate>Image must be smaller than 1 MB</i18n.Translate>
             </p>
           )}
           {!value && (
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
index c0a7f38ec..aa46a7f8c 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
@@ -18,9 +18,13 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import {
+  PaytoUri,
+  parsePaytoUri,
+  stringifyPaytoUri,
+} from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { COUNTRY_TABLE } from "../../utils/constants.js";
 import { undefinedIfEmpty } from "../../utils/table.js";
@@ -30,9 +34,7 @@ import { InputGroup } from "./InputGroup.js";
 import { InputSelector } from "./InputSelector.js";
 import { InputProps, useField } from "./useField.js";
 
-export interface Props<T> extends InputProps<T> {
-  isValid?: (e: any) => boolean;
-}
+export interface Props<T> extends InputProps<T> {}
 
 // type Entity = PaytoUriGeneric
 // https://datatracker.ietf.org/doc/html/rfc8905
@@ -175,7 +177,7 @@ function validateIBAN_path1(
 
   const checksum = calculate_iban_checksum(step3);
   if (checksum !== 1)
-    return i18n.str`IBAN number is not valid, checksum is wrong`;
+    return i18n.str`IBAN number is invalid, checksum is wrong`;
   return undefined;
 }
 
@@ -192,17 +194,16 @@ export function InputPaytoForm<T>({
 
   const targets = [
     i18n.str`Choose one...`,
-    i18n.str`iban`,
-    i18n.str`x-taler-bank`,
-    i18n.str`bitcoin`,
-    i18n.str`ethereum`,
+    "iban",
+    "bitcoin",
+    "ethereum",
+    "x-taler-bank",
   ];
   const noTargetValue = targets[0];
   const defaultTarget: Entity = {
     target: noTargetValue,
     params: {},
   };
-  
 
   const paths = !initialPayto ? [] : initialPayto.targetPath.split("/");
   const initialPath1 = paths.length >= 1 ? paths[0] : undefined;
@@ -235,10 +236,10 @@ export function InputPaytoForm<T>({
     }
   }, [initialValueStr]);
 
-  const errors: FormErrors<Entity> = {
-    target: value.target === noTargetValue ? i18n.str`required` : undefined,
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
+    target: value.target === noTargetValue ? i18n.str`Required` : undefined,
     path1: !value.path1
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : value.target === "iban"
         ? validateIBAN_path1(value.path1, i18n)
         : value.target === "bitcoin"
@@ -251,38 +252,36 @@ export function InputPaytoForm<T>({
     path2:
       value.target === "x-taler-bank"
         ? !value.path2
-          ? i18n.str`required`
+          ? i18n.str`Required`
           : undefined
         : undefined,
     params: undefinedIfEmpty({
       "receiver-name": !value.params?.["receiver-name"]
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : undefined,
     }),
-  };
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const path1WithSlash =
     value.path1 && !value.path1.endsWith("/") ? value.path1 + "/" : 
value.path1;
   const pto =
     hasErrors || !value.target
       ? undefined
-      : {
+      : ({
           targetType: value.target,
           targetPath: value.path2
             ? `${path1WithSlash}${value.path2}`
             : value.path1 ?? "",
-          params: value.params ?? ({} as any),
+          params: value.params ?? {},
           isKnown: false as const,
-        };
+        } as PaytoUri);
 
   const str = !pto ? undefined : stringifyPaytoUri(pto);
 
   useEffect(() => {
-    onChange(str as any);
+    onChange(str as T[keyof T]);
   }, [str]);
 
   return (
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
index 9956a6427..513095396 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
@@ -103,7 +103,7 @@ export function InputSearchOnList<T extends Entity>({
       <InputWithAddon<Search>
         name="name"
         label={label}
-        tooltip={i18n.str`enter description or id`}
+        tooltip={i18n.str`Enter description or id`}
         addonAfter={
           <span class="icon">
             <i class="mdi mdi-magnify" />
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
index f567f7247..5a1a87236 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
@@ -78,14 +78,16 @@ export function InputSelector<T>({
                 );
               })}
             </select>
-
-            {help}
           </p>
-            {required && (
-              <span class="icon has-text-danger is-right" style={{height: 
"2.5em"}}>
-                <i class="mdi mdi-alert" />
-              </span>
-            )}
+          <p class="help">{help}</p>
+          {required && (
+            <span
+              class="icon has-text-danger is-right"
+              style={{ height: "2.5em" }}
+            >
+              <i class="mdi mdi-alert" />
+            </span>
+          )}
           {error && <p class="help is-danger">{error}</p>}
         </div>
       </div>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
index 8104d1f9f..680ddcd02 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
@@ -96,7 +96,7 @@ export function InputStock<T>({
               {!alreadyExist ? (
                 <button
                   class="button"
-                  data-tooltip={i18n.str`click here to configure the stock of 
the product, leave it as is and the backend will not control stock`}
+                  data-tooltip={i18n.str`Click here to configure the stock of 
the product, leave it as is and the backend will not control stock.`}
                   onClick={(): void => {
                     valueHandler({
                       current: 0,
@@ -112,7 +112,7 @@ export function InputStock<T>({
               ) : (
                 <button
                   class="button"
-                  data-tooltip={i18n.str`this product has been configured 
without stock control`}
+                  data-tooltip={i18n.str`This product has been configured 
without stock control`}
                   disabled
                 >
                   <span>
@@ -133,17 +133,11 @@ export function InputStock<T>({
   const stockAddedErrors: FormErrors<typeof addedStock> = {
     lost:
       currentStock + addedStock.incoming < addedStock.lost
-        ? i18n.str`lost cannot be greater than current and incoming (max 
${currentStock + addedStock.incoming
+        ? i18n.str`Lost can't be greater than current and incoming (max 
${currentStock + addedStock.incoming
           })`
         : undefined,
   };
 
-  // const stockUpdateDescription = stockAddedErrors.lost ? '' : (
-  //   !!addedStock.incoming || !!addedStock.lost ?
-  //     i18n.str`current stock will change from ${currentStock} to 
${currentStock + addedStock.incoming - addedStock.lost}` :
-  //     i18n.str`current stock will stay at ${currentStock}`
-  // )
-
   return (
     <Fragment>
       <div class="card">
@@ -192,7 +186,7 @@ export function InputStock<T>({
                 side={
                   <button
                     class="button is-danger"
-                    data-tooltip={i18n.str`remove stock control for this 
product`}
+                    data-tooltip={i18n.str`Remove stock control for this 
product`}
                     onClick={(): void => {
                       valueHandler(undefined as any);
                     }}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
index 74a7259aa..4957d7dda 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
@@ -40,15 +40,15 @@ export function InputTaxes<T>({ name, label }: Props<keyof 
T>): VNode {
   const [value, valueHandler] = useState<Partial<Entity>>({});
 
   const errors = undefinedIfEmpty({
-    name: !value.name ? i18n.str`required` : undefined,
+    name: !value.name ? i18n.str`Required` : undefined,
     tax: !value.tax
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : Amounts.parse(value.tax) === undefined
-        ? i18n.str`invalid`
+        ? i18n.str`Invalid`
         : undefined,
   });
 
-  const hasErrors = errors === undefined;
+  const hasErrors = errors !== undefined;
 
   const submit = useCallback((): void => {
     onChange([value as any, ...taxes] as any);
@@ -124,7 +124,7 @@ export function InputTaxes<T>({ name, label }: Props<keyof 
T>): VNode {
         <div class="buttons is-right mt-5">
           <button
             class="button is-info"
-            data-tooltip={i18n.str`add tax to the tax list`}
+            data-tooltip={i18n.str`Add tax to the tax list`}
             disabled={hasErrors}
             onClick={submit}
           >
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx 
b/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx
index f5f9d5b4f..c43d28108 100644
--- a/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx
@@ -1,19 +1,42 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
 import { TranslatedString } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 
-export function JumpToElementById({ testIfExist, onSelect, placeholder, 
description }: { placeholder: TranslatedString, description: TranslatedString, 
testIfExist: (id: string) => Promise<boolean>, onSelect: (id: string) => void 
}): VNode {
-  const { i18n } = useTranslationContext()
+export function JumpToElementById({
+  testIfExist,
+  onSelect,
+  placeholder,
+  description,
+}: {
+  placeholder: TranslatedString;
+  description: TranslatedString;
+  testIfExist: (id: string) => Promise<boolean>;
+  onSelect: (id: string) => void;
+}): VNode {
+  const { i18n } = useTranslationContext();
 
-  const [error, setError] = useState<string | undefined>(
-    undefined,
-  );
+  const [error, setError] = useState<string | undefined>(undefined);
 
-  const [id, setId] = useState<string>()
+  const [id, setId] = useState<string>();
   async function check(currentId: string | undefined): Promise<void> {
     if (!currentId) {
-      setError(i18n.str`missing id`);
+      setError(i18n.str`Missing id`);
       return;
     }
     try {
@@ -22,42 +45,38 @@ export function JumpToElementById({ testIfExist, onSelect, 
placeholder, descript
         onSelect(currentId);
         setError(undefined);
       } else {
-        setError(i18n.str`not found`);
+        setError(i18n.str`Not found`);
       }
     } catch {
-      setError(i18n.str`not found`);
+      setError(i18n.str`Not found`);
     }
   }
 
-  return <div class="level">
-    <div class="level-left">
-      <div class="level-item">
-        <div class="field has-addons">
-          <div class="control">
-            <input
-              class={error ? "input is-danger" : "input"}
-              type="text"
-              value={id ?? ""}
-              onChange={(e) => setId(e.currentTarget.value)}
-              placeholder={placeholder}
-            />
-            {error && <p class="help is-danger">{error}</p>}
+  return (
+    <div class="level">
+      <div class="level-left">
+        <div class="level-item">
+          <div class="field has-addons">
+            <div class="control">
+              <input
+                class={error ? "input is-danger" : "input"}
+                type="text"
+                value={id ?? ""}
+                onChange={(e) => setId(e.currentTarget.value)}
+                placeholder={placeholder}
+              />
+              {error && <p class="help is-danger">{error}</p>}
+            </div>
+            <span class="has-tooltip-bottom" data-tooltip={description}>
+              <button class="button" onClick={() => check(id)}>
+                <span class="icon">
+                  <i class="mdi mdi-arrow-right" />
+                </span>
+              </button>
+            </span>
           </div>
-          <span
-            class="has-tooltip-bottom"
-            data-tooltip={description}
-          >
-            <button
-              class="button"
-              onClick={(e) => check(id)}
-            >
-              <span class="icon">
-                <i class="mdi mdi-arrow-right" />
-              </span>
-            </button>
-          </span>
         </div>
       </div>
     </div>
-  </div>
+  );
 }
diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
index aeb49e81d..f7bfce91d 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -25,6 +25,7 @@ import { Fragment, VNode, h } from "preact";
 import { useSessionContext } from "../../context/session.js";
 import { useInstanceKYCDetails } from "../../hooks/instance.js";
 import { LangSelector } from "./LangSelector.js";
+import { usePreference } from "../../hooks/preference.js";
 
 // const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : 
undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -37,6 +38,7 @@ export function Sidebar({ mobile }: Props): VNode {
   const { i18n } = useTranslationContext();
   const { state, logOut, config } = useSessionContext();
   const kycStatus = useInstanceKYCDetails();
+  const [pref] = usePreference();
 
   const needKYC =
     kycStatus !== undefined &&
@@ -45,7 +47,7 @@ export function Sidebar({ mobile }: Props): VNode {
     !!kycStatus.body;
   const isLoggedIn = state.status === "loggedIn";
   const hasToken = isLoggedIn && state.token !== undefined;
-  
+
   return (
     <aside
       class="aside is-placed-left is-expanded"
@@ -118,16 +120,18 @@ export function Sidebar({ mobile }: Props): VNode {
                   </span>
                 </a>
               </li>
-              <li>
-                <a href={"/tokenfamilies"} class="has-icon">
-                  <span class="icon">
-                    <i class="mdi mdi-key" />
-                  </span>
-                  <span class="menu-item-label">
-                    <i18n.Translate>Token Families</i18n.Translate>
-                  </span>
-                </a>
-              </li>
+              {pref.developerMode ? (
+                <li>
+                  <a href={"/tokenfamilies"} class="has-icon">
+                    <span class="icon">
+                      <i class="mdi mdi-key" />
+                    </span>
+                    <span class="menu-item-label">
+                      <i18n.Translate>Token Families</i18n.Translate>
+                    </span>
+                  </a>
+                </li>
+              ) : undefined}
               {needKYC && (
                 <li>
                   <a href={"/kyc"} class="has-icon">
diff --git a/packages/merchant-backoffice-ui/src/components/modal/index.tsx 
b/packages/merchant-backoffice-ui/src/components/modal/index.tsx
index 2a56485fb..8d2cb2831 100644
--- a/packages/merchant-backoffice-ui/src/components/modal/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/modal/index.tsx
@@ -143,7 +143,13 @@ export function ContinueModal({
   );
 }
 
-export function SimpleModal({ onCancel, children }: any): VNode {
+export function SimpleModal({
+  onCancel,
+  children,
+}: {
+  onCancel: () => void;
+  children: ComponentChildren;
+}): VNode {
   return (
     <div class="modal is-active">
       <div class="modal-background " onClick={onCancel} />
@@ -233,15 +239,13 @@ export function ImportingAccountModal({
       parsed !== undefined ? codecForAccountLetter().decode(parsed) : 
undefined;
   } catch (e) {
     account = undefined;
-    if (e instanceof Error) {
-      parsingError = e.message;
-    }
+    parsingError = e instanceof Error ? e.message : String(e);
   }
   const errors: FormErrors<{ letter: string }> = {
     letter: !letter
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : parsed === undefined
-        ? i18n.str`letter should be a JSON string`
+        ? i18n.str`Letter must be a JSON string`
         : account === undefined
           ? i18n.str`JSON string is invalid`
           : undefined,
@@ -329,7 +333,7 @@ export function CompareAccountsModal({
   const { i18n } = useTranslationContext();
   return (
     <ConfirmModal
-      label={i18n.str`Update form`}
+      label={i18n.str`Correct form`}
       description={i18n.str`Comparing account details`}
       active
       onCancel={onCancel}
@@ -455,7 +459,7 @@ export function DeleteModal({
         <i18n.Translate>
           Deleting an instance{" "}
           <b>
-            <i18n.Translate>cannot be undone</i18n.Translate>
+            <i18n.Translate>Can't be undone</i18n.Translate>
           </b>
           .
         </i18n.Translate>
@@ -496,7 +500,7 @@ export function PurgeModal({
         <i18n.Translate>
           Purging an instance{" "}
           <b>
-            <i18n.Translate>cannot be undone</i18n.Translate>
+            <i18n.Translate>Can't be undone</i18n.Translate>
           </b>
           .
         </i18n.Translate>
@@ -530,20 +534,20 @@ export function UpdateTokenModal({
   const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token;
   const errors = undefinedIfEmpty({
     old_token: hasInputTheCorrectOldToken
-      ? i18n.str`is not the same as the current access token`
+      ? i18n.str`Is not the same as the current access token`
       : undefined,
     new_token: !form.new_token
-      ? i18n.str`cannot be empty`
+      ? i18n.str`Required`
       : form.new_token === form.old_token
-        ? i18n.str`cannot be the same as the old token`
+        ? i18n.str`Can't be the same as the old token`
         : undefined,
     repeat_token:
       form.new_token !== form.repeat_token
-        ? i18n.str`is not the same`
+        ? i18n.str`Is not the same`
         : undefined,
   });
 
-  const hasErrors = errors === undefined;
+  const hasErrors = errors !== undefined;
 
   const { state } = useSessionContext();
 
@@ -564,20 +568,20 @@ export function UpdateTokenModal({
               <Input<State>
                 name="old_token"
                 label={i18n.str`Old access token`}
-                tooltip={i18n.str`access token currently in use`}
+                tooltip={i18n.str`Access token currently in use`}
                 inputType="password"
               />
             )}
             <Input<State>
               name="new_token"
               label={i18n.str`New access token`}
-              tooltip={i18n.str`next access token to be used`}
+              tooltip={i18n.str`Next access token to be used`}
               inputType="password"
             />
             <Input<State>
               name="repeat_token"
               label={i18n.str`Repeat access token`}
-              tooltip={i18n.str`confirm the same access token`}
+              tooltip={i18n.str`Confirm the same access token`}
               inputType="password"
             />
           </FormProvider>
@@ -605,21 +609,19 @@ export function SetTokenNewInstanceModal({
   });
   const { i18n } = useTranslationContext();
 
-  const errors = {
+  const errors = undefinedIfEmpty({
     new_token: !form.new_token
-      ? i18n.str`cannot be empty`
+      ? i18n.str`Required`
       : form.new_token === form.old_token
-        ? i18n.str`cannot be the same as the old access token`
+        ? i18n.str`Can't be the same as the old access token`
         : undefined,
     repeat_token:
       form.new_token !== form.repeat_token
-        ? i18n.str`is not the same`
+        ? i18n.str`Is not the same`
         : undefined,
-  };
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   return (
     <div class="modal is-active">
@@ -641,13 +643,13 @@ export function SetTokenNewInstanceModal({
                 <Input<State>
                   name="new_token"
                   label={i18n.str`New access token`}
-                  tooltip={i18n.str`next access token to be used`}
+                  tooltip={i18n.str`Next access token to be used`}
                   inputType="password"
                 />
                 <Input<State>
                   name="repeat_token"
                   label={i18n.str`Repeat access token`}
-                  tooltip={i18n.str`confirm the same access token`}
+                  tooltip={i18n.str`Confirm the same access token`}
                   inputType="password"
                 />
               </FormProvider>
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
 
b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
index 3d15918dc..ca7ee3278 100644
--- 
a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
@@ -110,7 +110,7 @@ export function InventoryProductForm({
               <InputNumber<Form>
                 name="quantity"
                 label={i18n.str`Quantity`}
-                tooltip={i18n.str`how many products will be added`}
+                tooltip={i18n.str`How many products will be added`}
               />
             )}
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
 
b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
index cf603522a..4fd6e62ad 100644
--- 
a/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
@@ -15,17 +15,16 @@
  */
 import { AmountString, Amounts, TalerMerchantApi } from 
"@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useCallback, useEffect, useState } from "preact/hooks";
-import * as yup from "yup";
 import { useListener } from "../../hooks/listener.js";
+import { undefinedIfEmpty } from "../../utils/table.js";
 import { FormErrors, FormProvider } from "../form/FormProvider.js";
 import { Input } from "../form/Input.js";
 import { InputCurrency } from "../form/InputCurrency.js";
 import { InputImage } from "../form/InputImage.js";
 import { InputNumber } from "../form/InputNumber.js";
 import { InputTaxes } from "../form/InputTaxes.js";
-import { undefinedIfEmpty } from "../../utils/table.js";
 
 type Entity = TalerMerchantApi.Product;
 
@@ -69,7 +68,7 @@ export function NonInventoryProductFrom({
       <div class="buttons">
         <button
           class="button is-success"
-          data-tooltip={i18n.str`describe and add a product that is not in the 
inventory list`}
+          data-tooltip={i18n.str`Describe and add a product that is not in the 
inventory list`}
           onClick={() => setShowCreateProduct(true)}
         >
           <i18n.Translate>Add custom product</i18n.Translate>
@@ -145,22 +144,21 @@ export function ProductForm({ onSubscribe, initial }: 
ProductProps): VNode {
     taxes: [],
     ...initial,
   });
-  // let errors: FormErrors<NonInventoryProduct> = {};
-  const errors: FormErrors<NonInventoryProduct> | undefined = 
undefinedIfEmpty({
+  const errors = undefinedIfEmpty<FormErrors<NonInventoryProduct>>({
     quantity:
       value.quantity === undefined
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : typeof value.quantity !== "number"
-          ? i18n.str`should be a number`
+          ? i18n.str`Must be a number`
           : value.quantity < 1
-            ? i18n.str`should be grater than 0`
+            ? i18n.str`Must be grater than 0`
             : undefined,
-    description: !value.description ? i18n.str`required` : undefined,
-    unit: !value.description ? i18n.str`required` : undefined,
+    description: !value.description ? i18n.str`Required` : undefined,
+    unit: !value.description ? i18n.str`Required` : undefined,
     price: !value.price
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : Amounts.parse(value.price) === undefined
-        ? i18n.str`invalid`
+        ? i18n.str`Invalid`
         : undefined,
   });
 
@@ -168,7 +166,7 @@ export function ProductForm({ onSubscribe, initial }: 
ProductProps): VNode {
     return value as TalerMerchantApi.Product;
   }, [value]);
 
-  const hasErrors = errors === undefined;
+  const hasErrors = errors !== undefined;
 
   useEffect(() => {
     onSubscribe(hasErrors ? undefined : submit);
@@ -185,29 +183,29 @@ export function ProductForm({ onSubscribe, initial }: 
ProductProps): VNode {
         <InputImage<NonInventoryProduct>
           name="image"
           label={i18n.str`Image`}
-          tooltip={i18n.str`photo of the product`}
+          tooltip={i18n.str`Photo of the product.`}
         />
         <Input<NonInventoryProduct>
           name="description"
           inputType="multiline"
           label={i18n.str`Description`}
-          tooltip={i18n.str`full product description`}
+          tooltip={i18n.str`Full product description.`}
         />
         <Input<NonInventoryProduct>
           name="unit"
           label={i18n.str`Unit`}
-          tooltip={i18n.str`name of the product unit`}
+          tooltip={i18n.str`Name of the product unit.`}
         />
         <InputCurrency<NonInventoryProduct>
           name="price"
           label={i18n.str`Price`}
-          tooltip={i18n.str`amount in the current currency`}
+          tooltip={i18n.str`Amount in the current currency.`}
         />
 
         <InputNumber<NonInventoryProduct>
           name="quantity"
           label={i18n.str`Quantity`}
-          tooltip={i18n.str`how many products will be added`}
+          tooltip={i18n.str`How many products will be added.`}
         />
 
         <InputTaxes<NonInventoryProduct> name="taxes" label={i18n.str`Taxes`} 
/>
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx 
b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
index 0a811da5c..4bdcb4acd 100644
--- a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
@@ -66,19 +66,19 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
   });
 
   const errors = undefinedIfEmpty({
-    product_id: !value.product_id ? i18n.str`required` : undefined,    
-    description: !value.description ? i18n.str`required` : undefined,
-    unit: !value.unit ? i18n.str`required` : undefined,
+    product_id: !value.product_id ? i18n.str`Required` : undefined,
+    description: !value.description ? i18n.str`Required` : undefined,
+    unit: !value.unit ? i18n.str`Required` : undefined,
     price: !value.price
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : Amounts.parse(value.price) === undefined
-        ? i18n.str`invalid amount`
+        ? i18n.str`Invalid amount`
         : undefined,
     minimum_age:
       value.minimum_age === undefined
         ? undefined
         : value.minimum_age < 1
-          ? i18n.str`should be greater than 0`
+          ? i18n.str`Must be greater than 0`
           : undefined,
   });
 
@@ -131,47 +131,47 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
             name="product_id"
             addonBefore={new URL("product/", state.backendUrl.href).href}
             label={i18n.str`ID`}
-            tooltip={i18n.str`product identification to use in URLs (for 
internal use only)`}
+            tooltip={i18n.str`Product identification to use in URLs (for 
internal use only)`}
           />
         )}
         <InputImage<Entity>
           name="image"
           label={i18n.str`Image`}
-          tooltip={i18n.str`illustration of the product for customers`}
+          tooltip={i18n.str`Illustration of the product for customers`}
         />
         <Input<Entity>
           name="description"
           inputType="multiline"
           label={i18n.str`Description`}
-          tooltip={i18n.str`product description for customers`}
+          tooltip={i18n.str`Product description for customers`}
         />
         <InputNumber<Entity>
           name="minimum_age"
           label={i18n.str`Age restriction`}
-          tooltip={i18n.str`is this product restricted for customer below 
certain age?`}
-          help={i18n.str`minimum age of the customer`}
+          tooltip={i18n.str`Is this product restricted for customer below 
certain age?`}
+          help={i18n.str`Minimum age of the customer`}
         />
         <Input<Entity>
           name="unit"
           label={i18n.str`Unit name`}
-          tooltip={i18n.str`unit describing quantity of product sold (e.g. 2 
kilograms, 5 liters, 3 items, 5 meters) for customers`}
-          help={i18n.str`example: kg, items or liters`}
+          tooltip={i18n.str`Unit describing quantity of product sold (e.g. 2 
kilograms, 5 liters, 3 items, 5 meters) for customers`}
+          help={i18n.str`Example: kg, items or liters`}
         />
         <InputCurrency<Entity>
           name="price"
           label={i18n.str`Price per unit`}
-          tooltip={i18n.str`sale price for customers, including taxes, for 
above units of the product`}
+          tooltip={i18n.str`Sale price for customers, including taxes, for 
above units of the product`}
         />
         <InputStock
           name="stock"
           label={i18n.str`Stock`}
           alreadyExist={alreadyExist}
-          tooltip={i18n.str`inventory for products with finite supply (for 
internal use only)`}
+          tooltip={i18n.str`Inventory for products with finite supply (for 
internal use only)`}
         />
         <InputTaxes<Entity>
           name="taxes"
           label={i18n.str`Taxes`}
-          tooltip={i18n.str`taxes included in the product price, exposed to 
customers`}
+          tooltip={i18n.str`Taxes included in the product price, exposed to 
customers`}
         />
       </FormProvider>
     </div>
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx 
b/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx
index 4fff66fd7..401013782 100644
--- a/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx
@@ -34,19 +34,19 @@ export function ProductList({ list, actions = [] }: Props): 
VNode {
         <thead>
           <tr>
             <th>
-              <i18n.Translate>image</i18n.Translate>
+              <i18n.Translate>Image</i18n.Translate>
             </th>
             <th>
-              <i18n.Translate>description</i18n.Translate>
+              <i18n.Translate>Description</i18n.Translate>
             </th>
             <th>
-              <i18n.Translate>quantity</i18n.Translate>
+              <i18n.Translate>Quantity</i18n.Translate>
             </th>
             <th>
-              <i18n.Translate>unit price</i18n.Translate>
+              <i18n.Translate>Unit price</i18n.Translate>
             </th>
             <th>
-              <i18n.Translate>total price</i18n.Translate>
+              <i18n.Translate>Total price</i18n.Translate>
             </th>
             <th />
           </tr>
diff --git 
a/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx
 
b/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx
index 603efca1e..a85912efa 100644
--- 
a/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx
@@ -58,13 +58,13 @@ export function TokenFamilyForm({ onSubscribe }: Props) {
   const { i18n } = useTranslationContext();
 
   const errors: FormErrors<Entity> = {
-    slug: !value.slug ? i18n.str`required` : undefined,
-    name: !value.name ? i18n.str`required` : undefined,
-    description: !value.description ? i18n.str`required` : undefined,
+    slug: !value.slug ? i18n.str`Required` : undefined,
+    name: !value.name ? i18n.str`Required` : undefined,
+    description: !value.description ? i18n.str`Required` : undefined,
     valid_after: !value.valid_after ? undefined : undefined,
-    valid_before: !value.valid_before ? i18n.str`required` : undefined,
-    duration: !value.duration ? i18n.str`required` : undefined,
-    kind: !value.kind ? i18n.str`required` : undefined,
+    valid_before: !value.valid_before ? i18n.str`Required` : undefined,
+    duration: !value.duration ? i18n.str`Required` : undefined,
+    kind: !value.kind ? i18n.str`Required` : undefined,
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -94,42 +94,42 @@ export function TokenFamilyForm({ onSubscribe }: Props) {
           name="slug"
           addonBefore={new URL("tokenfamily/", state.backendUrl.href).href}
           label={i18n.str`Slug`}
-          tooltip={i18n.str`token family slug to use in URLs (for internal use 
only)`}
+          tooltip={i18n.str`Token family slug to use in URLs (for internal use 
only)`}
         />
         <InputSelector<Entity>
           name="kind"
           label={i18n.str`Kind`}
-          tooltip={i18n.str`token family kind`}
+          tooltip={i18n.str`Token family kind`}
           values={["discount", "subscription"]}
         />
         <Input<Entity>
           name="name"
           inputType="text"
           label={i18n.str`Name`}
-          tooltip={i18n.str`user-readable token family name`}
+          tooltip={i18n.str`User-readable token family name`}
         />
         <Input<Entity>
           name="description"
           inputType="multiline"
           label={i18n.str`Description`}
-          tooltip={i18n.str`token family description for customers`}
+          tooltip={i18n.str`Token family description for customers`}
         />
         <InputDate<Entity>
           name="valid_after"
           label={i18n.str`Valid After`}
-          tooltip={i18n.str`token family can issue tokens after this date`}
+          tooltip={i18n.str`Token family can issue tokens after this date`}
           withTimestampSupport
         />
         <InputDate<Entity>
           name="valid_before"
           label={i18n.str`Valid Before`}
-          tooltip={i18n.str`token family can issue tokens until this date`}
+          tooltip={i18n.str`Token family can issue tokens until this date`}
           withTimestampSupport
         />
         <InputDuration<Entity>
           name="duration"
           label={i18n.str`Duration`}
-          tooltip={i18n.str`validity duration of a issued token`}
+          tooltip={i18n.str`Validity duration of a issued token`}
           withForever
         />
       </FormProvider>
diff --git a/packages/merchant-backoffice-ui/src/hooks/async.ts 
b/packages/merchant-backoffice-ui/src/hooks/async.ts
index 212ef2211..e4e50ab8e 100644
--- a/packages/merchant-backoffice-ui/src/hooks/async.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/async.ts
@@ -25,7 +25,7 @@ export interface Options {
 }
 
 export interface AsyncOperationApi<T> {
-  request: (...a: any) => void;
+  request: (...a: unknown[]) => void;
   cancel: () => void;
   data: T | undefined;
   isSlow: boolean;
@@ -34,15 +34,15 @@ export interface AsyncOperationApi<T> {
 }
 
 export function useAsync<T>(
-  fn?: (...args: any) => Promise<T>,
+  fn?: (...args: unknown[]) => Promise<T>,
   { slowTolerance: tooLong }: Options = { slowTolerance: 1000 },
 ): AsyncOperationApi<T> {
   const [data, setData] = useState<T | undefined>(undefined);
   const [isLoading, setLoading] = useState<boolean>(false);
-  const [error, setError] = useState<any>(undefined);
+  const [error, setError] = useState<string>();
   const [isSlow, setSlow] = useState(false);
 
-  const request = async (...args: any) => {
+  const request = async (...args: unknown[]) => {
     if (!fn) return;
     setLoading(true);
 
@@ -54,7 +54,7 @@ export function useAsync<T>(
       const result = await fn(...args);
       setData(result);
     } catch (error) {
-      setError(error);
+      setError(error instanceof Error ? error.message : String(error));
     }
     setLoading(false);
     setSlow(false);
diff --git a/packages/merchant-backoffice-ui/src/hooks/preference.ts 
b/packages/merchant-backoffice-ui/src/hooks/preference.ts
index fddbc6e08..705422654 100644
--- a/packages/merchant-backoffice-ui/src/hooks/preference.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/preference.ts
@@ -28,6 +28,7 @@ import { buildStorageKey, useLocalStorage } from 
"@gnu-taler/web-util/browser";
 export interface Preferences {
   advanceOrderMode: boolean;
   advanceInstanceMode: boolean;
+  developerMode: boolean;
   hideKycUntil: AbsoluteTime;
   hideMissingAccountUntil: AbsoluteTime;
   dateFormat: "ymd" | "dmy" | "mdy";
@@ -36,6 +37,7 @@ export interface Preferences {
 const defaultSettings: Preferences = {
   advanceOrderMode: false,
   advanceInstanceMode: false,
+  developerMode: false,
   hideKycUntil: AbsoluteTime.never(),
   hideMissingAccountUntil: AbsoluteTime.never(),
   dateFormat: "ymd",
@@ -45,6 +47,7 @@ export const codecForPreferences = (): Codec<Preferences> =>
   buildCodecForObject<Preferences>()
     .property("advanceOrderMode", codecForBoolean())
     .property("advanceInstanceMode", codecForBoolean())
+    .property("developerMode", codecForBoolean())
     .property("hideKycUntil", codecForAbsoluteTime)
     .property("hideMissingAccountUntil", codecForAbsoluteTime)
     .property(
diff --git 
a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
index 6a94109a0..832616e9c 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -38,13 +38,10 @@ import { usePreference } from 
"../../../hooks/preference.js";
 import { INSTANCE_ID_REGEX } from "../../../utils/constants.js";
 import { undefinedIfEmpty } from "../../../utils/table.js";
 
-export type Entity = Omit<
-  Omit<TalerMerchantApi.InstanceConfigurationMessage, "default_pay_delay">,
-  "default_wire_transfer_delay"
-> & {
+export type Entity = TalerMerchantApi.InstanceConfigurationMessage & {
   auth_token?: string;
-  default_pay_delay: Duration;
-  default_wire_transfer_delay: Duration;
+  // default_pay_delay: Duration;
+  // default_wire_transfer_delay: Duration;
 };
 
 interface Props {
@@ -53,14 +50,16 @@ interface Props {
   forceId?: string;
 }
 
+const twoHours = Duration.fromSpec({ hours: 2 });
+const twoDays = Duration.fromSpec({ days: 2 });
+
 function with_defaults(id?: string): Partial<Entity> {
   return {
     id,
-    // accounts: [],
     user_type: "business",
     use_stefan: true,
-    default_pay_delay: { d_ms: 2 * 60 * 60 * 1000 }, // two hours
-    default_wire_transfer_delay: { d_ms: 2 * 60 * 60 * 24 * 1000 }, // two days
+    default_pay_delay: Duration.toTalerProtocolDuration(twoHours),
+    default_wire_transfer_delay: Duration.toTalerProtocolDuration(twoDays),
   };
 }
 
@@ -74,48 +73,48 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
 
   const errors: FormErrors<Entity> = {
     id: !value.id
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !INSTANCE_ID_REGEX.test(value.id)
-        ? i18n.str`is not valid`
+        ? i18n.str`Invalid`
         : undefined,
-    name: !value.name ? i18n.str`required` : undefined,
+    name: !value.name ? i18n.str`Required` : undefined,
 
     user_type: !value.user_type
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : value.user_type !== "business" && value.user_type !== "individual"
-        ? i18n.str`should be business or individual`
+        ? i18n.str`Must be business or individual`
         : undefined,
     // accounts:
     //   !value.accounts || !value.accounts.length
-    //     ? i18n.str`required`
+    //     ? i18n.str`Required`
     //     : undefinedIfEmpty(
     //       value.accounts.map((p) => {
     //         return !PAYTO_REGEX.test(p.payto_uri)
-    //           ? i18n.str`is not valid`
+    //           ? i18n.str`Invalid`
     //           : undefined;
     //       }),
     //     ),
     default_pay_delay: !value.default_pay_delay
-      ? i18n.str`required`
-      : !!value.default_wire_transfer_delay &&
-          value.default_wire_transfer_delay.d_ms !== "forever" &&
-          value.default_pay_delay.d_ms !== "forever" &&
-          value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms
-        ? i18n.str`pay delay can't be greater than wire transfer delay`
+      ? i18n.str`Required`
+      : value.default_wire_transfer_delay !== undefined &&
+          value.default_wire_transfer_delay.d_us !== "forever" &&
+          value.default_pay_delay.d_us !== "forever" &&
+          value.default_pay_delay.d_us > value.default_wire_transfer_delay.d_us
+        ? i18n.str`Pay delay can't be greater than wire transfer delay`
         : undefined,
     default_wire_transfer_delay: !value.default_wire_transfer_delay
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : undefined,
     address: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n.str`max 7 lines`
+          ? i18n.str`Max 7 lines`
           : undefined,
     }),
     jurisdiction: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n.str`max 7 lines`
+          ? i18n.str`Max 7 lines`
           : undefined,
     }),
   };
@@ -136,20 +135,8 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
         : { method: "token", token: createRFC8959AccessTokenPlain(newToken) };
     if (!newValue.address) newValue.address = {};
     if (!newValue.jurisdiction) newValue.jurisdiction = {};
-    // remove above use conversion
-    // schema.validateSync(value, { abortEarly: false })
-    newValue.default_pay_delay = Duration.toTalerProtocolDuration(
-      newValue.default_pay_delay!,
-    ) as any;
-    newValue.default_wire_transfer_delay = Duration.toTalerProtocolDuration(
-      newValue.default_wire_transfer_delay!,
-    ) as any;
-    // delete value.default_pay_delay;
-    // delete value.default_wire_transfer_delay;
 
-    return onCreate(
-      newValue as any as TalerMerchantApi.InstanceConfigurationMessage,
-    );
+    return onCreate(newValue as TalerMerchantApi.InstanceConfigurationMessage);
   };
 
   function updateToken(token: string | null) {
@@ -241,7 +228,7 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
                           ? "button has-tooltip-bottom"
                           : "button is-info has-tooltip-bottom"
                     }
-                    data-tooltip={i18n.str`change authorization configuration`}
+                    data-tooltip={i18n.str`Change authorization configuration`}
                     onClick={() => updateIsTokenDialogActive(true)}
                   >
                     <div class="icon is-centered">
@@ -291,7 +278,7 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields and choose 
authorization method`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
               >
                 <i18n.Translate>Confirm</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx
index c4b467363..c7b97e670 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx
@@ -18,9 +18,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 import { TalerMerchantApi } from "@gnu-taler/taler-util";
-import {
-  useTranslationContext
-} from "@gnu-taler/web-util/browser";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../components/menu/index.js";
@@ -47,9 +45,7 @@ export default function Create({ onBack, onConfirm, forceId 
}: Props): VNode {
       <CreatePage
         onBack={onBack}
         forceId={forceId}
-        onCreate={async (
-          d: TalerMerchantApi.InstanceConfigurationMessage,
-        ) => {
+        onCreate={async (d: TalerMerchantApi.InstanceConfigurationMessage) => {
           if (state.status !== "loggedIn") return;
           try {
             const resp = await lib.instance.createInstance(state.token, d);
@@ -79,16 +75,12 @@ export default function Create({ onBack, onConfirm, forceId 
}: Props): VNode {
               }
             }
             onConfirm();
-          } catch (ex) {
-            if (ex instanceof Error) {
-              setNotif({
-                message: i18n.str`Failed to create instance`,
-                type: "ERROR",
-                description: ex.message,
-              });
-            } else {
-              console.error(ex);
-            }
+          } catch (error) {
+            setNotif({
+              message: i18n.str`Failed to create instance`,
+              type: "ERROR",
+              description: error instanceof Error ? error.message : 
String(error),
+            });
           }
         }}
       />
diff --git 
a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
index 870d77a33..4e3d98aba 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
@@ -97,7 +97,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new instance`}
+            data-tooltip={i18n.str`Add new instance`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
index 61f62e631..953e412fe 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
@@ -21,16 +21,15 @@
 
 import {
   HttpStatusCode,
-  PaytoString,
   PaytoUri,
   TalerError,
   TalerMerchantApi,
   TranslatedString,
   assertUnreachable,
-  parsePaytoUri,
+  parsePaytoUri
 } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
 import {
@@ -40,6 +39,7 @@ import {
 import { Input } from "../../../../components/form/Input.js";
 import { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
+import { InputToggle } from "../../../../components/form/InputToggle.js";
 import {
   CompareAccountsModal,
   ImportingAccountModal,
@@ -47,7 +47,6 @@ import {
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 import { safeConvertURL } from "../update/UpdatePage.js";
 import { testRevenueAPI } from "./index.js";
-import { InputToggle } from "../../../../components/form/InputToggle.js";
 
 type Entity = TalerMerchantApi.AccountAddDetails & { verified?: boolean };
 
@@ -73,7 +72,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     undefined,
   );
   const errors: FormErrors<Entity> = {
-    payto_uri: !state.payto_uri ? i18n.str`required` : undefined,
+    payto_uri: !state.payto_uri ? i18n.str`Required` : undefined,
 
     credit_facade_credentials: !state.credit_facade_credentials
       ? undefined
@@ -81,12 +80,12 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
           username:
             state.credit_facade_credentials.type === "basic" &&
             !state.credit_facade_credentials.username
-              ? i18n.str`required`
+              ? i18n.str`Required`
               : undefined,
           password:
             state.credit_facade_credentials.type === "basic" &&
             !state.credit_facade_credentials.password
-              ? i18n.str`required`
+              ? i18n.str`Required`
               : undefined,
         }),
     credit_facade_url: !state.credit_facade_url
@@ -94,11 +93,11 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
       : !facadeURL
         ? i18n.str`Invalid url`
         : !facadeURL.href.endsWith("/")
-          ? i18n.str`URL should end with a '/'`
+          ? i18n.str`URL must end with a '/'`
           : facadeURL.searchParams.size > 0
-            ? i18n.str`URL should not contain params`
+            ? i18n.str`URL must not contain params`
             : facadeURL.hash
-              ? i18n.str`URL should not hash param`
+              ? i18n.str`URL must not hash param`
               : undefined,
   };
 
@@ -223,7 +222,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               <Input<Entity>
                 name="credit_facade_url"
                 label={i18n.str`Endpoint URL`}
-                
help="https://bank.demo.taler.net/accounts/_username_/taler-revenue/";
+                
help="https://bank.demo.taler.net/accounts/${USERNAME}/taler-revenue/";
                 expand
                 tooltip={i18n.str`From where the merchant can download 
information about incoming wire transfers to this account`}
               />
@@ -273,7 +272,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                     data-tooltip={i18n.str`Compare info from server with 
account form`}
                     disabled={!state.credit_facade_url}
                     onClick={async () => {
-                      const result = await testAccountInfo();
+                      await testAccountInfo();
                     }}
                   >
                     <i18n.Translate>Test</i18n.Translate>
@@ -302,7 +301,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
                 onClick={submitForm}
               >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index 26b8d26b9..ac48e65ea 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -64,7 +64,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
             .then((created) => {
               if (created.type === "fail") {
                 setNotif({
-                  message: i18n.str`could not create account`,
+                  message: i18n.str`Could not create account`,
                   type: "ERROR",
                   description: created.detail.hint,
                 });
@@ -74,9 +74,9 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not create account`,
+                message: i18n.str`Could not create account`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
index 0e813f4d2..f9403af05 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
@@ -64,7 +64,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new accounts`}
+            data-tooltip={i18n.str`Add new account`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -133,7 +133,7 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
       {bitcoinAccounts.length > 0 && (
         <div class="table-container">
           <p class="card-header-title">
-            <i18n.Translate>Bitcoin type accounts</i18n.Translate>
+            <i18n.Translate>Wire method: Bitcoin</i18n.Translate>
           </p>
           <table class="table is-fullwidth is-striped is-hoverable 
is-fullwidth">
             <thead>
@@ -177,10 +177,10 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
                       <div class="buttons is-right">
                         <button
                           class="button is-danger is-small has-tooltip-left"
-                          data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                          data-tooltip={i18n.str`Delete selected accounts from 
the database`}
                           onClick={() => onDelete(acc)}
                         >
-                          Delete
+                          <i18n.Translate>Delete</i18n.Translate>
                         </button>
                       </div>
                     </td>
@@ -195,7 +195,7 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
       {talerbankAccounts.length > 0 && (
         <div class="table-container">
           <p class="card-header-title">
-            <i18n.Translate>Taler type accounts</i18n.Translate>
+            <i18n.Translate>Wire method: x-taler-bank</i18n.Translate>
           </p>
           <table class="table is-fullwidth is-striped is-hoverable 
is-fullwidth">
             <thead>
@@ -230,10 +230,10 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
                       <div class="buttons is-right">
                         <button
                           class="button is-danger is-small has-tooltip-left"
-                          data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                          data-tooltip={i18n.str`Delete selected accounts from 
the database`}
                           onClick={() => onDelete(acc)}
                         >
-                          Delete
+                          <i18n.Translate>Delete</i18n.Translate>
                         </button>
                       </div>
                     </td>
@@ -248,7 +248,7 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
       {ibanAccounts.length > 0 && (
         <div class="table-container">
           <p class="card-header-title">
-            <i18n.Translate>IBAN type accounts</i18n.Translate>
+            <i18n.Translate>Wire method: IBAN</i18n.Translate>
           </p>
           <table class="table is-fullwidth is-striped is-hoverable 
is-fullwidth">
             <thead>
@@ -283,10 +283,10 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
                       <div class="buttons is-right">
                         <button
                           class="button is-danger is-small has-tooltip-left"
-                          data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                          data-tooltip={i18n.str`Delete selected accounts from 
the database`}
                           onClick={() => onDelete(acc)}
                         >
-                          Delete
+                          <i18n.Translate>Delete</i18n.Translate>
                         </button>
                       </div>
                     </td>
@@ -301,7 +301,7 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
       {unkownAccounts.length > 0 && (
         <div class="table-container">
           <p class="card-header-title">
-            <i18n.Translate>Other type accounts</i18n.Translate>
+            <i18n.Translate>Other accounts</i18n.Translate>
           </p>
           <table class="table is-fullwidth is-striped is-hoverable 
is-fullwidth">
             <thead>
@@ -336,10 +336,10 @@ function Table({ accounts, onDelete, onSelect }: 
TableProps): VNode {
                       <div class="buttons is-right">
                         <button
                           class="button is-danger is-small has-tooltip-left"
-                          data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                          data-tooltip={i18n.str`Delete selected accounts from 
the database`}
                           onClick={() => onDelete(acc)}
                         >
-                          Delete
+                          <i18n.Translate>Delete</i18n.Translate>
                         </button>
                       </div>
                     </td>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
index c0712bbcb..2bb1144f5 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
@@ -95,12 +95,12 @@ export default function ListOtpDevices({ onCreate, onSelect 
}: Props): VNode {
               .then((resp) => {
                 if (resp.type === "ok") {
                   setNotif({
-                    message: i18n.str`bank account delete successfully`,
+                    message: i18n.str`Bank account delete successfully`,
                     type: "SUCCESS",
                   });
                 } else {
                   setNotif({
-                    message: i18n.str`could not delete the bank account`,
+                    message: i18n.str`Could not delete the bank account`,
                     type: "ERROR",
                     description: resp.detail.hint,
                   });
@@ -108,9 +108,9 @@ export default function ListOtpDevices({ onCreate, onSelect 
}: Props): VNode {
               })
               .catch((error) =>
                 setNotif({
-                  message: i18n.str`could not delete the bank account`,
+                  message: i18n.str`Could not delete the bank account`,
                   type: "ERROR",
-                  description: error.message,
+                  description: error instanceof Error ? error.message : 
String(error),
                 }),
               );
           }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
index 5886a35ae..e6dcfec7b 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
@@ -95,19 +95,19 @@ export function UpdatePage({
 
   const facadeURL = safeConvertURL(state.credit_facade_url);
 
-  const errors: FormErrors<FormType> = {
-    payto_uri: !state.payto_uri ? i18n.str`required` : undefined,
+  const errors = undefinedIfEmpty<FormErrors<FormType>>({
+    payto_uri: !state.payto_uri ? i18n.str`Required` : undefined,
 
     credit_facade_url: !state.credit_facade_url
       ? undefined
       : !facadeURL
         ? i18n.str`Invalid url`
         : !facadeURL.href.endsWith("/")
-          ? i18n.str`URL should end with a '/'`
+          ? i18n.str`URL must end with a '/'`
           : facadeURL.searchParams.size > 0
-            ? i18n.str`URL should not contain params`
+            ? i18n.str`URL must not contain params`
             : facadeURL.hash
-              ? i18n.str`URL should not hash param`
+              ? i18n.str`URL must not hash param`
               : undefined,
     credit_facade_credentials: !state.credit_facade_credentials
       ? undefined
@@ -116,27 +116,25 @@ export function UpdatePage({
             replacingAccountId &&
             // @ts-expect-error unedit is not in facade creds
             state.credit_facade_credentials?.type === "unedit"
-              ? i18n.str`required`
+              ? i18n.str`Required`
               : undefined,
           username:
             state.credit_facade_credentials?.type !== "basic"
               ? undefined
               : !state.credit_facade_credentials.username
-                ? i18n.str`required`
+                ? i18n.str`Required`
                 : undefined,
 
           password:
             state.credit_facade_credentials?.type !== "basic"
               ? undefined
               : !state.credit_facade_credentials.password
-                ? i18n.str`required`
+                ? i18n.str`Required`
                 : undefined,
         }),
-  };
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
@@ -281,7 +279,7 @@ export function UpdatePage({
                 <Input<Entity>
                   name="credit_facade_url"
                   label={i18n.str`Endpoint URL`}
-                  
help="https://bank.demo.taler.net/accounts/_username_/taler-revenue/";
+                  
help="https://bank.demo.taler.net/accounts/${USERNAME}/taler-revenue/";
                   expand
                   tooltip={i18n.str`From where the merchant can download 
information about incoming wire transfers to this account`}
                 />
@@ -352,7 +350,7 @@ export function UpdatePage({
                   data-tooltip={
                     hasErrors
                       ? i18n.str`Need to complete marked fields`
-                      : "confirm operation"
+                      : i18n.str`Confirm operation`
                   }
                   onClick={submitForm}
                 >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
index e830b5463..aca8aeb8e 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -87,7 +87,7 @@ export default function UpdateValidator({
             .then((updated) => {
               if (updated.type === "fail") {
                 setNotif({
-                  message: i18n.str`could not update account`,
+                  message: i18n.str`Could not update account`,
                   type: "ERROR",
                   description: updated.detail.hint,
                 });
@@ -97,9 +97,9 @@ export default function UpdateValidator({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update account`,
+                message: i18n.str`Could not update account`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
@@ -111,17 +111,17 @@ export default function UpdateValidator({
             );
             if (created.type === "fail") {
               setNotif({
-                message: i18n.str`could not create account`,
+                message: i18n.str`Could not create account`,
                 type: "ERROR",
                 description: created.detail.hint,
               });
               return;
             }
-          } catch (error: any) {
+          } catch (error) {
             setNotif({
-              message: i18n.str`could not create account`,
+              message: i18n.str`Could not create account`,
               type: "ERROR",
-              description: error.message,
+              description: error instanceof Error ? error.message : 
String(error),
             });
             return;
           }
@@ -132,17 +132,17 @@ export default function UpdateValidator({
             );
             if (deleted.type === "fail") {
               setNotif({
-                message: i18n.str`could not delete account`,
+                message: i18n.str`Could not delete account`,
                 type: "ERROR",
                 description: deleted.detail.hint,
               });
               return;
             }
-          } catch (error: any) {
+          } catch (error) {
             setNotif({
-              message: i18n.str`could not delete account`,
+              message: i18n.str`Could not delete account`,
               type: "ERROR",
-              description: error.message,
+              description: error instanceof Error ? error.message : 
String(error),
             });
             return;
           }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
index 0b3bcd618..3adf79b04 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
@@ -147,15 +147,15 @@ export function CreatePage({
     ? undefined
     : Amounts.parse(value.pricing.order_price);
 
-  const errors: FormErrors<Entity> = {
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
     pricing: undefinedIfEmpty({
-      summary: !value.pricing?.summary ? i18n.str`required` : undefined,
+      summary: !value.pricing?.summary ? i18n.str`Required` : undefined,
       order_price: !value.pricing?.order_price
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : !parsedPrice
-          ? i18n.str`not valid`
+          ? i18n.str`Invalid`
           : Amounts.isZero(parsedPrice)
-            ? i18n.str`must be greater than 0`
+            ? i18n.str`Must be greater than 0`
             : undefined,
     }),
     payments: undefinedIfEmpty({
@@ -166,58 +166,59 @@ export function CreatePage({
               value.payments.refund_deadline,
               value.payments.pay_deadline,
             ) === -1
-          ? i18n.str`refund deadline cannot be before pay deadline`
+          ? i18n.str`Refund deadline can't be before pay deadline`
           : value.payments.wire_transfer_deadline &&
               Duration.cmp(
                 value.payments.wire_transfer_deadline,
                 value.payments.refund_deadline,
               ) === -1
-            ? i18n.str`wire transfer deadline cannot be before refund deadline`
+            ? i18n.str`Wire transfer deadline can't be before refund deadline`
             : undefined,
       pay_deadline: !value.payments?.pay_deadline
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : value.payments.wire_transfer_deadline &&
             Duration.cmp(
               value.payments.wire_transfer_deadline,
               value.payments.pay_deadline,
             ) === -1
-          ? i18n.str`wire transfer deadline cannot be before pay deadline`
+          ? i18n.str`Wire transfer deadline can't be before pay deadline`
           : undefined,
       wire_transfer_deadline: !value.payments?.wire_transfer_deadline
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : undefined,
       auto_refund_deadline: !value.payments?.auto_refund_deadline
         ? undefined
         : !value.payments?.refund_deadline
-          ? i18n.str`should have a refund deadline`
+          ? i18n.str`Must have a refund deadline`
           : Duration.cmp(
                 value.payments.refund_deadline,
                 value.payments.auto_refund_deadline,
               ) == -1
-            ? i18n.str`auto refund cannot be after refund deadline`
+            ? i18n.str`Auto refund can't be after refund deadline`
             : undefined,
     }),
     shipping: undefinedIfEmpty({
       delivery_date: !value.shipping?.delivery_date
         ? undefined
         : !isFuture(value.shipping.delivery_date)
-          ? i18n.str`should be in the future`
+          ? i18n.str`Must be in the future`
           : undefined,
     }),
-  };
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  });
+  const hasErrors = errors !== undefined;
 
   const submit = (): void => {
-    const order = value as any; //schema.cast(value);
+    const order = value;
+    const price = order.pricing?.order_price as AmountString | undefined;
+    const summary = order.pricing?.summary;
     if (!value.payments) return;
     if (!value.shipping) return;
+    if (!price || ! summary) return;
 
     const request: TalerMerchantApi.PostOrderRequest = {
       order: {
-        amount: order.pricing.order_price,
-        summary: order.pricing.summary,
+        amount: price,
+        summary: summary,
         products: productList,
         extra: undefinedIfEmpty(value.extra),
         pay_deadline: AbsoluteTime.toProtocolTimestamp(
@@ -453,7 +454,7 @@ export function CreatePage({
                     name="pricing.products_price"
                     label={i18n.str`Total price`}
                     readonly
-                    tooltip={i18n.str`total product price added up`}
+                    tooltip={i18n.str`Total product price added up`}
                   />
                   <InputCurrency
                     name="pricing.order_price"
@@ -473,7 +474,7 @@ export function CreatePage({
                 <InputCurrency
                   name="pricing.order_price"
                   label={i18n.str`Order price`}
-                  tooltip={i18n.str`final order price`}
+                  tooltip={i18n.str`Final order price`}
                 />
               )}
 
@@ -499,7 +500,7 @@ export function CreatePage({
                     <InputGroup
                       name="shipping.delivery_location"
                       label={i18n.str`Location`}
-                      tooltip={i18n.str`address where the products will be 
delivered`}
+                      tooltip={i18n.str`Address where the products will be 
delivered`}
                     >
                       <InputLocation name="shipping.delivery_location" />
                     </InputGroup>
@@ -544,7 +545,7 @@ export function CreatePage({
                               valueHandler(c);
                             }}
                           >
-                            <i18n.Translate>default</i18n.Translate>
+                            <i18n.Translate>Default</i18n.Translate>
                           </button>
                         </span>
                       }
@@ -577,7 +578,7 @@ export function CreatePage({
                               });
                             }}
                           >
-                            <i18n.Translate>default</i18n.Translate>
+                            <i18n.Translate>Default</i18n.Translate>
                           </button>
                         </span>
                       }
@@ -611,7 +612,7 @@ export function CreatePage({
                               });
                             }}
                           >
-                            <i18n.Translate>default</i18n.Translate>
+                            <i18n.Translate>Default</i18n.Translate>
                           </button>
                         </span>
                       }
@@ -733,7 +734,7 @@ export function CreatePage({
                         e.preventDefault();
                       }}
                     >
-                      <i18n.Translate>add</i18n.Translate>
+                      <i18n.Translate>Add</i18n.Translate>
                     </button>
                   </div>
                 </InputGroup>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
index 49333a0ae..147c9e99c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
@@ -103,7 +103,7 @@ export default function OrderCreate({ onConfirm, onBack }: 
Props): VNode {
                 return onConfirm(r.body.order_id);
               } else {
                 setNotif({
-                  message: "could not create order",
+                  message: i18n.str`Could not create order`,
                   type: "ERROR",
                   description:
                     r.case === HttpStatusCode.Gone
@@ -114,9 +114,9 @@ export default function OrderCreate({ onConfirm, onBack }: 
Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: "could not create order",
+                message: i18n.str`Could not create order`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
index 498ea83e3..2ffb4203d 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
@@ -75,13 +75,13 @@ function ContractTerms({ value }: { value: CT }) {
           readonly
           name="summary"
           label={i18n.str`Summary`}
-          tooltip={i18n.str`human-readable description of the whole purchase`}
+          tooltip={i18n.str`Human-readable description of the whole purchase`}
         />
         <InputCurrency<CT>
           readonly
           name="amount"
           label={i18n.str`Amount`}
-          tooltip={i18n.str`total price for the transaction`}
+          tooltip={i18n.str`Total price for the transaction`}
         />
         {value.fulfillment_url && (
           <Input<CT>
@@ -95,43 +95,43 @@ function ContractTerms({ value }: { value: CT }) {
           readonly
           name="max_fee"
           label={i18n.str`Max fee`}
-          tooltip={i18n.str`maximum total deposit fee accepted by the merchant 
for this contract`}
+          tooltip={i18n.str`Maximum total deposit fee accepted by the merchant 
for this contract`}
         />
         <InputDate<CT>
           readonly
           name="timestamp"
           label={i18n.str`Created at`}
-          tooltip={i18n.str`time when this contract was generated`}
+          tooltip={i18n.str`Time when this contract was generated`}
         />
         <InputDate<CT>
           readonly
           name="refund_deadline"
           label={i18n.str`Refund deadline`}
-          tooltip={i18n.str`after this deadline has passed no refunds will be 
accepted`}
+          tooltip={i18n.str`After this deadline has passed no refunds will be 
accepted`}
         />
         <InputDate<CT>
           readonly
           name="pay_deadline"
           label={i18n.str`Payment deadline`}
-          tooltip={i18n.str`after this deadline, the merchant won't accept 
payments for the contract`}
+          tooltip={i18n.str`After this deadline, the merchant won't accept 
payments for the contract`}
         />
         <InputDate<CT>
           readonly
           name="wire_transfer_deadline"
           label={i18n.str`Wire transfer deadline`}
-          tooltip={i18n.str`transfer deadline for the exchange`}
+          tooltip={i18n.str`Transfer deadline for the exchange`}
         />
         <InputDate<CT>
           readonly
           name="delivery_date"
           label={i18n.str`Delivery date`}
-          tooltip={i18n.str`time indicating when the order should be 
delivered`}
+          tooltip={i18n.str`Time indicating when the order should be 
delivered`}
         />
         {value.delivery_date && (
           <InputGroup
             name="delivery_location"
             label={i18n.str`Location`}
-            tooltip={i18n.str`where the order will be delivered`}
+            tooltip={i18n.str`Where the order will be delivered`}
           >
             <InputLocation name="payments.delivery_location" />
           </InputGroup>
@@ -140,13 +140,13 @@ function ContractTerms({ value }: { value: CT }) {
           readonly
           name="auto_refund"
           label={i18n.str`Auto-refund delay`}
-          tooltip={i18n.str`how long the wallet should try to get an automatic 
refund for the purchase`}
+          tooltip={i18n.str`How long the wallet should try to get an automatic 
refund for the purchase`}
         />
         <Input<CT>
           readonly
           name="extra"
           label={i18n.str`Extra info`}
-          tooltip={i18n.str`extra data that is only interpreted by the 
merchant frontend`}
+          tooltip={i18n.str`Extra data that is only interpreted by the 
merchant frontend`}
         />
       </FormProvider>
     </InputGroup>
@@ -221,7 +221,7 @@ function ClaimedPage({
                     <div class="level-item">
                       <i18n.Translate>Order</i18n.Translate> #{id}
                       <div class="tag is-info ml-4">
-                        <i18n.Translate>claimed</i18n.Translate>
+                        <i18n.Translate>Claimed</i18n.Translate>
                       </div>
                     </div>
                   </div>
@@ -248,7 +248,7 @@ function ClaimedPage({
                       >
                         <p>
                           <b>
-                            <i18n.Translate>claimed at</i18n.Translate>:
+                            <i18n.Translate>Claimed at</i18n.Translate>:
                           </b>{" "}
                           {order.contract_terms.timestamp.t_s === "never"
                             ? "never"
@@ -458,16 +458,16 @@ function PaidPage({
                     <div class="level-item">
                       <i18n.Translate>Order</i18n.Translate> #{id}
                       <div class="tag is-success ml-4">
-                        <i18n.Translate>paid</i18n.Translate>
+                        <i18n.Translate>Paid</i18n.Translate>
                       </div>
                       {order.wired ? (
                         <div class="tag is-success ml-4">
-                          <i18n.Translate>wired</i18n.Translate>
+                          <i18n.Translate>Wired</i18n.Translate>
                         </div>
                       ) : null}
                       {order.refunded ? (
                         <div class="tag is-danger ml-4">
-                          <i18n.Translate>refunded</i18n.Translate>
+                          <i18n.Translate>Refunded</i18n.Translate>
                         </div>
                       ) : null}
                     </div>
@@ -487,8 +487,8 @@ function PaidPage({
                             class="has-tooltip-left"
                             data-tooltip={
                               refundable
-                                ? i18n.str`refund order`
-                                : i18n.str`not refundable`
+                                ? i18n.str`Refund order`
+                                : i18n.str`Not refundable`
                             }
                           >
                             <button
@@ -496,7 +496,7 @@ function PaidPage({
                               disabled={!refundable}
                               onClick={() => onRefund(id)}
                             >
-                              <i18n.Translate>refund</i18n.Translate>
+                              <i18n.Translate>Refund</i18n.Translate>
                             </button>
                           </span>
                         </div>
@@ -638,7 +638,7 @@ function UnpaidPage({
                 </h1>
               </div>
               <div class="tag is-dark">
-                <i18n.Translate>unpaid</i18n.Translate>
+                <i18n.Translate>Unpaid</i18n.Translate>
               </div>
             </div>
           </div>
@@ -656,7 +656,7 @@ function UnpaidPage({
                 >
                   <p>
                     <b>
-                      <i18n.Translate>pay at</i18n.Translate>:
+                      <i18n.Translate>Pay at</i18n.Translate>:
                     </b>{" "}
                     <a
                       href={order.order_status_url}
@@ -668,7 +668,7 @@ function UnpaidPage({
                   </p>
                   <p>
                     <b>
-                      <i18n.Translate>created at</i18n.Translate>:
+                      <i18n.Translate>Created at</i18n.Translate>:
                     </b>{" "}
                     {order.creation_time.t_s === "never"
                       ? "never"
@@ -693,13 +693,13 @@ function UnpaidPage({
                 readonly
                 name="summary"
                 label={i18n.str`Summary`}
-                tooltip={i18n.str`human-readable description of the whole 
purchase`}
+                tooltip={i18n.str`Human-readable description of the whole 
purchase`}
               />
               <InputCurrency<Unpaid>
                 readonly
                 name="total_amount"
                 label={i18n.str`Amount`}
-                tooltip={i18n.str`total price for the transaction`}
+                tooltip={i18n.str`Total price for the transaction`}
               />
               <Input<Unpaid>
                 name="order_status"
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx
index 625ed5d9c..7d5acd07f 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx
@@ -85,12 +85,12 @@ export default function Update({ oid, onBack }: Props): 
VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`refund created successfully`,
+                  message: i18n.str`Refund created successfully`,
                   type: "SUCCESS",
                 });
               } else {
                 setNotif({
-                  message: i18n.str`could not create the refund`,
+                  message: i18n.str`Could not create the refund`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -98,9 +98,9 @@ export default function Update({ oid, onBack }: Props): VNode 
{
             })
             .catch((error) =>
               setNotif({
-                message: i18n.str`could not create the refund`,
+                message: i18n.str`Could not create the refund`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               }),
             );
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
index 4681f9943..fd1c9fa30 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
@@ -80,7 +80,7 @@ export function ListPage({
   isWiredActive,
 }: ListPageProps): VNode {
   const { i18n } = useTranslationContext();
-  const dateTooltip = i18n.str`select date to show nearby orders`;
+  const dateTooltip = i18n.str`Select date to show nearby orders`;
   const [pickDate, setPickDate] = useState(false);
   const [settings] = usePreference();
 
@@ -93,7 +93,7 @@ export function ListPage({
               <li class={isNotPaidActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n.str`only show paid orders`}
+                  data-tooltip={i18n.str`Only show paid orders`}
                 >
                   <a onClick={onShowNotPaid}>
                     <i18n.Translate>New</i18n.Translate>
@@ -103,7 +103,7 @@ export function ListPage({
               <li class={isPaidActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n.str`only show paid orders`}
+                  data-tooltip={i18n.str`Only show paid orders`}
                 >
                   <a onClick={onShowPaid}>
                     <i18n.Translate>Paid</i18n.Translate>
@@ -113,7 +113,7 @@ export function ListPage({
               <li class={isRefundedActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n.str`only show orders with refunds`}
+                  data-tooltip={i18n.str`Only show orders with refunds`}
                 >
                   <a onClick={onShowRefunded}>
                     <i18n.Translate>Refunded</i18n.Translate>
@@ -123,7 +123,7 @@ export function ListPage({
               <li class={isNotWiredActive}>
                 <div
                   class="has-tooltip-left"
-                  data-tooltip={i18n.str`only show orders where customers 
paid, but wire payments from payment provider are still pending`}
+                  data-tooltip={i18n.str`Only show orders where customers 
paid, but wire payments from payment provider are still pending`}
                 >
                   <a onClick={onShowNotWired}>
                     <i18n.Translate>Not wired</i18n.Translate>
@@ -133,7 +133,7 @@ export function ListPage({
               <li class={isWiredActive}>
                 <div
                   class="has-tooltip-left"
-                  data-tooltip={i18n.str`only show orders where customers 
paid, but wire payments from payment provider are still pending`}
+                  data-tooltip={i18n.str`Only show orders where customers 
paid, but wire payments from payment provider are still pending`}
                 >
                   <a onClick={onShowWired}>
                     <i18n.Translate>Completed</i18n.Translate>
@@ -143,7 +143,7 @@ export function ListPage({
               <li class={isAllActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n.str`remove all filters`}
+                  data-tooltip={i18n.str`Remove all filters`}
                 >
                   <a onClick={onShowAll}>
                     <i18n.Translate>All</i18n.Translate>
@@ -161,7 +161,7 @@ export function ListPage({
                   <a class="button is-fullwidth" onClick={() => 
onSelectDate(undefined)}>
                     <span
                       class="icon"
-                      data-tooltip={i18n.str`clear date filter`}
+                      data-tooltip={i18n.str`Clear date filter`}
                     >
                       <i class="mdi mdi-close" />
                     </span>
@@ -175,7 +175,7 @@ export function ListPage({
                     type="text"
                     readonly
                     value={!jumpToDate || jumpToDate.t_ms === "never" ? "" : 
format(jumpToDate.t_ms, dateFormatForSettings(settings))}
-                    placeholder={i18n.str`date 
(${dateFormatForSettings(settings)})`}
+                    placeholder={i18n.str`Jump to date 
(${dateFormatForSettings(settings)})`}
                     onClick={() => {
                       setPickDate(true);
                     }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
index 7b9691fdd..74bfe7939 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
@@ -80,7 +80,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options" />
 
         <div class="card-header-icon" aria-label="more options">
-          <span class="has-tooltip-left" data-tooltip={i18n.str`create order`}>
+          <span class="has-tooltip-left" data-tooltip={i18n.str`Create order`}>
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
                 <i class="mdi mdi-plus mdi-36px" />
@@ -137,7 +137,7 @@ function Table({
     <div class="table-container">
       {onLoadMoreBefore && (
         <button class="button is-fullwidth" onClick={onLoadMoreBefore}>
-          <i18n.Translate>load first page</i18n.Translate>
+          <i18n.Translate>Load first page</i18n.Translate>
         </button>
       )}
       <table class="table is-striped is-hoverable is-fullwidth">
@@ -211,9 +211,9 @@ function Table({
       </table>
       {onLoadMoreAfter && (
         <button class="button is-fullwidth" 
-        data-tooltip={i18n.str`load more orders after the last one`}
+        data-tooltip={i18n.str`Load more orders after the last one`}
         onClick={onLoadMoreAfter}>
-          <i18n.Translate>load next page</i18n.Translate>
+          <i18n.Translate>Load next page</i18n.Translate>
         </button>
       )}
     </div>
@@ -277,20 +277,20 @@ export function RefundModal({
       : orderPrice;
 
   const isRefundable = Amounts.isNonZero(totalRefundable);
-  const duplicatedText = i18n.str`duplicated`;
+  const duplicatedText = i18n.str`Duplicated`;
 
   const errors: FormErrors<State> = {
-    mainReason: !form.mainReason ? i18n.str`required` : undefined,
+    mainReason: !form.mainReason ? i18n.str`Required` : undefined,
     description:
       !form.description && form.mainReason !== duplicatedText
-        ? i18n.str`required`
+        ? i18n.str`Required`
         : undefined,
     refund: !form.refund
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !Amounts.parse(form.refund)
-        ? i18n.str`invalid format`
+        ? i18n.str`Invalid`
         : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
-          ? i18n.str`this value exceed the refundable amount`
+          ? i18n.str`This value exceed the refundable amount`
           : undefined,
   };
   const hasErrors = Object.keys(errors).some(
@@ -335,13 +335,13 @@ export function RefundModal({
                 <thead>
                   <tr>
                     <th>
-                      <i18n.Translate>date</i18n.Translate>
+                      <i18n.Translate>Date</i18n.Translate>
                     </th>
                     <th>
-                      <i18n.Translate>amount</i18n.Translate>
+                      <i18n.Translate>Amount</i18n.Translate>
                     </th>
                     <th>
-                      <i18n.Translate>reason</i18n.Translate>
+                      <i18n.Translate>Reason</i18n.Translate>
                     </th>
                   </tr>
                 </thead>
@@ -378,7 +378,7 @@ export function RefundModal({
           <InputCurrency<State>
             name="refund"
             label={i18n.str`Refund`}
-            tooltip={i18n.str`amount to be refunded`}
+            tooltip={i18n.str`Amount to be refunded`}
           >
             <i18n.Translate>Max refundable:</i18n.Translate>{" "}
             {Amounts.stringify(totalRefundable)}
@@ -389,16 +389,16 @@ export function RefundModal({
             values={[
               i18n.str`Choose one...`,
               duplicatedText,
-              i18n.str`requested by the customer`,
-              i18n.str`other`,
+              i18n.str`Requested by the customer`,
+              i18n.str`Other`,
             ]}
-            tooltip={i18n.str`why this order is being refunded`}
+            tooltip={i18n.str`Why this order is being refunded`}
           />
           {form.mainReason && form.mainReason !== duplicatedText ? (
             <Input<State>
               label={i18n.str`Description`}
               name="description"
-              tooltip={i18n.str`more information to give context`}
+              tooltip={i18n.str`More information to give context`}
             />
           ) : undefined}
         </FormProvider>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
index d0d7a9ef1..3d1892b47 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
@@ -110,8 +110,8 @@ export default function OrderList({ onCreate, onSelect }: 
Props): VNode {
           return resp.type === "ok";
         }}
         onSelect={onSelect}
-        description={i18n.str`jump to order with the given product ID`}
-        placeholder={i18n.str`order id`}
+        description={i18n.str`Jump to order with the given product ID`}
+        placeholder={i18n.str`Order id`}
       />
 
       <ListPage
@@ -160,12 +160,12 @@ export default function OrderList({ onCreate, onSelect }: 
Props): VNode {
               .then((resp) => {
                 if (resp.type === "ok") {
                   setNotif({
-                    message: i18n.str`refund created successfully`,
+                    message: i18n.str`Refund created successfully`,
                     type: "SUCCESS",
                   });
                 } else {
                   setNotif({
-                    message: i18n.str`could not create the refund`,
+                    message: i18n.str`Could not create the refund`,
                     type: "ERROR",
                     description: resp.detail.hint,
                   });
@@ -173,9 +173,9 @@ export default function OrderList({ onCreate, onSelect }: 
Props): VNode {
               })
               .catch((error) =>
                 setNotif({
-                  message: i18n.str`could not create the refund`,
+                  message: i18n.str`Could not create the refund`,
                   type: "ERROR",
-                  description: error.message,
+                  description: error instanceof Error ? error.message : 
String(error),
                 }),
               )
               .then(() => setOrderToBeRefunded(undefined));
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
index a16817bab..bd024fb38 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
@@ -35,6 +35,7 @@ import {
 import { Input } from "../../../../components/form/Input.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = TalerMerchantApi.OtpDeviceAddDetails;
 
@@ -53,34 +54,32 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
   const [showKey, setShowKey] = useState(false);
 
-  const errors: FormErrors<Entity> = {
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
     otp_device_id: !state.otp_device_id
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !/[a-zA-Z0-9]*/.test(state.otp_device_id)
-        ? i18n.str`no valid. only characters and numbers`
+        ? i18n.str`Invalid. Only characters and numbers`
         : undefined,
-    otp_algorithm: !state.otp_algorithm ? i18n.str`required` : undefined,
+    otp_algorithm: !state.otp_algorithm ? i18n.str`Required` : undefined,
     otp_key: !state.otp_key
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !isRfc3548Base32Charset(state.otp_key)
-        ? i18n.str`just letters and numbers from 2 to 7`
+        ? i18n.str`Just letters and numbers from 2 to 7`
         : state.otp_key.length !== 32
-          ? i18n.str`size of the key should be 32`
+          ? i18n.str`Size of the key must be 32`
           : undefined,
     otp_device_description: !state.otp_device_description
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !/[a-zA-Z0-9]*/.test(state.otp_device_description)
-        ? i18n.str`no valid. only characters and numbers`
+        ? i18n.str`Invalid. Only characters and numbers`
         : undefined,
-  };
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
-    return onCreate(state as any);
+    return onCreate(state as TalerMerchantApi.OtpDeviceAddDetails);
   };
 
   return (
@@ -112,14 +111,14 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 toStr={(v) => algorithmsNames[v]}
                 fromStr={(v) => Number(v)}
               />
-              {state.otp_algorithm  ? (
+              {state.otp_algorithm ? (
                 <Fragment>
                   <InputWithAddon<Entity>
                     expand
                     name="otp_key"
                     label={i18n.str`Device key`}
                     inputType={showKey ? "text" : "password"}
-                    help="Be sure to be very hard to guess or use the random 
generator"
+                    help={i18n.str`Be sure to be very hard to guess or use the 
random generator`}
                     tooltip={i18n.str`Your device need to have exactly the 
same value`}
                     fromStr={(v) => v.toUpperCase()}
                     addonAfterAction={() => {
@@ -136,7 +135,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                     }
                     side={
                       <button
-                        data-tooltip={i18n.str`generate random secret key`}
+                        data-tooltip={i18n.str`Generate random secret key`}
                         class="button is-info mr-3"
                         onClick={(e) => {
                           setState((s) => ({
@@ -146,7 +145,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                           e.preventDefault();
                         }}
                       >
-                        <i18n.Translate>random</i18n.Translate>
+                        <i18n.Translate>Random</i18n.Translate>
                       </button>
                     }
                   />
@@ -165,7 +164,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
                 onClick={submitForm}
               >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
index 8dd2890e3..adb417a09 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
@@ -57,13 +57,13 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`device added successfully`,
+                  message: i18n.str`Device added successfully`,
                   type: "SUCCESS",
                 });
                 setCreated(request);
               } else {
                 setNotif({
-                  message: i18n.str`could not add device`,
+                  message: i18n.str`Could not add device`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -71,9 +71,9 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not add device`,
+                message: i18n.str`Could not add device`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
index e4206ff7d..26074f57e 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
@@ -59,7 +59,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new devices`}
+            data-tooltip={i18n.str`Add new devices`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -114,10 +114,10 @@ function Table({
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more devices before the first one`}
+          data-tooltip={i18n.str`Load more devices before the first one`}
           onClick={onLoadMoreBefore}
         >
-          <i18n.Translate>load first page</i18n.Translate>
+          <i18n.Translate>Load first page</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -152,7 +152,7 @@ function Table({
                   <div class="buttons is-right">
                     <button
                       class="button is-danger is-small has-tooltip-left"
-                      data-tooltip={i18n.str`delete selected devices from the 
database`}
+                      data-tooltip={i18n.str`Delete selected devices from the 
database`}
                       onClick={() => onDelete(i)}
                     >
                       Delete
@@ -167,10 +167,10 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more devices after the last one`}
+          data-tooltip={i18n.str`Load more devices after the last one`}
           onClick={onLoadMoreAfter}
         >
-          <i18n.Translate>load next page</i18n.Translate>
+          <i18n.Translate>Load next page</i18n.Translate>
         </button>
       )}
     </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
index 0178beeb9..d6f2bfe4d 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
@@ -87,12 +87,12 @@ export default function ListOtpDevices({ onCreate, onSelect 
}: Props): VNode {
               .then((resp) => {
                 if (resp.type === "ok") {
                   setNotif({
-                    message: i18n.str`device delete successfully`,
+                    message: i18n.str`Device delete successfully`,
                     type: "SUCCESS",
                   });
                 } else {
                   setNotif({
-                    message: i18n.str`could not delete the device`,
+                    message: i18n.str`Could not delete the device`,
                     type: "ERROR",
                     description: resp.detail.hint,
                   });
@@ -100,9 +100,9 @@ export default function ListOtpDevices({ onCreate, onSelect 
}: Props): VNode {
               })
               .catch((error) =>
                 setNotif({
-                  message: i18n.str`could not delete the device`,
+                  message: i18n.str`Could not delete the device`,
                   type: "ERROR",
-                  description: error.message,
+                  description: error instanceof Error ? error.message : 
String(error),
                 }),
               );
           }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
index 7866b9cd9..68fbf371a 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
@@ -19,15 +19,15 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { randomRfc3548Base32Key, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import {
+  randomRfc3548Base32Key,
+  TalerMerchantApi,
+} from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
-import {
-  FormErrors,
-  FormProvider,
-} from "../../../../components/form/FormProvider.js";
+import { FormProvider } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
@@ -48,15 +48,8 @@ export function UpdatePage({ device, onUpdate, onBack }: 
Props): VNode {
   const [state, setState] = useState<Partial<Entity>>(device);
   const [showKey, setShowKey] = useState(false);
 
-  const errors: FormErrors<Entity> = {};
-
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
-
   const submitForm = () => {
-    if (hasErrors) return Promise.reject();
-    return onUpdate(state as any);
+    return onUpdate(state as Entity);
   };
 
   return (
@@ -68,7 +61,8 @@ export function UpdatePage({ device, onUpdate, onBack }: 
Props): VNode {
               <div class="level-left">
                 <div class="level-item">
                   <span class="is-size-4">
-                    Device: <b>{device.id}</b>
+                    <i18n.Translate>Device:</i18n.Translate>
+                    <b>{device.id}</b>
                   </span>
                 </div>
               </div>
@@ -80,11 +74,7 @@ export function UpdatePage({ device, onUpdate, onBack }: 
Props): VNode {
         <section class="section is-main-section">
           <div class="columns">
             <div class="column is-four-fifths">
-              <FormProvider
-                object={state}
-                valueHandler={setState}
-                errors={errors}
-              >
+              <FormProvider object={state} valueHandler={setState}>
                 <Input<Entity>
                   name="otp_device_description"
                   label={i18n.str`Description`}
@@ -107,8 +97,8 @@ export function UpdatePage({ device, onUpdate, onBack }: 
Props): VNode {
                       inputType={showKey ? "text" : "password"}
                       help={
                         state.otp_key === undefined
-                          ? "Not modified"
-                          : "Be sure to be very hard to guess or use the 
random generator"
+                          ? i18n.str`Not modified`
+                          : i18n.str`Be sure to be very hard to guess or use 
the random generator`
                       }
                       tooltip={i18n.str`Your device need to have exactly the 
same value`}
                       fromStr={(v) => v.toUpperCase()}
@@ -132,25 +122,25 @@ export function UpdatePage({ device, onUpdate, onBack }: 
Props): VNode {
                       side={
                         state.otp_key === undefined ? (
                           <button
-                            onClick={(e) => {
+                            onClick={() => {
                               setState((s) => ({ ...s, otp_key: "" }));
                             }}
                             class="button"
                           >
-                            change key
+                            <i18n.Translate>Change key</i18n.Translate>
                           </button>
                         ) : (
                           <button
-                            data-tooltip={i18n.str`generate random secret key`}
+                            data-tooltip={i18n.str`Generate random secret key`}
                             class="button is-info mr-3"
-                            onClick={(e) => {
+                            onClick={() => {
                               setState((s) => ({
                                 ...s,
                                 otp_key: randomRfc3548Base32Key(),
                               }));
                             }}
                           >
-                            <i18n.Translate>random</i18n.Translate>
+                            <i18n.Translate>Random</i18n.Translate>
                           </button>
                         )
                       }
@@ -166,12 +156,8 @@ export function UpdatePage({ device, onUpdate, onBack }: 
Props): VNode {
                   </button>
                 )}
                 <AsyncButton
-                  disabled={hasErrors}
-                  data-tooltip={
-                    hasErrors
-                      ? i18n.str`Need to complete marked fields`
-                      : "confirm operation"
-                  }
+                  disabled={false}
+                  data-tooltip={i18n.str`Confirm operation`}
                   onClick={submitForm}
                 >
                   <i18n.Translate>Confirm</i18n.Translate>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
index 3719d96ed..8f9997cfb 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
@@ -135,9 +135,9 @@ export default function UpdateValidator({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update template`,
+                message: i18n.str`Could not update template`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx
index 270cb3bb4..5352fd792 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx
@@ -49,13 +49,13 @@ export default function CreateProduct({ onConfirm, onBack 
}: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`product created successfully`,
+                  message: i18n.str`Product created successfully`,
                   type: "SUCCESS",
                 });
                 onConfirm();
               } else {
                 setNotif({
-                  message: i18n.str`could not create product`,
+                  message: i18n.str`Could not create product`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -63,9 +63,9 @@ export default function CreateProduct({ onConfirm, onBack }: 
Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not create product`,
+                message: i18n.str`Could not create product`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
index 08d42a8c9..a4e3663ef 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
@@ -75,7 +75,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add product to inventory`}
+            data-tooltip={i18n.str`Add product to inventory`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -138,7 +138,7 @@ function Table({
     <div class="table-container">
       {onLoadMoreBefore && (
         <button class="button is-fullwidth" onClick={onLoadMoreBefore}>
-          <i18n.Translate>load first page</i18n.Translate>
+          <i18n.Translate>Load first page</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -227,7 +227,7 @@ function Table({
                     }
                     style={{ cursor: "pointer" }}
                   >
-                    {isFree ? i18n.str`free` : `${i.price} / ${i.unit}`}
+                    {isFree ? i18n.str`Free` : `${i.price} / ${i.unit}`}
                   </td>
                   <td
                     onClick={() =>
@@ -268,7 +268,7 @@ function Table({
                     <div class="buttons is-right">
                       <span
                         class="has-tooltip-bottom"
-                        data-tooltip={i18n.str`go to product update page`}
+                        data-tooltip={i18n.str`Go to product update page`}
                       >
                         <button
                           class="button is-small is-success "
@@ -280,7 +280,7 @@ function Table({
                       </span>
                       <span
                         class="has-tooltip-left"
-                        data-tooltip={i18n.str`remove this product from the 
database`}
+                        data-tooltip={i18n.str`Remove this product from the 
database`}
                       >
                         <button
                           class="button is-small is-danger"
@@ -315,9 +315,9 @@ function Table({
       </table>
       {onLoadMoreAfter && (
         <button class="button is-fullwidth" 
-        data-tooltip={i18n.str`load more products after the last one`}
+        data-tooltip={i18n.str`Load more products after the last one`}
         onClick={onLoadMoreAfter}>
-          <i18n.Translate>load next page</i18n.Translate>
+          <i18n.Translate>Load next page</i18n.Translate>
         </button>
       )}
     </div>
@@ -358,7 +358,7 @@ function FastProductWithInfiniteStockUpdateForm({
         <InputCurrency<FastProductUpdate>
           name="price"
           label={i18n.str`Price`}
-          tooltip={i18n.str`update the product with new price`}
+          tooltip={i18n.str`Update the product with new price`}
         />
       </FormProvider>
 
@@ -370,7 +370,7 @@ function FastProductWithInfiniteStockUpdateForm({
           </button>
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`update product with new price`}
+            data-tooltip={i18n.str`Update product with new price`}
           >
             <button
               class="button is-info"
@@ -407,7 +407,7 @@ function FastProductWithManagedStockUpdateForm({
   const errors: FormErrors<FastProductUpdate> = {
     lost:
       currentStock + value.incoming < value.lost
-        ? `lost cannot be greater that current + incoming (max ${currentStock 
+ value.incoming
+        ? `lost can't be greater that current + incoming (max ${currentStock + 
value.incoming
         })`
         : undefined,
   };
@@ -428,17 +428,17 @@ function FastProductWithManagedStockUpdateForm({
         <InputNumber<FastProductUpdate>
           name="incoming"
           label={i18n.str`Incoming`}
-          tooltip={i18n.str`add more elements to the inventory`}
+          tooltip={i18n.str`Add more elements to the inventory`}
         />
         <InputNumber<FastProductUpdate>
           name="lost"
           label={i18n.str`Lost`}
-          tooltip={i18n.str`report elements lost in the inventory`}
+          tooltip={i18n.str`Report elements lost in the inventory`}
         />
         <InputCurrency<FastProductUpdate>
           name="price"
           label={i18n.str`Price`}
-          tooltip={i18n.str`new price for the product`}
+          tooltip={i18n.str`New price for the product`}
         />
       </FormProvider>
 
@@ -450,8 +450,8 @@ function FastProductWithManagedStockUpdateForm({
           class="has-tooltip-left"
           data-tooltip={
             hasErrors
-              ? i18n.str`the are value with errors`
-              : i18n.str`update product with new stock and price`
+              ? i18n.str`The are value with errors`
+              : i18n.str`Update product with new stock and price`
           }
         >
           <button
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
index 93e2bddcf..3b0fb7ed6 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
@@ -83,8 +83,8 @@ export default function ProductList({ onCreate, onSelect }: 
Props): VNode {
           return resp.type === "ok";
         }}
         onSelect={onSelect}
-        description={i18n.str`jump to product with the given product ID`}
-        placeholder={i18n.str`product id`}
+        description={i18n.str`Jump to product with the given product ID`}
+        placeholder={i18n.str`Product id`}
       />
 
       <CardTable
@@ -101,19 +101,19 @@ export default function ProductList({ onCreate, onSelect 
}: Props): VNode {
             );
             if (resp.type === "ok") {
               setNotif({
-                message: i18n.str`product updated successfully`,
+                message: i18n.str`Product updated successfully`,
                 type: "SUCCESS",
               });
             } else {
               setNotif({
-                message: i18n.str`could not update the product`,
+                message: i18n.str`Could not update the product`,
                 type: "ERROR",
                 description: resp.detail.hint,
               });
             }
           } catch (error) {
             setNotif({
-              message: i18n.str`could not update the product`,
+              message: i18n.str`Could not update the product`,
               type: "ERROR",
               description: error instanceof Error ? error.message : undefined,
             });
@@ -146,14 +146,14 @@ export default function ProductList({ onCreate, onSelect 
}: Props): VNode {
                 });
               } else {
                 setNotif({
-                  message: i18n.str`could not delete the product`,
+                  message: i18n.str`Could not delete the product`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
               }
             } catch (error) {
               setNotif({
-                message: i18n.str`could not delete the product`,
+                message: i18n.str`Could not delete the product`,
                 type: "ERROR",
                 description: error instanceof Error ? error.message : 
undefined,
               });
@@ -171,7 +171,7 @@ export default function ProductList({ onCreate, onSelect }: 
Props): VNode {
           </p>
           <p class="warning">
             <i18n.Translate>
-              Deleting an product cannot be undone.
+              Deleting an product can't be undone.
             </i18n.Translate>
           </p>
         </ConfirmModal>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx
index b394c2d30..06e140680 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx
@@ -87,7 +87,7 @@ export default function UpdateProduct({
                 onConfirm()
               } else {
                 setNotif({
-                  message: i18n.str`could not update product`,
+                  message: i18n.str`Could not update product`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -96,9 +96,9 @@ export default function UpdateProduct({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update product`,
+                message: i18n.str`Could not update product`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index 90fc52593..c20f8edf0 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -91,32 +91,32 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
   const errors: FormErrors<Entity> = {
     id: !state.id
-      ? i18n.str`should not be empty`
+      ? i18n.str`Required`
       : !/[a-zA-Z0-9]*/.test(state.id)
-        ? i18n.str`no valid. only characters and numbers`
+        ? i18n.str`Invalid. only characters and numbers`
         : undefined,
-    description: !state.description ? i18n.str`should not be empty` : 
undefined,
+    description: !state.description ? i18n.str`Required` : undefined,
     amount: !state.amount
       ? state.amount_editable
         ? undefined
-        : i18n.str`required`
+        : i18n.str`Required`
       : !parsedPrice
-        ? i18n.str`not valid`
+        ? i18n.str`Invalid`
         : Amounts.isZero(parsedPrice)
           ? state.amount_editable
             ? undefined
-            : i18n.str`must be greater than 0`
+            : i18n.str`Must be greater than 0`
           : undefined,
     minimum_age:
       state.minimum_age && state.minimum_age < 0
-        ? i18n.str`should be greater that 0`
+        ? i18n.str`Must be greater that 0`
         : undefined,
     pay_duration: !state.pay_duration
-      ? i18n.str`can't be empty`
+      ? i18n.str`Required`
       : state.pay_duration.d_ms === "forever"
         ? undefined
         : state.pay_duration.d_ms < 1000 //less than one second
-          ? i18n.str`to short`
+          ? i18n.str`To short`
           : undefined,
   };
 
@@ -231,7 +231,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                   />
                   <TextField name="sc" label={i18n.str`Supported currencies`}>
                     <i18n.Translate>
-                      supported currencies: {cList.join(", ")}
+                      Supported currencies: {cList.join(", ")}
                     </i18n.Translate>
                   </TextField>
                 </Fragment>
@@ -289,7 +289,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
                 onClick={submitForm}
               >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/index.tsx
index 52fda7d91..cc40591d4 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/index.tsx
@@ -49,13 +49,13 @@ export default function CreateTemplate({ onConfirm, onBack 
}: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`template has been created`,
+                  message: i18n.str`Template has been created`,
                   type: "SUCCESS",
                 });
                 onConfirm();
               } else {
                 setNotif({
-                  message: i18n.str`could not create template`,
+                  message: i18n.str`Could not create template`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -63,9 +63,9 @@ export default function CreateTemplate({ onConfirm, onBack }: 
Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not inform template`,
+                message: i18n.str`Could not create template`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
index 4c55bae2a..80c893049 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
@@ -63,7 +63,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new templates`}
+            data-tooltip={i18n.str`Add new templates`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -124,10 +124,10 @@ function Table({
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more templates before the first one`}
+          data-tooltip={i18n.str`Load more templates before the first one`}
           onClick={onLoadMoreBefore}
         >
-          <i18n.Translate>load first page</i18n.Translate>
+          <i18n.Translate>Load first page</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -162,21 +162,21 @@ function Table({
                   <div class="buttons is-right">
                     <button
                       class="button is-danger is-small has-tooltip-left"
-                      data-tooltip={i18n.str`delete selected templates from 
the database`}
+                      data-tooltip={i18n.str`Delete selected templates from 
the database`}
                       onClick={() => onDelete(i)}
                     >
-                      Delete
+                      <i18n.Translate>Delete</i18n.Translate>
                     </button>
                     <button
                       class="button is-info is-small has-tooltip-left"
-                      data-tooltip={i18n.str`use template to create new order`}
+                      data-tooltip={i18n.str`Use template to create new order`}
                       onClick={() => onNewOrder(i)}
                     >
-                      Use template
+                      <i18n.Translate>Use template</i18n.Translate>
                     </button>
                     <button
                       class="button is-info is-small has-tooltip-left"
-                      data-tooltip={i18n.str`create qr code for the template`}
+                      data-tooltip={i18n.str`Create qr code for the template`}
                       onClick={() => onQR(i)}
                     >
                       QR
@@ -191,10 +191,10 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more templates after the last one`}
+          data-tooltip={i18n.str`Load more templates after the last one`}
           onClick={onLoadMoreAfter}
         >
-          <i18n.Translate>load next page</i18n.Translate>
+          <i18n.Translate>Load next page</i18n.Translate>
         </button>
       )}
     </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index af9b004c6..ad4ed9363 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -88,8 +88,8 @@ export default function ListTemplates({
           return resp.type === "ok";
         }}
         onSelect={onSelect}
-        description={i18n.str`jump to template with the given template ID`}
-        placeholder={i18n.str`template id`}
+        description={i18n.str`Jump to template with the given template ID`}
+        placeholder={i18n.str`Template identification`}
       />
 
       <CardTable
@@ -159,7 +159,7 @@ export default function ListTemplates({
           <p class="warning">
             <i18n.Translate>Deleting an template </i18n.Translate>
             <b>
-              <i18n.Translate>cannot be undone</i18n.Translate>
+              <i18n.Translate>can't be undone</i18n.Translate>
             </b>
             .
           </p>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
index 3e83722da..93a21bd80 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
@@ -21,11 +21,9 @@
 
 import {
   TalerMerchantApi,
-  stringifyPayTemplateUri
+  stringifyPayTemplateUri,
 } from "@gnu-taler/taler-util";
-import {
-  useTranslationContext
-} from "@gnu-taler/web-util/browser";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { QR } from "../../../../components/exception/QR.js";
 import { useSessionContext } from "../../../../context/session.js";
@@ -56,7 +54,9 @@ export function QrPage({ id: templateId, onBack }: Props): 
VNode {
       <section id="printThis">
         <QR text={payTemplateUri} />
         <pre style={{ textAlign: "center" }}>
-          <a target="_blank" rel="noreferrer" 
href={payTemplateUri}>{payTemplateUri}</a>
+          <a target="_blank" rel="noreferrer" href={payTemplateUri}>
+            {payTemplateUri}
+          </a>
         </pre>
       </section>
 
@@ -64,35 +64,6 @@ export function QrPage({ id: templateId, onBack }: Props): 
VNode {
         <div class="columns">
           <div class="column" />
           <div class="column is-four-fifths">
-            {/* <p class="is-size-5 mt-5 mb-5">
-              <i18n.Translate>
-                Here you can specify a default value for fields that are not
-                fixed. Default values can be edited by the customer before the
-                payment.
-              </i18n.Translate>
-            </p> */}
-
-            <p></p>
-            {/* <FormProvider
-              object={state}
-              valueHandler={setState}
-              errors={errors}
-            >
-              <InputCurrency<Entity>
-                name="amount"
-                label={i18n.str`Amount`}
-                readonly
-                tooltip={i18n.str`Amount of the order`}
-              />
-              <Input<Entity>
-                name="summary"
-                inputType="multiline"
-                readonly
-                label={i18n.str`Summary`}
-                tooltip={i18n.str`Title of the order to be shown to the 
customer`}
-              />
-            </FormProvider> */}
-
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index 9e8e78dd4..37f2bf898 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
@@ -115,24 +115,24 @@ export function UpdatePage({ template, onUpdate, onBack 
}: Props): VNode {
   const parsedPrice = !state.amount ? undefined : Amounts.parse(state.amount);
 
   const errors: FormErrors<Entity> = {
-    description: !state.description ? i18n.str`should not be empty` : 
undefined,
+    description: !state.description ? i18n.str`Required` : undefined,
     amount: !state.amount
-      ? state.amount_editable ? undefined : i18n.str`required`
+      ? state.amount_editable ? undefined : i18n.str`Required`
       : !parsedPrice
-        ? i18n.str`not valid`
+        ? i18n.str`Invalid`
         : Amounts.isZero(parsedPrice)
-          ? state.amount_editable ? undefined : i18n.str`must be greater than 
0`
+          ? state.amount_editable ? undefined : i18n.str`Must be greater than 
0`
           : undefined,
     minimum_age:
       state.minimum_age && state.minimum_age < 0
-        ? i18n.str`should be greater that 0`
+        ? i18n.str`Must be greater that 0`
         : undefined,
     pay_duration: !state.pay_duration
-      ? i18n.str`can't be empty`
+      ? i18n.str`Required`
       : state.pay_duration.d_ms === "forever"
         ? undefined
         : state.pay_duration.d_ms < 1000 // less than one second
-          ? i18n.str`to short`
+          ? i18n.str`Too short`
           : undefined,
   };
 
@@ -236,7 +236,7 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
                     />
                     <TextField name="sc" label={i18n.str`Supported 
currencies`}>
                       <i18n.Translate>
-                        supported currencies: {cList.join(", ")}
+                        Supported currencies: {cList.join(", ")}
                       </i18n.Translate>
                     </TextField>
                   </Fragment>
@@ -294,7 +294,7 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
                   data-tooltip={
                     hasErrors
                       ? i18n.str`Need to complete marked fields`
-                      : "confirm operation"
+                      : i18n.str`Confirm operation`
                   }
                   onClick={submitForm}
                 >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
index ed6625b36..a42a40c63 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
@@ -93,7 +93,7 @@ export default function UpdateTemplate({
                 onConfirm();
               } else {
                 setNotif({
-                  message: i18n.str`could not update template`,
+                  message: i18n.str`Could not update template`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -101,9 +101,9 @@ export default function UpdateTemplate({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update template`,
+                message: i18n.str`Could not update template`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
index bfca98b77..ebdb1a6fa 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
@@ -88,7 +88,7 @@ export default function TemplateUsePage({
                 onOrderCreated(res.body.order_id)
               } else {
                 setNotif({
-                  message: i18n.str`could not create order from template`,
+                  message: i18n.str`Could not create order from template`,
                   type: "ERROR",
                   description:
                     res.case === HttpStatusCode.Gone
@@ -99,9 +99,9 @@ export default function TemplateUsePage({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not create order from template`,
+                message: i18n.str`Could not create order from template`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
index d718ffb69..be23299ff 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
@@ -28,6 +28,7 @@ import { Input } from "../../../components/form/Input.js";
 import { NotificationCard } from "../../../components/menu/index.js";
 import { useSessionContext } from "../../../context/session.js";
 import { AccessToken, createRFC8959AccessTokenPlain } from 
"@gnu-taler/taler-util";
+import { undefinedIfEmpty } from "../../../utils/table.js";
 
 interface Props {
   hasToken: boolean | undefined;
@@ -50,25 +51,23 @@ export function DetailPage({
   });
   const { i18n } = useTranslationContext();
 
-  const errors = {
+  const errors = undefinedIfEmpty({
     old_token:
       hasToken && !form.old_token
-        ? i18n.str`you need your access token to perform the operation`
+        ? i18n.str`You need your access token to perform the operation`
         : undefined,
     new_token: !form.new_token
-      ? i18n.str`cannot be empty`
+      ? i18n.str`Required`
       : form.new_token === form.old_token
-        ? i18n.str`cannot be the same as the old token`
+        ? i18n.str`Can't be the same as the old token`
         : undefined,
     repeat_token:
       form.new_token !== form.repeat_token
-        ? i18n.str`is not the same`
+        ? i18n.str`Is not the same`
         : undefined,
-  };
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as Record<string, unknown>)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const { state } = useSessionContext();
 
@@ -120,7 +119,7 @@ export function DetailPage({
                     <Input<State>
                       name="old_token"
                       label={i18n.str`Current access token`}
-                      tooltip={i18n.str`access token currently in use`}
+                      tooltip={i18n.str`Access token currently in use`}
                       inputType="password"
                     />
                     <p>
@@ -149,13 +148,13 @@ export function DetailPage({
                 <Input<State>
                   name="new_token"
                   label={i18n.str`New access token`}
-                  tooltip={i18n.str`next access token to be used`}
+                  tooltip={i18n.str`Next access token to be used`}
                   inputType="password"
                 />
                 <Input<State>
                   name="repeat_token"
                   label={i18n.str`Repeat access token`}
-                  tooltip={i18n.str`confirm the same access token`}
+                  tooltip={i18n.str`Confirm the same access token`}
                   inputType="password"
                 />
               </Fragment>
@@ -171,7 +170,7 @@ export function DetailPage({
                   data-tooltip={
                     hasErrors
                       ? i18n.str`Need to complete marked fields`
-                      : "confirm operation"
+                      : i18n.str`Confirm operation`
                   }
                   onClick={submitForm}
                 >
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
index ab2ed96a1..def8ec821 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
@@ -18,9 +18,7 @@ import {
   TalerError,
   assertUnreachable,
 } from "@gnu-taler/taler-util";
-import {
-  useTranslationContext
-} from "@gnu-taler/web-util/browser";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
@@ -88,13 +86,11 @@ export default function Token({ onChange, onCancel }: 
Props): VNode {
               });
             }
           } catch (error) {
-            if (error instanceof Error) {
-              return setNotif({
-                message: i18n.str`Failed to clear token`,
-                type: "ERROR",
-                description: error.message,
-              });
-            }
+            return setNotif({
+              message: i18n.str`Failed to clear token`,
+              type: "ERROR",
+              description: error instanceof Error ? error.message : 
String(error),
+            });
           }
         }}
         onNewToken={async (currentToken, newToken): Promise<void> => {
@@ -136,13 +132,11 @@ export default function Token({ onChange, onCancel }: 
Props): VNode {
               });
             }
           } catch (error) {
-            if (error instanceof Error) {
               return setNotif({
                 message: i18n.str`Failed to set new token`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
-            }
           }
         }}
       />
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/index.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/index.tsx
index 51e2fedb2..34361fd56 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/index.tsx
@@ -48,13 +48,13 @@ export default function CreateTokenFamily({ onConfirm, 
onBack }: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`token familty created successfully`,
+                  message: i18n.str`Token familty created successfully`,
                   type: "SUCCESS",
                 });
                 onConfirm();
               } else {
                 setNotif({
-                  message: i18n.str`could not create token family`,
+                  message: i18n.str`Could not create token family`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -62,9 +62,9 @@ export default function CreateTokenFamily({ onConfirm, onBack 
}: Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not create token family`,
+                message: i18n.str`Could not create token family`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/Table.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/Table.tsx
index be2ad9e8d..28823e8a1 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/Table.tsx
@@ -62,7 +62,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add token family`}
+            data-tooltip={i18n.str`Add token family`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -189,7 +189,7 @@ function Table({
                     <div class="buttons is-right">
                       <span
                         class="has-tooltip-bottom"
-                        data-tooltip={i18n.str`go to token family update page`}
+                        data-tooltip={i18n.str`Go to token family update page`}
                       >
                         <button
                           class="button is-small is-success "
@@ -201,7 +201,7 @@ function Table({
                       </span>
                       <span
                         class="has-tooltip-left"
-                        data-tooltip={i18n.str`remove this token family from 
the database`}
+                        data-tooltip={i18n.str`Remove this token family from 
the database`}
                       >
                         <button
                           class="button is-small is-danger"
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx
index 8a7be80a8..f01d0b0d7 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx
@@ -88,19 +88,19 @@ export default function TokenFamilyList({ onCreate, 
onSelect }: Props): VNode {
             );
             if (resp.type === "ok") {
               setNotif({
-                message: i18n.str`token family updated successfully`,
+                message: i18n.str`Token family updated successfully`,
                 type: "SUCCESS",
               });
             } else {
               setNotif({
-                message: i18n.str`could not update the token family`,
+                message: i18n.str`Could not update the token family`,
                 type: "ERROR",
                 description: resp.detail.hint,
               });
             }
           } catch (error) {
             setNotif({
-              message: i18n.str`could not update the token family`,
+              message: i18n.str`Could not update the token family`,
               type: "ERROR",
               description: error instanceof Error ? error.message : undefined,
             });
@@ -157,7 +157,7 @@ export default function TokenFamilyList({ onCreate, 
onSelect }: Props): VNode {
             <i18n.Translate>
               Deleting a token family{" "}
               <b>
-                <i18n.Translate>cannot be undone</i18n.Translate>
+                <i18n.Translate>can't be undone</i18n.Translate>
               </b>
               .
             </i18n.Translate>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx
index ab91baa36..382821f8c 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx
@@ -28,6 +28,7 @@ import { FormErrors, FormProvider } from 
"../../../../components/form/FormProvid
 import { Input } from "../../../../components/form/Input.js";
 import { InputDate } from "../../../../components/form/InputDate.js";
 import { InputDuration } from "../../../../components/form/InputDuration.js";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = Omit<TalerMerchantApi.TokenFamilyUpdateRequest, "duration"> & {
   duration: Duration,
@@ -51,17 +52,15 @@ function convert(from: 
TalerMerchantApi.TokenFamilyUpdateRequest) {
 export function UpdatePage({ onUpdate, onBack, tokenFamily }: Props) {
   const [value, valueHandler] = 
useState<Partial<Entity>>(convert(tokenFamily));
   const { i18n } = useTranslationContext();
-  const errors: FormErrors<Entity> = {
-    name: !value.name ? i18n.str`required` : undefined,
-    description: !value.description ? i18n.str`required` : undefined,
-    valid_after: !value.valid_after ? i18n.str`required` : undefined,
-    valid_before: !value.valid_before ? i18n.str`required` : undefined,
-    duration: !value.duration ? i18n.str`required` : undefined,
-  };
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
+    name: !value.name ? i18n.str`Required` : undefined,
+    description: !value.description ? i18n.str`Required` : undefined,
+    valid_after: !value.valid_after ? i18n.str`Required` : undefined,
+    valid_before: !value.valid_before ? i18n.str`Required` : undefined,
+    duration: !value.duration ? i18n.str`Required` : undefined,
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
@@ -107,30 +106,30 @@ export function UpdatePage({ onUpdate, onBack, 
tokenFamily }: Props) {
                   name="name"
                   inputType="text"
                   label={i18n.str`Name`}
-                  tooltip={i18n.str`user-readable token family name`}
+                  tooltip={i18n.str`User-readable token family name`}
                 />
                 <Input<Entity>
                   name="description"
                   inputType="multiline"
                   label={i18n.str`Description`}
-                  tooltip={i18n.str`token family description for customers`}
+                  tooltip={i18n.str`Token family description for customers`}
                 />
                 <InputDate<Entity>
                   name="valid_after"
                   label={i18n.str`Valid After`}
-                  tooltip={i18n.str`token family can issue tokens after this 
date`}
+                  tooltip={i18n.str`Token family can issue tokens after this 
date`}
                   withTimestampSupport
                 />
                 <InputDate<Entity>
                   name="valid_before"
                   label={i18n.str`Valid Before`}
-                  tooltip={i18n.str`token family can issue tokens until this 
date`}
+                  tooltip={i18n.str`Token family can issue tokens until this 
date`}
                   withTimestampSupport
                 />
                 <InputDuration<Entity>
                   name="duration"
                   label={i18n.str`Duration`}
-                  tooltip={i18n.str`validity duration of a issued token`}
+                  tooltip={i18n.str`Validity duration of a issued token`}
                   withForever
                 />
               </FormProvider>
@@ -146,7 +145,7 @@ export function UpdatePage({ onUpdate, onBack, tokenFamily 
}: Props) {
                   data-tooltip={
                     hasErrors
                       ? i18n.str`Need to complete marked fields`
-                      : "confirm operation"
+                      : i18n.str`Confirm operation`
                   }
                   onClick={submitForm}
                 >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/index.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/index.tsx
index fc1afd2d9..287cc7a51 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/index.tsx
@@ -103,7 +103,7 @@ export default function UpdateTokenFamily({
                 onConfirm();
               } else {
                 setNotif({
-                  message: i18n.str`could not update token family`,
+                  message: i18n.str`Could not update token family`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -111,9 +111,9 @@ export default function UpdateTokenFamily({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update token family`,
+                message: i18n.str`Could not update token family`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx
index 91aabe58e..d3a1d7832 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx
@@ -35,6 +35,7 @@ import {
   CROCKFORD_BASE32_REGEX,
   URL_REGEX,
 } from "../../../../utils/constants.js";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = TalerMerchantApi.TransferInformation;
 
@@ -54,26 +55,24 @@ export function CreatePage({ accounts, onCreate, onBack }: 
Props): VNode {
     credit_amount: `` as AmountString,
   });
 
-  const errors: FormErrors<Entity> = {
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
     wtid: !state.wtid
-      ? i18n.str`cannot be empty`
+      ? i18n.str`Required`
       : !CROCKFORD_BASE32_REGEX.test(state.wtid)
-      ? i18n.str`check the id, does not look valid`
-      : state.wtid.length !== 52
-      ? i18n.str`should have 52 characters, current ${state.wtid.length}`
-      : undefined,
-    payto_uri: !state.payto_uri ? i18n.str`cannot be empty` : undefined,
-    credit_amount: !state.credit_amount ? i18n.str`cannot be empty` : 
undefined,
+        ? i18n.str`Check the id, does not look valid`
+        : state.wtid.length !== 52
+          ? i18n.str`Must have 52 characters, current ${state.wtid.length}`
+          : undefined,
+    payto_uri: !state.payto_uri ? i18n.str`Required` : undefined,
+    credit_amount: !state.credit_amount ? i18n.str`Required` : undefined,
     exchange_url: !state.exchange_url
-      ? i18n.str`cannot be empty`
+      ? i18n.str`Required`
       : !URL_REGEX.test(state.exchange_url)
-      ? i18n.str`URL doesn't have the right format`
-      : undefined,
-  };
+        ? i18n.str`URL doesn't have the right format`
+        : undefined,
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
@@ -102,7 +101,7 @@ export function CreatePage({ accounts, onCreate, onBack }: 
Props): VNode {
                 name="wtid"
                 label={i18n.str`Wire transfer ID`}
                 help=""
-                tooltip={i18n.str`unique identifier of the wire transfer used 
by the exchange, must be 52 characters long`}
+                tooltip={i18n.str`Unique identifier of the wire transfer used 
by the exchange, must be 52 characters long`}
               />
               <Input<Entity>
                 name="exchange_url"
@@ -128,7 +127,7 @@ export function CreatePage({ accounts, onCreate, onBack }: 
Props): VNode {
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
                 onClick={submitForm}
               >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
index 1e63dc72d..2f295fcc8 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
@@ -59,13 +59,13 @@ export default function CreateTransfer({ onConfirm, onBack 
}: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`wire transfer informed successfully`,
+                  message: i18n.str`Wire transfer informed successfully`,
                   type: "SUCCESS",
                 });
                 onConfirm()
               } else {
                 setNotif({
-                  message: i18n.str`could not inform transfer`,
+                  message: i18n.str`Could not inform transfer`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -73,9 +73,9 @@ export default function CreateTransfer({ onConfirm, onBack }: 
Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not inform transfer`,
+                message: i18n.str`Could not inform transfer`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
index 927e36cf7..f80c0f53f 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
@@ -81,7 +81,7 @@ export function ListPage({
                 return d
               }}
               placeholder={i18n.str`All accounts`}
-              tooltip={i18n.str`filter by account address`}
+              tooltip={i18n.str`Filter by account address`}
             />
           </FormProvider>
         </div>
@@ -92,7 +92,7 @@ export function ListPage({
           <li class={isAllTransfers ? "is-active" : ""}>
             <div
               class="has-tooltip-right"
-              data-tooltip={i18n.str`remove all filters`}
+              data-tooltip={i18n.str`Remove all filters`}
             >
               <a onClick={onShowAll}>
                 <i18n.Translate>All</i18n.Translate>
@@ -102,7 +102,7 @@ export function ListPage({
           <li class={isVerifiedTransfers ? "is-active" : ""}>
             <div
               class="has-tooltip-right"
-              data-tooltip={i18n.str`only show wire transfers confirmed by the 
merchant`}
+              data-tooltip={i18n.str`Only show wire transfers confirmed by the 
merchant`}
             >
               <a onClick={onShowVerified}>
                 <i18n.Translate>Verified</i18n.Translate>
@@ -112,7 +112,7 @@ export function ListPage({
           <li class={isNonVerifiedTransfers ? "is-active" : ""}>
             <div
               class="has-tooltip-right"
-              data-tooltip={i18n.str`only show wire transfers claimed by the 
exchange`}
+              data-tooltip={i18n.str`Only show wire transfers claimed by the 
exchange`}
             >
               <a onClick={onShowUnverified}>
                 <i18n.Translate>Unverified</i18n.Translate>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
index 5687d5e57..29ff69f55 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
@@ -61,7 +61,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new transfer`}
+            data-tooltip={i18n.str`Add new transfer`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -114,10 +114,10 @@ function Table({
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more transfers before the first one`}
+          data-tooltip={i18n.str`Load more transfers before the first one`}
           onClick={onLoadMoreBefore}
         >
-          <i18n.Translate>load first page</i18n.Translate>
+          <i18n.Translate>Load first page</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -163,7 +163,7 @@ function Table({
                   {i.verified !== true ? (
                     <button
                       class="button is-danger is-small has-tooltip-left"
-                      data-tooltip={i18n.str`delete selected transfer from the 
database`}
+                      data-tooltip={i18n.str`Delete selected transfer from the 
database`}
                       onClick={() => onDelete(i)}
                     >
                       <i18n.Translate>Delete</i18n.Translate>
@@ -178,10 +178,10 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more transfers after the last one`}
+          data-tooltip={i18n.str`Load more transfers after the last one`}
           onClick={onLoadMoreAfter}
         >
-          <i18n.Translate>load next page</i18n.Translate>
+          <i18n.Translate>Load next page</i18n.Translate>
         </button>
       )}
     </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
index cde58967f..c7d38cf32 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
@@ -37,7 +37,6 @@ export type Entity = 
Omit<Omit<TalerMerchantApi.InstanceReconfigurationMessage,
   default_wire_transfer_delay: Duration,
 };
 
-//TalerMerchantApi.InstanceAuthConfigurationMessage
 interface Props {
   onUpdate: (d: TalerMerchantApi.InstanceReconfigurationMessage) => void;
   selected: TalerMerchantApi.QueryInstancesResponse;
@@ -69,40 +68,38 @@ export function UpdatePage({
 
   const { i18n } = useTranslationContext();
 
-  const errors: FormErrors<Entity> = {
-    name: !value.name ? i18n.str`required` : undefined,
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
+    name: !value.name ? i18n.str`Required` : undefined,
     user_type: !value.user_type
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : value.user_type !== "business" && value.user_type !== "individual"
-        ? i18n.str`should be business or individual`
+        ? i18n.str`Must be business or individual`
         : undefined,
     default_pay_delay: !value.default_pay_delay
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !!value.default_wire_transfer_delay &&
         value.default_wire_transfer_delay.d_ms !== "forever" &&
         value.default_pay_delay.d_ms !== "forever" &&
         value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms ?
-        i18n.str`pay delay can't be greater than wire transfer delay` : 
undefined,
+        i18n.str`Pay delay can't be greater than wire transfer delay` : 
undefined,
     default_wire_transfer_delay: !value.default_wire_transfer_delay
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : undefined,
     address: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n.str`max 7 lines`
+          ? i18n.str`Max 7 lines`
           : undefined,
     }),
     jurisdiction: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n.str`max 7 lines`
+          ? i18n.str`Max 7 lines`
           : undefined,
     }),
-  };
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submit = async (): Promise<void> => {
     const { default_pay_delay, default_wire_transfer_delay, ...rest } = value 
as Required<Entity>;
@@ -111,7 +108,7 @@ export function UpdatePage({
       default_wire_transfer_delay: 
Duration.toTalerProtocolDuration(default_wire_transfer_delay),
       ...rest,
     }
-    await onUpdate(result);
+    onUpdate(result);
   };
   // const [active, setActive] = useState(false);
 
@@ -159,7 +156,7 @@ export function UpdatePage({
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
                 disabled={hasErrors}
               >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
index 9da7f7efb..2b2327eb2 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
@@ -103,11 +103,11 @@ function CommonUpdate(
           }
           return updateInstance(state.token, d)
             .then(onConfirm)
-            .catch((error: Error) =>
+            .catch((error) =>
               setNotif({
                 message: i18n.str`Failed to update instance`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               }),
             );
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx
index 8792aabea..234295174 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx
@@ -30,6 +30,7 @@ import {
 import { Input } from "../../../../components/form/Input.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { TalerMerchantApi } from "@gnu-taler/taler-util";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = TalerMerchantApi.WebhookAddDetails;
 
@@ -45,26 +46,26 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
   const [state, setState] = useState<Partial<Entity>>({});
 
-  const errors: FormErrors<Entity> = {
-    webhook_id: !state.webhook_id ? i18n.str`required` : undefined,
-    event_type: !state.event_type ? i18n.str`required`
-      : state.event_type !== "pay" && state.event_type !== "refund" ? 
i18n.str`it should be "pay" or "refund"`
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
+    webhook_id: !state.webhook_id ? i18n.str`Required` : undefined,
+    event_type: !state.event_type
+      ? i18n.str`Required`
+      : state.event_type !== "pay" && state.event_type !== "refund"
+        ? i18n.str`Must be "pay" or "refund"`
         : undefined,
     http_method: !state.http_method
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !validMethod.includes(state.http_method)
-        ? i18n.str`should be one of '${validMethod.join(", ")}'`
+        ? i18n.str`Must be one of '${validMethod.join(", ")}'`
         : undefined,
-    url: !state.url ? i18n.str`required` : undefined,
-  };
+    url: !state.url ? i18n.str`Required` : undefined,
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
-    return onCreate(state as any);
+    return onCreate(state as TalerMerchantApi.WebhookAddDetails);
   };
 
   return (
@@ -88,8 +89,8 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 label={i18n.str`Event`}
                 values={[
                   i18n.str`Choose one...`,
-                  i18n.str`pay`,
-                  i18n.str`refund`,
+                  i18n.str`Pay`,
+                  i18n.str`Refund`,
                 ]}
                 tooltip={i18n.str`The event of the webhook: why the webhook is 
used`}
               />
@@ -114,28 +115,79 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               />
 
               <p>
-                The text below support <a target="_blank" rel="noreferrer" 
href="https://mustache.github.io/mustache.5.html";>mustache</a> template engine. 
Any string
-                between <pre style={{ display: "inline", padding: 0 
}}>&#123;&#123;</pre> and <pre style={{ display: "inline", padding: 0 
}}>&#125;&#125;</pre> will
-                be replaced with replaced with the value of the corresponding 
variable.
+                {/* prettier will add some nodes which we don't want because 
of i18n */}
+                {/* prettier-ignore */}
+                <i18n.Translate>
+                  The text below support <a
+                    target="_blank"
+                    rel="noreferrer"
+                    href="https://mustache.github.io/mustache.5.html";
+                  >
+                    mustache
+                  </a> template engine. Any string between <pre style={{ 
display: "inline", padding: 0 }}>
+                    &#123;&#123;
+                  </pre> and <pre style={{ display: "inline", padding: 0 }}>
+                    &#125;&#125;
+                  </pre> will be replaced with replaced with the value of the
+                  corresponding variable.
+                </i18n.Translate>
               </p>
               <p>
-                For example <pre style={{ display: "inline", padding: 0 
}}>&#123;&#123;contract_terms.amount&#125;&#125;</pre> will be replaced
-                with the the order's price
+                {/* prettier will add some nodes which we don't want because 
of i18n */}
+                {/* prettier-ignore */}
+                <i18n.Translate>
+                  For example <pre style={{ display: "inline", padding: 0 }}>
+                    &#123;&#123;contract_terms.amount&#125;&#125;
+                  </pre> will be replaced with the the order's price
+                </i18n.Translate>
               </p>
               <p>
-                The short list of variables are:
+                <i18n.Translate>
+                  The short list of variables are:
+                </i18n.Translate>
               </p>
               <div class="menu">
-
-                <ul class="menu-list" style={{ listStyleType: "disc", 
marginLeft: 20 }}>
-                  <li><b>contract_terms.summary:</b> order's description </li>
-                  <li><b>contract_terms.amount:</b> order's price </li>
-                  <li><b>order_id:</b> order's unique identification </li>
-                  {state.event_type === "refund" && <Fragment>
-                    <li><b>refund_amout:</b> the amount that was being 
refunded</li>
-                    <li><b>reason:</b> the reason entered by the merchant 
staff for granting the refund</li>
-                    <li><b>timestamp:</b> time of the refund in nanoseconds 
since 1970</li>
-                  </Fragment>}
+                <ul
+                  class="menu-list"
+                  style={{ listStyleType: "disc", marginLeft: 20 }}
+                >
+                  <li>
+                    <b>contract_terms.summary:</b>{" "}
+                    <i18n.Translate>order's description</i18n.Translate>
+                  </li>
+                  <li>
+                    <b>contract_terms.amount:</b>{" "}
+                    <i18n.Translate>order's price</i18n.Translate>
+                  </li>
+                  <li>
+                    <b>order_id:</b>{" "}
+                    <i18n.Translate>
+                      order's unique identification
+                    </i18n.Translate>
+                  </li>
+                  {state.event_type === "refund" && (
+                    <Fragment>
+                      <li>
+                        <b>refund_amout:</b>{" "}
+                        <i18n.Translate>
+                          the amount that was being refunded
+                        </i18n.Translate>
+                      </li>
+                      <li>
+                        <b>reason:</b>{" "}
+                        <i18n.Translate>
+                          the reason entered by the merchant staff for granting
+                          the refund
+                        </i18n.Translate>
+                      </li>
+                      <li>
+                        <b>timestamp:</b>{" "}
+                        <i18n.Translate>
+                          time of the refund in nanoseconds since 1970
+                        </i18n.Translate>
+                      </li>
+                    </Fragment>
+                  )}
                 </ul>
               </div>
               {/* <Input<Entity>
@@ -163,7 +215,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields`
-                    : "confirm operation"
+                    : i18n.str`Confirm operation`
                 }
                 onClick={submitForm}
               >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
index 84641e88d..6595879fb 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
@@ -49,13 +49,13 @@ export default function CreateWebhook({ onConfirm, onBack 
}: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`webhook create successfully`,
+                  message: i18n.str`Webhook create successfully`,
                   type: "SUCCESS",
                 });
                 onConfirm()
               } else {
                 setNotif({
-                  message: i18n.str`could not create the webhook`,
+                  message: i18n.str`Could not create the webhook`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -63,9 +63,9 @@ export default function CreateWebhook({ onConfirm, onBack }: 
Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not create webhook`,
+                message: i18n.str`Could not create webhook`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
index 877bd30e5..2d42ea6a5 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
@@ -59,7 +59,7 @@ export function CardTable({
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new webhooks`}
+            data-tooltip={i18n.str`Add new webhooks`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -114,10 +114,10 @@ function Table({
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more webhooks before the first one`}
+          data-tooltip={i18n.str`Load more webhooks before the first one`}
           onClick={onLoadMoreBefore}
         >
-          <i18n.Translate>load first page</i18n.Translate>
+          <i18n.Translate>Load first page</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -152,18 +152,11 @@ function Table({
                   <div class="buttons is-right">
                     <button
                       class="button is-danger is-small has-tooltip-left"
-                      data-tooltip={i18n.str`delete selected webhook from the 
database`}
+                      data-tooltip={i18n.str`Delete selected webhook from the 
database`}
                       onClick={() => onDelete(i)}
                     >
-                      Delete
+                      <i18n.Translate>Delete</i18n.Translate>
                     </button>
-                    {/* <button
-                      class="button is-info is-small has-tooltip-left"
-                      data-tooltip={i18n.str`test webhook`}
-                      onClick={() => onNewOrder(i)}
-                    >
-                      Test
-                    </button> */}
                   </div>
                 </td>
               </tr>
@@ -174,10 +167,10 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more webhooks after the last one`}
+          data-tooltip={i18n.str`Load more webhooks after the last one`}
           onClick={onLoadMoreAfter}
         >
-          <i18n.Translate>load next page</i18n.Translate>
+          <i18n.Translate>Load next page</i18n.Translate>
         </button>
       )}
     </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
index 2b34d8413..0e1c57839 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
@@ -85,12 +85,12 @@ export default function ListWebhooks({ onCreate, onSelect 
}: Props): VNode {
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`webhook delete successfully`,
+                  message: i18n.str`Webhook delete successfully`,
                   type: "SUCCESS",
                 });
               } else {
                 setNotif({
-                  message: i18n.str`could not delete the webhook`,
+                  message: i18n.str`Could not delete the webhook`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -98,9 +98,9 @@ export default function ListWebhooks({ onCreate, onSelect }: 
Props): VNode {
             })
             .catch((error) =>
               setNotif({
-                message: i18n.str`could not delete the webhook`,
+                message: i18n.str`Could not delete the webhook`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               }),
             );
         }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
index bcd53ffd0..1c1d0da79 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { TalerMerchantApi } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -28,8 +29,8 @@ import {
   FormProvider,
 } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
-import { TalerMerchantApi } from "@gnu-taler/taler-util";
 import { WithId } from "../../../../declaration.js";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = TalerMerchantApi.WebhookPatchDetails & WithId;
 
@@ -45,23 +46,21 @@ export function UpdatePage({ webhook, onUpdate, onBack }: 
Props): VNode {
 
   const [state, setState] = useState<Partial<Entity>>(webhook);
 
-  const errors: FormErrors<Entity> = {
-    event_type: !state.event_type ? i18n.str`required` : undefined,
+  const errors = undefinedIfEmpty<FormErrors<Entity>>({
+    event_type: !state.event_type ? i18n.str`Required` : undefined,
     http_method: !state.http_method
-      ? i18n.str`required`
+      ? i18n.str`Required`
       : !validMethod.includes(state.http_method)
-      ? i18n.str`should be one of '${validMethod.join(", ")}'`
-      : undefined,
-    url: !state.url ? i18n.str`required` : undefined,
-  };
+        ? i18n.str`Must be one of '${validMethod.join(", ")}'`
+        : undefined,
+    url: !state.url ? i18n.str`Required` : undefined,
+  });
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
+  const hasErrors = errors !== undefined;
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
-    return onUpdate(state as any);
+    return onUpdate(state as Entity);
   };
 
   return (
@@ -130,7 +129,7 @@ export function UpdatePage({ webhook, onUpdate, onBack }: 
Props): VNode {
                   data-tooltip={
                     hasErrors
                       ? i18n.str`Need to complete marked fields`
-                      : "confirm operation"
+                      : i18n.str`Confirm operation`
                   }
                   onClick={submitForm}
                 >
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
index a4ea0df6e..83dea45eb 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
@@ -29,6 +29,7 @@ import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchan
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { useSessionContext } from "../../../../context/session.js";
+import { WithId } from "../../../../declaration.js";
 import {
   useWebhookDetails,
 } from "../../../../hooks/webhooks.js";
@@ -36,7 +37,6 @@ import { Notification } from "../../../../utils/types.js";
 import { LoginPage } from "../../../login/index.js";
 import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { WithId } from "../../../../declaration.js";
 
 export type Entity = TalerMerchantApi.WebhookPatchDetails & WithId;
 
@@ -80,18 +80,18 @@ export default function UpdateWebhook({
       <UpdatePage
         webhook={{ ...result.body, id: tid }}
         onBack={onBack}
-        onUpdate={(data) => {
+        onUpdate={async (data) => {
           return lib.instance.updateWebhook(state.token, tid, data)
             .then((resp) => {
               if (resp.type === "ok") {
                 setNotif({
-                  message: i18n.str`webhook updated`,
+                  message: i18n.str`Webhook updated`,
                   type: "SUCCESS",
                 });
                 onConfirm()  
               } else {
                 setNotif({
-                  message: i18n.str`could not update webhook`,
+                  message: i18n.str`Could not update webhook`,
                   type: "ERROR",
                   description: resp.detail.hint,
                 });
@@ -100,9 +100,9 @@ export default function UpdateWebhook({
             })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update webhook`,
+                message: i18n.str`Could not update webhook`,
                 type: "ERROR",
-                description: error.message,
+                description: error instanceof Error ? error.message : 
String(error),
               });
             });
         }}
diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/login/index.tsx
index 7707fb18a..5b58c1690 100644
--- a/packages/merchant-backoffice-ui/src/paths/login/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/login/index.tsx
@@ -19,10 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, createRFC8959AccessTokenEncoded } from 
"@gnu-taler/taler-util";
 import {
-  useTranslationContext
-} from "@gnu-taler/web-util/browser";
+  HttpStatusCode,
+  createRFC8959AccessTokenEncoded,
+} from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../components/menu/index.js";
@@ -59,14 +60,14 @@ export function LoginPage(_p: Props): VNode {
       switch (result.case) {
         case HttpStatusCode.Unauthorized: {
           setNotif({
-            message: "Your password is incorrect",
+            message: i18n.str`Your password is incorrect`,
             type: "ERROR",
           });
           return;
         }
         case HttpStatusCode.NotFound: {
           setNotif({
-            message: "Your instance not found",
+            message: i18n.str`Your instance not found`,
             type: "ERROR",
           });
           return;
diff --git a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
index ac23f7f09..ef7dbd4e4 100644
--- a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { AbsoluteTime } from "@gnu-taler/taler-util";
 import { useLang, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import {
@@ -24,7 +25,6 @@ import { InputSelector } from 
"../../components/form/InputSelector.js";
 import { InputToggle } from "../../components/form/InputToggle.js";
 import { LangSelector } from "../../components/menu/LangSelector.js";
 import { Preferences, usePreference } from "../../hooks/preference.js";
-import { AbsoluteTime } from "@gnu-taler/taler-util";
 
 function getBrowserLang(): string | undefined {
   if (typeof window === "undefined") return undefined;
@@ -46,6 +46,7 @@ export function Settings({ onClose }: { onClose?: () => void 
}): VNode {
     const v: Preferences = {
       advanceOrderMode: next.advanceOrderMode ?? false,
       advanceInstanceMode: next.advanceInstanceMode ?? false,
+      developerMode: next.developerMode ?? false,
       hideMissingAccountUntil: next.hideMissingAccountUntil ?? 
AbsoluteTime.never(),
       hideKycUntil: next.hideKycUntil ?? AbsoluteTime.never(),
       dateFormat: next.dateFormat ?? "ymd",
@@ -85,7 +86,7 @@ export function Settings({ onClose }: { onClose?: () => void 
}): VNode {
                     &nbsp;
                     {borwserLang !== undefined && (
                       <button
-                        data-tooltip={i18n.str`generate random secret key`}
+                        data-tooltip={i18n.str`Generate random secret key`}
                         class="button is-info mr-2"
                         onClick={(e) => {
                           update(borwserLang.substring(0, 2));
@@ -127,7 +128,12 @@ export function Settings({ onClose }: { onClose?: () => 
void }): VNode {
                     return "choose one";
                   }}
                   values={["ymd", "mdy", "dmy"]}
-                  tooltip={i18n.str`how the date is going to be displayed`}
+                  tooltip={i18n.str`How the date is going to be displayed`}
+                />
+                <InputToggle<Preferences>
+                  label={i18n.str`Developer mode`}
+                  tooltip={i18n.str`Shows more options and tools which are not 
intended for general audience.`}
+                  name="developerMode"
                 />
               </FormProvider>
             </div>
diff --git a/packages/merchant-backoffice-ui/src/schemas/index.ts 
b/packages/merchant-backoffice-ui/src/schemas/index.ts
deleted file mode 100644
index 9e94b8d2e..000000000
--- a/packages/merchant-backoffice-ui/src/schemas/index.ts
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util";
-import { isAfter, isFuture } from "date-fns";
-import * as yup from "yup";
-import { PAYTO_REGEX } from "../utils/constants.js";
-// import { MerchantBackend } from "../declaration.js";
-
-yup.setLocale({
-  mixed: {
-    default: "field_invalid",
-  },
-  number: {
-    min: ({ min }: any) => ({ key: "field_too_short", values: { min } }),
-    max: ({ max }: any) => ({ key: "field_too_big", values: { max } }),
-  },
-});
-
-function listOfPayToUrisAreValid(values?: (string | undefined)[]): boolean {
-  return !!values && values.every((v) => v && PAYTO_REGEX.test(v));
-}
-
-function currencyWithAmountIsValid(value?: string): boolean {
-  return !!value && Amounts.parse(value) !== undefined;
-}
-function currencyGreaterThan0(value?: string) {
-  if (value) {
-    try {
-      const [, amount] = value.split(":");
-      const intAmount = parseInt(amount, 10);
-      return intAmount > 0;
-    } catch {
-      return false;
-    }
-  }
-  return true;
-}
-
-export const InstanceSchema = yup.object().shape({
-  id: yup.string().required().meta({ type: "url" }),
-  name: yup.string().required(),
-  auth: yup.object().shape({
-    method: yup.string().matches(/^(external|token)$/),
-    token: yup.string().optional().nullable(),
-  }),
-  payto_uris: yup
-    .array()
-    .of(yup.string())
-    .min(1)
-    .meta({ type: "array" })
-    .test("payto", "{path} is not valid", listOfPayToUrisAreValid),
-  default_max_deposit_fee: yup
-    .string()
-    .required()
-    .test("amount", "the amount is not valid", currencyWithAmountIsValid)
-    .meta({ type: "amount" }),
-  default_max_wire_fee: yup
-    .string()
-    .required()
-    .test("amount", "{path} is not valid", currencyWithAmountIsValid)
-    .meta({ type: "amount" }),
-  default_wire_fee_amortization: yup.number().required(),
-  address: yup
-    .object()
-    .shape({
-      country: yup.string().optional(),
-      address_lines: yup.array().of(yup.string()).max(7).optional(),
-      building_number: yup.string().optional(),
-      building_name: yup.string().optional(),
-      street: yup.string().optional(),
-      post_code: yup.string().optional(),
-      town_location: yup.string().optional(),
-      town: yup.string(),
-      district: yup.string().optional(),
-      country_subdivision: yup.string().optional(),
-    })
-    .meta({ type: "group" }),
-  jurisdiction: yup
-    .object()
-    .shape({
-      country: yup.string().optional(),
-      address_lines: yup.array().of(yup.string()).max(7).optional(),
-      building_number: yup.string().optional(),
-      building_name: yup.string().optional(),
-      street: yup.string().optional(),
-      post_code: yup.string().optional(),
-      town_location: yup.string().optional(),
-      town: yup.string(),
-      district: yup.string().optional(),
-      country_subdivision: yup.string().optional(),
-    })
-    .meta({ type: "group" }),
-  // default_pay_delay: yup.object()
-  //   .shape({ d_us: yup.number() })
-  //   .required()
-  //   .meta({ type: 'duration' }),
-  // .transform(numberToDuration),
-  default_wire_transfer_delay: yup
-    .object()
-    .shape({ d_us: yup.number() })
-    .required()
-    .meta({ type: "duration" }),
-  // .transform(numberToDuration),
-});
-
-export const InstanceUpdateSchema = InstanceSchema.clone().omit(["id"]);
-export const InstanceCreateSchema = InstanceSchema.clone();
-
-export const OrderCreateSchema = yup.object().shape({
-  pricing: yup
-    .object()
-    .required()
-    .shape({
-      summary: yup.string().ensure().required(),
-      order_price: yup
-        .string()
-        .ensure()
-        .required()
-        .test("amount", "the amount is not valid", currencyWithAmountIsValid)
-        .test(
-          "amount_positive",
-          "the amount should be greater than 0",
-          currencyGreaterThan0,
-        ),
-    }),
-  // extra: yup.object().test("extra", "is not a JSON format", 
stringIsValidJSON),
-  payments: yup
-    .object()
-    .required()
-    .shape({
-      refund_deadline: yup
-        .date()
-        .test("future", "should be in the future", (d) =>
-          d ? isFuture(d) : true,
-        ),
-      pay_deadline: yup
-        .date()
-        .test("future", "should be in the future", (d) =>
-          d ? isFuture(d) : true,
-        ),
-      auto_refund_deadline: yup
-        .date()
-        .test("future", "should be in the future", (d) =>
-          d ? isFuture(d) : true,
-        ),
-      delivery_date: yup
-        .date()
-        .test("future", "should be in the future", (d) =>
-          d ? isFuture(d) : true,
-        ),
-    })
-    .test("payment", "dates", (d) => {
-      if (
-        d.pay_deadline &&
-        d.refund_deadline &&
-        isAfter(d.refund_deadline, d.pay_deadline)
-      ) {
-        return new yup.ValidationError(
-          "pay deadline should be greater than refund",
-          "asd",
-          "payments.pay_deadline",
-        );
-      }
-      return true;
-    }),
-});
-
-export const ProductCreateSchema = yup.object().shape({
-  product_id: yup.string().ensure().required(),
-  description: yup.string().required(),
-  unit: yup.string().ensure().required(),
-  price: yup
-    .string()
-    .required()
-    .test("amount", "the amount is not valid", currencyWithAmountIsValid),
-  stock: yup.object({}).optional(),
-  minimum_age: yup.number().optional().min(0),
-});
-
-export const ProductUpdateSchema = yup.object().shape({
-  description: yup.string().required(),
-  price: yup
-    .string()
-    .required()
-    .test("amount", "the amount is not valid", currencyWithAmountIsValid),
-  stock: yup.object({}).optional(),
-  minimum_age: yup.number().optional().min(0),
-});
-
-export const TaxSchema = yup.object().shape({
-  name: yup.string().required().ensure(),
-  tax: yup
-    .string()
-    .required()
-    .test("amount", "the amount is not valid", currencyWithAmountIsValid),
-});
-
-export const NonInventoryProductSchema = yup.object().shape({
-  quantity: yup.number().required().positive(),
-  description: yup.string().required(),
-  unit: yup.string().ensure().required(),
-  price: yup
-    .string()
-    .required()
-    .test("amount", "the amount is not valid", currencyWithAmountIsValid),
-});
-
-const timestampSchema = yup
-  .object()
-  .shape({
-    t_s: yup
-      .mixed()
-      .test(
-        "is-timestamp",
-        "Invalid timestamp",
-        (value) => typeof value === "number" || value === "never",
-      ),
-  })
-  .required();
-
-const durationSchema = yup
-  .object()
-  .shape({
-    d_us: yup
-      .mixed()
-      .test(
-        "is-duration",
-        "Invalid duration",
-        (value) => typeof value === "number" || value === "forever",
-      ),
-  })
-  .required();
-
-const tokenFamilyKindSchema = yup
-  .mixed()
-  .oneOf<TalerMerchantApi.TokenFamilyKind>([
-    TalerMerchantApi.TokenFamilyKind.Discount,
-    TalerMerchantApi.TokenFamilyKind.Subscription,
-  ])
-  .required();
-
-export const TokenFamilyCreateSchema = yup.object().shape({
-  slug: yup.string().ensure().required(),
-  name: yup.string().required(),
-  description: yup.string().required(),
-  // description_i18n: yup.lazy((obj) =>
-  //   yup.object().shape(
-  //     Object.keys(obj || {}).reduce((acc, key) => {
-  //       acc[key] = yup.string().required();
-  //       return acc;
-  //     }, {})
-  //   )
-  // ).optional(),
-  valid_after: timestampSchema.optional(),
-  valid_before: timestampSchema,
-  duration: durationSchema,
-  kind: tokenFamilyKindSchema,
-});
-
-export const TokenFamilyUpdateSchema = yup.object().shape({
-  name: yup.string().required(),
-  description: yup.string().required(),
-  // description_i18n: yup.lazy((obj) =>
-  //   yup.object().shape(
-  //     Object.keys(obj).reduce((acc, key) => {
-  //       acc[key] = yup.string().required();
-  //       return acc;
-  //     }, {})
-  //   )
-  // ),
-  valid_after: timestampSchema,
-  valid_before: timestampSchema,
-  duration: durationSchema,
-});
diff --git a/packages/merchant-backoffice-ui/src/settings.json 
b/packages/merchant-backoffice-ui/src/settings.json
new file mode 100644
index 000000000..42c3276fa
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/settings.json
@@ -0,0 +1,4 @@
+{
+  "backendBaseURL": "http://merchant.taler.test:1180/";
+}
+
diff --git a/packages/auditor-backoffice-ui/src/scss/_footer.scss 
b/packages/merchant-backoffice-ui/src/type-override.d.ts
similarity index 61%
copy from packages/auditor-backoffice-ui/src/scss/_footer.scss
copy to packages/merchant-backoffice-ui/src/type-override.d.ts
index 7e90c40cc..d820aff57 100644
--- a/packages/auditor-backoffice-ui/src/scss/_footer.scss
+++ b/packages/merchant-backoffice-ui/src/type-override.d.ts
@@ -15,21 +15,18 @@
  */
 
 /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
+ * define unkown type of catch function
  */
-
-footer.footer {
-  .logo {
-    img {
-      width: auto;
-      height: $footer-logo-height;
-    }
-  }
-}
-
-@include mobile {
-  .footer-copyright {
-    text-align: center;
-  }
+interface Promise<T> {
+  /**
+   * Attaches a callback for only the rejection of the Promise.
+   * @param onrejected The callback to execute when the Promise is rejected.
+   * @returns A Promise for the completion of the callback.
+   */
+  catch<TResult = never>(
+    onrejected?:
+      | ((reason: unknown) => TResult | PromiseLike<TResult>)
+      | undefined
+      | null,
+  ): Promise<T | TResult>;
 }
diff --git a/packages/taler-util/src/http-client/officer-account.ts 
b/packages/taler-util/src/http-client/officer-account.ts
index 2c1426be2..01b3681c0 100644
--- a/packages/taler-util/src/http-client/officer-account.ts
+++ b/packages/taler-util/src/http-client/officer-account.ts
@@ -50,8 +50,8 @@ export async function unlockOfficerAccount(
     rawKey,
     rawPassword,
     password,
-  ).catch((e: Error) => {
-    throw new UnwrapKeyError(e.message);
+  ).catch((e) => {
+    throw new UnwrapKeyError(e instanceof Error ? e.message : String(e));
   })) as SigningKey;
 
   const publicKey = eddsaGetPublic(signingKey);
diff --git a/packages/taler-util/src/http-common.ts 
b/packages/taler-util/src/http-common.ts
index 9e6558b77..20ac9dddd 100644
--- a/packages/taler-util/src/http-common.ts
+++ b/packages/taler-util/src/http-common.ts
@@ -147,7 +147,7 @@ export async function readTalerErrorResponse(
   let errJson;
   try {
     errJson = await httpResponse.json();
-  } catch (e: any) {
+  } catch (e) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
       {
@@ -155,7 +155,7 @@ export async function readTalerErrorResponse(
         requestMethod: httpResponse.requestMethod,
         httpStatusCode: httpResponse.status,
         response: await httpResponse.text(),
-        validationError: e.toString(),
+        validationError: e instanceof Error ? e.message : String(e),
       },
       "Couldn't parse JSON format from error response",
     );
@@ -188,7 +188,7 @@ export async function readUnexpectedResponseDetails(
   let errJson;
   try {
     errJson = await httpResponse.json();
-  } catch (e: any) {
+  } catch (e) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
       {
@@ -196,7 +196,7 @@ export async function readUnexpectedResponseDetails(
         requestMethod: httpResponse.requestMethod,
         httpStatusCode: httpResponse.status,
         response: await httpResponse.text(),
-        validationError: e.toString(),
+        validationError: e instanceof Error ? e.message : String(e),
       },
       "Couldn't parse JSON format from error response",
     );
@@ -239,7 +239,7 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
   let respJson;
   try {
     respJson = await httpResponse.json();
-  } catch (e: any) {
+  } catch (e) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
       {
@@ -247,7 +247,7 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
         requestMethod: httpResponse.requestMethod,
         httpStatusCode: httpResponse.status,
         response: await httpResponse.text(),
-        validationError: e.toString(),
+        validationError: e instanceof Error ? e.message : String(e),
       },
       "Couldn't parse JSON format from response",
     );
@@ -255,7 +255,7 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
   let parsedResponse: T;
   try {
     parsedResponse = codec.decode(respJson);
-  } catch (e: any) {
+  } catch (e) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
       {
@@ -263,7 +263,7 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
         requestMethod: httpResponse.requestMethod,
         httpStatusCode: httpResponse.status,
         response: await httpResponse.text(),
-        validationError: e.toString(),
+        validationError: e instanceof Error ? e.message : String(e),
       },
       "Response invalid",
     );
@@ -281,7 +281,7 @@ export async function readResponseJsonOrErrorCode<T>(
   let respJson;
   try {
     respJson = await httpResponse.json();
-  } catch (e: any) {
+  } catch (e) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
       {
@@ -289,7 +289,7 @@ export async function readResponseJsonOrErrorCode<T>(
         requestMethod: httpResponse.requestMethod,
         httpStatusCode: httpResponse.status,
         response: await httpResponse.text(),
-        validationError: e.toString(),
+        validationError: e instanceof Error ? e.message : String(e),
       },
       "Couldn't parse JSON format from response",
     );
@@ -297,7 +297,7 @@ export async function readResponseJsonOrErrorCode<T>(
   let parsedResponse: T;
   try {
     parsedResponse = codec.decode(respJson);
-  } catch (e: any) {
+  } catch (e) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
       {
@@ -305,7 +305,7 @@ export async function readResponseJsonOrErrorCode<T>(
         requestMethod: httpResponse.requestMethod,
         httpStatusCode: httpResponse.status,
         response: await httpResponse.text(),
-        validationError: e.toString(),
+        validationError: e instanceof Error ? e.message : String(e),
       },
       "Response invalid",
     );
@@ -377,7 +377,7 @@ export async function readSuccessResponseTextOrErrorCode<T>(
     let errJson;
     try {
       errJson = await httpResponse.json();
-    } catch (e: any) {
+    } catch (e) {
       throw TalerError.fromDetail(
         TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
         {
@@ -385,7 +385,7 @@ export async function readSuccessResponseTextOrErrorCode<T>(
           requestMethod: httpResponse.requestMethod,
           httpStatusCode: httpResponse.status,
           response: await httpResponse.text(),
-          validationError: e.toString(),
+          validationError: e instanceof Error ? e.message : String(e),
         },
         "Couldn't parse JSON format from error response",
       );
@@ -423,7 +423,7 @@ export async function checkSuccessResponseOrThrow(
     let errJson;
     try {
       errJson = await httpResponse.json();
-    } catch (e: any) {
+    } catch (e) {
       throw TalerError.fromDetail(
         TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
         {
@@ -431,7 +431,7 @@ export async function checkSuccessResponseOrThrow(
           requestMethod: httpResponse.requestMethod,
           httpStatusCode: httpResponse.status,
           response: await httpResponse.text(),
-          validationError: e.toString(),
+          validationError: e instanceof Error ? e.message : String(e),
         },
         "Couldn't parse JSON format from error response",
       );
@@ -496,7 +496,7 @@ export interface HttpLibArgs {
   printAsCurl?: boolean;
 }
 
-export function encodeBody(body: any): ArrayBuffer {
+export function encodeBody(body: unknown): ArrayBuffer {
   if (body == null) {
     return new ArrayBuffer(0);
   }
diff --git a/packages/auditor-backoffice-ui/src/scss/_footer.scss 
b/packages/taler-util/src/type-override.d.ts
similarity index 61%
copy from packages/auditor-backoffice-ui/src/scss/_footer.scss
copy to packages/taler-util/src/type-override.d.ts
index 7e90c40cc..d820aff57 100644
--- a/packages/auditor-backoffice-ui/src/scss/_footer.scss
+++ b/packages/taler-util/src/type-override.d.ts
@@ -15,21 +15,18 @@
  */
 
 /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
+ * define unkown type of catch function
  */
-
-footer.footer {
-  .logo {
-    img {
-      width: auto;
-      height: $footer-logo-height;
-    }
-  }
-}
-
-@include mobile {
-  .footer-copyright {
-    text-align: center;
-  }
+interface Promise<T> {
+  /**
+   * Attaches a callback for only the rejection of the Promise.
+   * @param onrejected The callback to execute when the Promise is rejected.
+   * @returns A Promise for the completion of the callback.
+   */
+  catch<TResult = never>(
+    onrejected?:
+      | ((reason: unknown) => TResult | PromiseLike<TResult>)
+      | undefined
+      | null,
+  ): Promise<T | TResult>;
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]