gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: use translation context from


From: gnunet
Subject: [taler-wallet-core] branch master updated: use translation context from web-utils, don't use match react-router since is broken
Date: Tue, 20 Dec 2022 21:45:34 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new c59f9a255 use translation context from web-utils, don't use match 
react-router since is broken
c59f9a255 is described below

commit c59f9a2556731ad95ab8bd7eefe7fa8a41629834
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Dec 20 17:45:24 2022 -0300

    use translation context from web-utils, don't use match react-router since 
is broken
---
 packages/merchant-backoffice-ui/build.mjs          |   3 +-
 packages/merchant-backoffice-ui/package.json       |  24 +--
 .../merchant-backoffice-ui/src/Application.tsx     |  23 ++-
 .../src/ApplicationReadyRoutes.tsx                 |  32 +--
 .../merchant-backoffice-ui/src/InstanceRoutes.tsx  |  58 +++---
 .../src/components/exception/AsyncButton.tsx       |   6 +-
 .../src/components/exception/login.tsx             |  12 +-
 .../src/components/form/InputArray.tsx             |  10 +-
 .../src/components/form/InputCurrency.tsx          |   9 +-
 .../src/components/form/InputDate.tsx              |  14 +-
 .../src/components/form/InputDuration.tsx          |  28 +--
 .../src/components/form/InputImage.tsx             |  10 +-
 .../src/components/form/InputLocation.tsx          |  27 +--
 .../src/components/form/InputPaytoForm.tsx         |  98 +++++-----
 .../src/components/form/InputSearchProduct.tsx     |  18 +-
 .../src/components/form/InputSecured.tsx           |  17 +-
 .../src/components/form/InputStock.tsx             |  46 ++---
 .../src/components/form/InputTaxes.tsx             |  25 +--
 .../instance/DefaultInstanceFormFields.tsx         |  56 +++---
 .../src/components/menu/LangSelector.tsx           |   4 +-
 .../src/components/menu/SideBar.tsx                |  28 ++-
 .../src/components/menu/index.tsx                  |  99 +++++-----
 .../src/components/modal/index.tsx                 |  96 ++++-----
 .../src/components/picker/DurationPicker.tsx       |  12 +-
 .../components/product/InventoryProductForm.tsx    |  26 +--
 .../components/product/NonInventoryProductForm.tsx |  44 ++---
 .../src/components/product/ProductForm.tsx         |  38 ++--
 .../src/components/product/ProductList.tsx         |  13 +-
 .../merchant-backoffice-ui/src/context/fetch.ts    |   2 +-
 .../merchant-backoffice-ui/src/context/listener.ts |  35 ----
 .../src/context/translation.ts                     |  66 -------
 packages/merchant-backoffice-ui/src/i18n/index.tsx | 215 ---------------------
 .../src/paths/admin/create/Create.stories.tsx      |  12 +-
 .../src/paths/admin/create/CreatePage.tsx          |  53 ++---
 .../src/paths/admin/create/index.tsx               |   6 +-
 .../paths/admin/create/{stories.ts => stories.tsx} |  22 ++-
 .../src/paths/admin/list/TableActive.tsx           |  29 +--
 .../src/paths/admin/list/View.tsx                  |  18 +-
 .../src/paths/admin/list/index.tsx                 |  12 +-
 .../src/paths/instance/details/DetailPage.tsx      |   8 +-
 .../src/paths/instance/details/stories.tsx         |  17 +-
 .../src/paths/instance/index.stories.ts            |   1 -
 .../src/paths/instance/kyc/list/ListPage.tsx       |  25 +--
 .../paths/instance/orders/create/CreatePage.tsx    | 132 ++++++-------
 .../orders/create/OrderCreatedSuccessfully.tsx     |  12 +-
 .../paths/instance/orders/details/DetailPage.tsx   | 151 ++++++++-------
 .../src/paths/instance/orders/details/index.tsx    |  10 +-
 .../src/paths/instance/orders/list/ListPage.tsx    |  33 ++--
 .../src/paths/instance/orders/list/Table.tsx       |  68 ++++---
 .../src/paths/instance/orders/list/index.tsx       |  20 +-
 .../paths/instance/products/create/CreatePage.tsx  |  10 +-
 .../src/paths/instance/products/create/index.tsx   |   6 +-
 .../src/paths/instance/products/list/Table.tsx     |  79 ++++----
 .../src/paths/instance/products/list/index.tsx     |  12 +-
 .../paths/instance/products/update/UpdatePage.tsx  |  14 +-
 .../src/paths/instance/products/update/index.tsx   |   6 +-
 .../paths/instance/reserves/create/CreatePage.tsx  |  55 +++---
 .../reserves/create/CreatedSuccessfully.tsx        |  14 +-
 .../src/paths/instance/reserves/create/index.tsx   |   6 +-
 .../paths/instance/reserves/details/DetailPage.tsx |  53 ++---
 .../instance/reserves/list/AutorizeTipModal.tsx    |  18 +-
 .../src/paths/instance/reserves/list/Table.tsx     |  44 +++--
 .../src/paths/instance/reserves/list/index.tsx     |   8 +-
 .../paths/instance/templates/create/CreatePage.tsx |  44 ++---
 .../src/paths/instance/templates/create/index.tsx  |   8 +-
 .../src/paths/instance/templates/list/ListPage.tsx |   4 +-
 .../src/paths/instance/templates/list/Table.tsx    |  32 +--
 .../src/paths/instance/templates/list/index.tsx    |   8 +-
 .../paths/instance/templates/update/UpdatePage.tsx |  44 ++---
 .../src/paths/instance/templates/update/index.tsx  |   7 +-
 .../paths/instance/transfers/create/CreatePage.tsx |  42 ++--
 .../src/paths/instance/transfers/create/index.tsx  |   6 +-
 .../src/paths/instance/transfers/list/ListPage.tsx |  22 +--
 .../src/paths/instance/transfers/list/Table.tsx    |  50 ++---
 .../src/paths/instance/update/UpdatePage.tsx       |  53 ++---
 .../src/paths/instance/update/index.tsx            |   6 +-
 .../merchant-backoffice-ui/src/stories.test.ts     |  13 +-
 .../tests/functions/regex.test.ts                  |  32 +--
 .../merchant-backoffice-ui/tests/header.test.tsx   |  18 +-
 packages/web-util/src/tests/hook.ts                |   2 +
 pnpm-lock.yaml                                     |  50 ++---
 81 files changed, 1167 insertions(+), 1422 deletions(-)

diff --git a/packages/merchant-backoffice-ui/build.mjs 
b/packages/merchant-backoffice-ui/build.mjs
index 611ef7044..4097e4e9a 100755
--- a/packages/merchant-backoffice-ui/build.mjs
+++ b/packages/merchant-backoffice-ui/build.mjs
@@ -64,8 +64,7 @@ function getFilesInDirectory(startPath, regex) {
   return result
 }
 
-const allTestFiles = getFilesInDirectory(path.join(BASE, 'tests'), 
/.test.tsx?$/)
-
+const allTestFiles = getFilesInDirectory(path.join(BASE, 'src'), /.test.tsx?$/)
 const entryPoints = ["src/index.tsx", "src/stories.tsx", ...allTestFiles];
 
 let GIT_ROOT = BASE;
diff --git a/packages/merchant-backoffice-ui/package.json 
b/packages/merchant-backoffice-ui/package.json
index 085f92d76..ca8923e05 100644
--- a/packages/merchant-backoffice-ui/package.json
+++ b/packages/merchant-backoffice-ui/package.json
@@ -3,12 +3,13 @@
   "name": "@gnu-taler/merchant-backoffice",
   "version": "0.0.4",
   "license": "MIT",
+  "type": "module",
   "scripts": {
     "build": "./build.mjs",
     "check": "tsc",
     "compile": "tsc && ./build.mjs",
     "dev": "preact watch --port ${PORT:=8080} --no-sw --no-esm",
-    "test": "pnpm compile && mocha --require source-map-support/register 
'*.test.js' 'dist/**/test.js'",
+    "test": "pnpm compile && mocha --require source-map-support/register 
'dist/**/*.test.js' 'dist/**/test.js'",
     "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
     "i18n:extract": "pogen extract",
     "i18n:merge": "pogen merge",
@@ -38,7 +39,7 @@
     "date-fns": "2.29.3",
     "history": "4.10.1",
     "jed": "1.1.1",
-    "preact": "10.6.5",
+    "preact": "10.11.3",
     "preact-router": "3.2.1",
     "qrcode-generator": "1.4.4",
     "swr": "1.3.0",
@@ -79,22 +80,5 @@
     "sass": "1.56.1",
     "typedoc": "^0.20.36",
     "typescript": "4.8.4"
-  },
-  "jest": {
-    "preset": "jest-preset-preact",
-    "transformIgnorePatterns": [
-      "node_modules/.pnpm/(?!(@gnu-taler\\+taler-util))",
-      "\\.pnp\\.[^\\/]+$"
-    ],
-    "setupFiles": [
-      "<rootDir>/tests/__mocks__/browserMocks.ts",
-      "<rootDir>/tests/__mocks__/setupTests.ts"
-    ],
-    "moduleNameMapper": {
-      "\\.(css|less)$": "identity-obj-proxy"
-    },
-    "transform": {
-      
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|po)$":
 "<rootDir>/tests/__mocks__/fileTransformer.js"
-    }
   }
-}
+}
\ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/src/Application.tsx 
b/packages/merchant-backoffice-ui/src/Application.tsx
index 0b92375c1..84536396d 100644
--- a/packages/merchant-backoffice-ui/src/Application.tsx
+++ b/packages/merchant-backoffice-ui/src/Application.tsx
@@ -19,6 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import {
+  TranslationProvider,
+  useTranslationContext,
+} from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { route } from "preact-router";
 import { useMemo } from "preact/hooks";
@@ -33,16 +37,15 @@ import {
   useBackendContext,
 } from "./context/backend.js";
 import { ConfigContextProvider } from "./context/config.js";
-import { TranslationProvider } from "./context/translation.js";
 import { useBackendConfig } from "./hooks/backend.js";
-import { useTranslator } from "./i18n/index.js";
+import { strings } from "./i18n/strings.js";
 import LoginPage from "./paths/login/index.js";
 
 export function Application(): VNode {
   return (
     // <FetchContextProvider>
     <BackendContextProvider>
-      <TranslationProvider>
+      <TranslationProvider source={strings}>
         <ApplicationStatusRoutes />
       </TranslationProvider>
     </BackendContextProvider>
@@ -53,7 +56,7 @@ export function Application(): VNode {
 function ApplicationStatusRoutes(): VNode {
   const { updateLoginStatus, triedToLog } = useBackendContext();
   const result = useBackendConfig();
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const updateLoginInfoAndGoToRoot = (url: string, token?: string) => {
     updateLoginStatus(url, token);
@@ -88,7 +91,7 @@ function ApplicationStatusRoutes(): VNode {
         <NotYetReadyAppMenu title="Error" />
         <NotificationCard
           notification={{
-            message: i18n`Server not found`,
+            message: i18n.str`Server not found`,
             type: "ERROR",
             description: `Check your url`,
           }}
@@ -103,9 +106,9 @@ function ApplicationStatusRoutes(): VNode {
         <NotYetReadyAppMenu title="Error" />
         <NotificationCard
           notification={{
-            message: i18n`Couldn't access the server`,
+            message: i18n.str`Couldn't access the server`,
             type: "ERROR",
-            description: i18n`Got message ${result.message} from 
${result.info?.url}`,
+            description: i18n.str`Got message ${result.message} from 
${result.info?.url}`,
           }}
         />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
@@ -120,9 +123,9 @@ function ApplicationStatusRoutes(): VNode {
         <NotYetReadyAppMenu title="Error" />
         <NotificationCard
           notification={{
-            message: i18n`Unexpected Error`,
+            message: i18n.str`Unexpected Error`,
             type: "ERROR",
-            description: i18n`Got message ${result.message} from 
${result.info?.url}`,
+            description: i18n.str`Got message ${result.message} from 
${result.info?.url}`,
           }}
         />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
@@ -130,7 +133,7 @@ function ApplicationStatusRoutes(): VNode {
     );
 
   return (
-    <div id="app" class="has-navbar-fixed-top">
+    <div class="has-navbar-fixed-top">
       <ConfigContextProvider value={ctx}>
         <ApplicationReadyRoutes />
       </ConfigContextProvider>
diff --git a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx 
b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
index fe4493bff..4390ef877 100644
--- a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
@@ -18,23 +18,23 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { createHashHistory } from "history";
 import { Fragment, h, VNode } from "preact";
 import Router, { Route, route } from "preact-router";
+import { useState } from "preact/hooks";
+import {
+  NotificationCard,
+  NotYetReadyAppMenu,
+} from "./components/menu/index.js";
 import { useBackendContext } from "./context/backend.js";
 import { useBackendInstancesTestForAdmin } from "./hooks/backend.js";
 import { InstanceRoutes } from "./InstanceRoutes.js";
 import LoginPage from "./paths/login/index.js";
 import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
-import {
-  NotYetReadyAppMenu,
-  NotificationCard,
-} from "./components/menu/index.js";
-import { useTranslator } from "./i18n/index.js";
-import { createHashHistory } from "history";
-import { useState } from "preact/hooks";
 
 export function ApplicationReadyRoutes(): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const {
     url: backendURL,
     updateLoginStatus,
@@ -54,8 +54,8 @@ export function ApplicationReadyRoutes(): VNode {
         <NotYetReadyAppMenu title="Login" onLogout={clearTokenAndGoToRoot} />
         <NotificationCard
           notification={{
-            message: i18n`Access denied`,
-            description: i18n`Check your token is valid`,
+            message: i18n.str`Access denied`,
+            description: i18n.str`Check your token is valid`,
             type: "ERROR",
           }}
         />
@@ -81,8 +81,8 @@ export function ApplicationReadyRoutes(): VNode {
           <NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} />
           <NotificationCard
             notification={{
-              message: i18n`Couldn't access the server.`,
-              description: i18n`Could not infer instance id from url 
${backendURL}`,
+              message: i18n.str`Couldn't access the server.`,
+              description: i18n.str`Could not infer instance id from url 
${backendURL}`,
               type: "ERROR",
             }}
           />
@@ -108,7 +108,12 @@ export function ApplicationReadyRoutes(): VNode {
   );
 }
 
-function DefaultMainRoute({ instance, admin, instanceNameByBackendURL }: any) {
+function DefaultMainRoute({
+  instance,
+  admin,
+  instanceNameByBackendURL,
+  url, //from preact-router
+}: any): VNode {
   const [instanceName, setInstanceName] = useState(
     instanceNameByBackendURL || instance || "default",
   );
@@ -116,6 +121,7 @@ function DefaultMainRoute({ instance, admin, 
instanceNameByBackendURL }: any) {
   return (
     <InstanceRoutes
       admin={admin}
+      path={url}
       id={instanceName}
       setInstanceName={setInstanceName}
     />
diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx 
b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
index 817de5f7b..8ac5c698b 100644
--- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
@@ -19,6 +19,8 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { format } from "date-fns";
 import { Fragment, FunctionComponent, h, VNode } from "preact";
 import { Route, route, Router } from "preact-router";
 import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
@@ -26,39 +28,37 @@ import { Loading } from "./components/exception/loading.js";
 import { Menu, NotificationCard } from "./components/menu/index.js";
 import { useBackendContext } from "./context/backend.js";
 import { InstanceContextProvider } from "./context/instance.js";
+import { HttpError } from "./hooks/backend.js";
 import {
   useBackendDefaultToken,
   useBackendInstanceToken,
   useLocalStorage,
 } from "./hooks/index.js";
-import { HttpError } from "./hooks/backend.js";
-import { Translate, useTranslator } from "./i18n/index.js";
+import { useInstanceKYCDetails } from "./hooks/instance.js";
 import InstanceCreatePage from "./paths/admin/create/index.js";
 import InstanceListPage from "./paths/admin/list/index.js";
+import ListKYCPage from "./paths/instance/kyc/list/index.js";
 import OrderCreatePage from "./paths/instance/orders/create/index.js";
 import OrderDetailsPage from "./paths/instance/orders/details/index.js";
 import OrderListPage from "./paths/instance/orders/list/index.js";
 import ProductCreatePage from "./paths/instance/products/create/index.js";
 import ProductListPage from "./paths/instance/products/list/index.js";
 import ProductUpdatePage from "./paths/instance/products/update/index.js";
-import TransferListPage from "./paths/instance/transfers/list/index.js";
-import TransferCreatePage from "./paths/instance/transfers/create/index.js";
-import TemplateListPage from "./paths/instance/templates/list/index.js";
-import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
-import TemplateCreatePage from "./paths/instance/templates/create/index.js";
 import ReservesCreatePage from "./paths/instance/reserves/create/index.js";
 import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
 import ReservesListPage from "./paths/instance/reserves/list/index.js";
-import ListKYCPage from "./paths/instance/kyc/list/index.js";
+import TemplateCreatePage from "./paths/instance/templates/create/index.js";
+import TemplateListPage from "./paths/instance/templates/list/index.js";
+import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
+import TransferCreatePage from "./paths/instance/transfers/create/index.js";
+import TransferListPage from "./paths/instance/transfers/list/index.js";
 import InstanceUpdatePage, {
-  Props as InstanceUpdatePageProps,
   AdminUpdate as InstanceAdminUpdatePage,
+  Props as InstanceUpdatePageProps,
 } from "./paths/instance/update/index.js";
 import LoginPage from "./paths/login/index.js";
 import NotFoundPage from "./paths/notfound/index.js";
 import { Notification } from "./utils/types.js";
-import { useInstanceKYCDetails } from "./hooks/instance.js";
-import { format } from "date-fns";
 
 export enum InstancePaths {
   // details = '/',
@@ -99,10 +99,16 @@ export enum AdminPaths {
 export interface Props {
   id: string;
   admin?: boolean;
+  path: string;
   setInstanceName: (s: string) => void;
 }
 
-export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
+export function InstanceRoutes({
+  id,
+  admin,
+  path,
+  setInstanceName,
+}: Props): VNode {
   const [_, updateDefaultToken] = useBackendDefaultToken();
   const [token, updateToken] = useBackendInstanceToken(id);
   const {
@@ -113,7 +119,7 @@ export function InstanceRoutes({ id, admin, setInstanceName 
}: Props): VNode {
   const cleaner = useCallback(() => {
     updateToken(undefined);
   }, [id]);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   type GlobalNotifState = (Notification & { to: string }) | undefined;
   const [globalNotification, setGlobalNotification] =
@@ -144,8 +150,8 @@ export function InstanceRoutes({ id, admin, setInstanceName 
}: Props): VNode {
   function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) {
     return function ServerErrorRedirectToImpl(error: HttpError) {
       setGlobalNotification({
-        message: i18n`The backend reported a problem: HTTP status 
#${error.status}`,
-        description: i18n`Diagnostic from ${error.info?.url} is 
"${error.message}"`,
+        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.clientError || error.serverError
             ? error.error?.detail
@@ -161,8 +167,8 @@ export function InstanceRoutes({ id, admin, setInstanceName 
}: Props): VNode {
     <Fragment>
       <NotificationCard
         notification={{
-          message: i18n`Access denied`,
-          description: i18n`The access token provided is invalid.`,
+          message: i18n.str`Access denied`,
+          description: i18n.str`The access token provided is invalid.`,
           type: "ERROR",
         }}
       />
@@ -177,8 +183,8 @@ export function InstanceRoutes({ id, admin, setInstanceName 
}: Props): VNode {
           <Fragment>
             <NotificationCard
               notification={{
-                message: i18n`No 'default' instance configured yet.`,
-                description: i18n`Create a 'default' instance to begin using 
the merchant backoffice.`,
+                message: i18n.str`No 'default' instance configured yet.`,
+                description: i18n.str`Create a 'default' instance to begin 
using the merchant backoffice.`,
                 type: "INFO",
               }}
             />
@@ -208,6 +214,7 @@ export function InstanceRoutes({ id, admin, setInstanceName 
}: Props): VNode {
       <Menu
         instance={id}
         admin={admin}
+        path={path}
         onLogout={clearTokenAndGoToRoot}
         setInstanceName={setInstanceName}
       />
@@ -488,7 +495,7 @@ function AdminInstanceUpdatePage({
     () => ({ id, token, admin: true, changeToken }),
     [id, token],
   );
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <InstanceContextProvider value={value}>
@@ -500,8 +507,8 @@ function AdminInstanceUpdatePage({
             <Fragment>
               <NotificationCard
                 notification={{
-                  message: i18n`The backend reported a problem: HTTP status 
#${error.status}`,
-                  description: i18n`Diagnostic from ${error.info?.url} is 
"${error.message}"`,
+                  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.clientError || error.serverError
                       ? error.error?.detail
@@ -518,8 +525,8 @@ function AdminInstanceUpdatePage({
             <Fragment>
               <NotificationCard
                 notification={{
-                  message: i18n`Access denied`,
-                  description: i18n`The access token provided is invalid`,
+                  message: i18n.str`Access denied`,
+                  description: i18n.str`The access token provided is invalid`,
                   type: "ERROR",
                 }}
               />
@@ -534,6 +541,7 @@ function AdminInstanceUpdatePage({
 
 function KycBanner(): VNode {
   const kycStatus = useInstanceKYCDetails();
+  const { i18n } = useTranslationContext();
   const today = format(new Date(), "yyyy-MM-dd");
   const [lastHide, setLastHide] = useLocalStorage("kyc-last-hide");
   const hasBeenHidden = today === lastHide;
@@ -552,7 +560,7 @@ function KycBanner(): VNode {
             </p>
             <div class="buttons is-right">
               <button class="button" onClick={() => setLastHide(today)}>
-                <Translate>Hide for today</Translate>
+                <i18n.Translate>Hide for today</i18n.Translate>
               </button>
             </div>
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx 
b/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx
index 510bc29b8..aa50dfa91 100644
--- a/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx
+++ b/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx
@@ -22,7 +22,7 @@
 import { ComponentChildren, h } from "preact";
 import { LoadingModal } from "../modal/index.js";
 import { useAsync } from "../../hooks/async.js";
-import { Translate } from "../../i18n/index.js";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 
 type Props = {
   children: ComponentChildren;
@@ -33,14 +33,14 @@ type Props = {
 
 export function AsyncButton({ onClick, disabled, children, ...rest }: Props) {
   const { isSlow, isLoading, request, cancel } = useAsync(onClick);
-
+  const { i18n } = useTranslationContext();
   if (isSlow) {
     return <LoadingModal onCancel={cancel} />;
   }
   if (isLoading) {
     return (
       <button class="button">
-        <Translate>Loading...</Translate>
+        <i18n.Translate>Loading...</i18n.Translate>
       </button>
     );
   }
diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx 
b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
index d1898915d..552e76ed6 100644
--- a/packages/merchant-backoffice-ui/src/components/exception/login.tsx
+++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { useBackendContext } from "../../context/backend.js";
 import { useInstanceContext } from "../../context/instance.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { Notification } from "../../utils/types.js";
 
 interface Props {
@@ -51,7 +51,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
   const [token, setToken] = useState(currentToken);
 
   const [url, setURL] = useState(backendUrl);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="columns is-centered">
@@ -61,13 +61,13 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
             class="modal-card-head"
             style={{ border: "1px solid", borderBottom: 0 }}
           >
-            <p class="modal-card-title">{i18n`Login required`}</p>
+            <p class="modal-card-title">{i18n.str`Login required`}</p>
           </header>
           <section
             class="modal-card-body"
             style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
           >
-            <Translate>Please enter your access token.</Translate>
+            <i18n.Translate>Please enter your access token.</i18n.Translate>
             <div class="field is-horizontal">
               <div class="field-label is-normal">
                 <label class="label">URL</label>
@@ -95,7 +95,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
             <div class="field is-horizontal">
               <div class="field-label is-normal">
                 <label class="label">
-                  <Translate>Access Token</Translate>
+                  <i18n.Translate>Access Token</i18n.Translate>
                 </label>
               </div>
               <div class="field-body">
@@ -133,7 +133,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
                 onConfirm(url, normalizeToken(token));
               }}
             >
-              <Translate>Confirm</Translate>
+              <i18n.Translate>Confirm</i18n.Translate>
             </button>
           </footer>
         </div>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
index b5da1117a..8f0249bb2 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
@@ -18,9 +18,9 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { InputProps, useField } from "./useField.js";
 
 export interface Props<T> extends InputProps<T> {
@@ -52,7 +52,7 @@ export function InputArray<T>({
 
   const array: any[] = (value ? value! : []) as any;
   const [currentValue, setCurrentValue] = useState("");
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="field is-horizontal">
@@ -99,7 +99,7 @@ export function InputArray<T>({
                   const v = fromStr(currentValue);
                   if (!isValid(v)) {
                     setLocalError(
-                      i18n`The value ${v} is invalid for a payment url`,
+                      i18n.str`The value ${v} is invalid for a payment url`,
                     );
                     return;
                   }
@@ -107,9 +107,9 @@ export function InputArray<T>({
                   onChange([v, ...array] as any);
                   setCurrentValue("");
                 }}
-                data-tooltip={i18n`add element to the list`}
+                data-tooltip={i18n.str`add element to the list`}
               >
-                <Translate>add</Translate>
+                <i18n.Translate>add</i18n.Translate>
               </button>
             </p>
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
index 57a5163b7..3b84855da 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
@@ -18,7 +18,7 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { ComponentChildren, h } from "preact";
+import { ComponentChildren, h, VNode } from "preact";
 import { useConfigContext } from "../../context/config.js";
 import { Amount } from "../../declaration.js";
 import { InputWithAddon } from "./InputWithAddon.js";
@@ -42,7 +42,7 @@ export function InputCurrency<T>({
   addonAfter,
   children,
   side,
-}: Props<keyof T>) {
+}: Props<keyof T>): VNode {
   const config = useConfigContext();
   return (
     <InputWithAddon<T>
@@ -60,7 +60,8 @@ export function InputCurrency<T>({
       toStr={(v?: Amount) => v?.split(":")[1] || ""}
       fromStr={(v: string) => (!v ? "" : `${config.currency}:${v}`)}
       inputExtra={{ min: 0 }}
-      children={children}
-    />
+    >
+      {children}
+    </InputWithAddon>
   );
 }
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
index 4704b3312..fba798bd5 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
@@ -18,10 +18,10 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { DatePicker } from "../picker/DatePicker.js";
 import { InputProps, useField } from "./useField.js";
 
@@ -43,7 +43,7 @@ export function InputDate<T>({
   withTimestampSupport,
 }: Props<keyof T>): VNode {
   const [opened, setOpened] = useState(false);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const { error, required, value, onChange } = useField<T>(name);
 
@@ -120,25 +120,25 @@ export function InputDate<T>({
           <span
             data-tooltip={
               withTimestampSupport
-                ? i18n`change value to unknown date`
-                : i18n`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)}
             >
-              <Translate>clear</Translate>
+              <i18n.Translate>clear</i18n.Translate>
             </button>
           </span>
         )}
         {withTimestampSupport && (
-          <span data-tooltip={i18n`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)}
             >
-              <Translate>never</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 658cc4db7..3f25d1dc7 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
@@ -18,10 +18,10 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { intervalToDuration, formatDuration } from "date-fns";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { formatDuration, intervalToDuration } from "date-fns";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { SimpleModal } from "../modal/index.js";
 import { DurationPicker } from "../picker/DurationPicker.js";
 import { InputProps, useField } from "./useField.js";
@@ -43,14 +43,14 @@ export function InputDuration<T>({
   withForever,
 }: Props<keyof T>): VNode {
   const [opened, setOpened] = useState(false);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const { error, required, value, onChange } = useField<T>(name);
   let strValue = "";
   if (!value) {
     strValue = "";
   } else if (value.d_us === "forever") {
-    strValue = i18n`forever`;
+    strValue = i18n.str`forever`;
   } else {
     strValue = formatDuration(
       intervalToDuration({ start: 0, end: value.d_us / 1000 }),
@@ -59,17 +59,17 @@ export function InputDuration<T>({
           formatDistance: (name, value) => {
             switch (name) {
               case "xMonths":
-                return i18n`${value}M`;
+                return i18n.str`${value}M`;
               case "xYears":
-                return i18n`${value}Y`;
+                return i18n.str`${value}Y`;
               case "xDays":
-                return i18n`${value}d`;
+                return i18n.str`${value}d`;
               case "xHours":
-                return i18n`${value}h`;
+                return i18n.str`${value}h`;
               case "xMinutes":
-                return i18n`${value}min`;
+                return i18n.str`${value}min`;
               case "xSeconds":
-                return i18n`${value}sec`;
+                return i18n.str`${value}sec`;
             }
           },
           localize: {
@@ -134,22 +134,22 @@ export function InputDuration<T>({
           {error && <p class="help is-danger">{error}</p>}
         </div>
         {withForever && (
-          <span data-tooltip={i18n`change value to never`}>
+          <span data-tooltip={i18n.str`change value to never`}>
             <button
               class="button is-info mr-3"
               onClick={() => onChange({ d_us: "forever" } as any)}
             >
-              <Translate>forever</Translate>
+              <i18n.Translate>forever</i18n.Translate>
             </button>
           </span>
         )}
         {!readonly && (
-          <span data-tooltip={i18n`change value to empty`}>
+          <span data-tooltip={i18n.str`change value to empty`}>
             <button
               class="button is-info "
               onClick={() => onChange(undefined as any)}
             >
-              <Translate>clear</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 d5b2aadb6..43a7af1a3 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
@@ -18,9 +18,9 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { ComponentChildren, h, VNode } from "preact";
 import { useRef, useState } from "preact/hooks";
-import { Translate } from "../../i18n/index.js";
 import { MAX_IMAGE_SIZE as MAX_IMAGE_UPLOAD_SIZE } from 
"../../utils/constants.js";
 import { InputProps, useField } from "./useField.js";
 
@@ -43,7 +43,7 @@ export function InputImage<T>({
   const { error, value, onChange } = useField<T>(name);
 
   const image = useRef<HTMLInputElement>(null);
-
+  const { i18n } = useTranslationContext();
   const [sizeError, setSizeError] = useState(false);
 
   return (
@@ -102,17 +102,17 @@ export function InputImage<T>({
           {error && <p class="help is-danger">{error}</p>}
           {sizeError && (
             <p class="help is-danger">
-              <Translate>Image should be smaller than 1 MB</Translate>
+              <i18n.Translate>Image should be smaller than 1 
MB</i18n.Translate>
             </p>
           )}
           {!value && (
             <button class="button" onClick={() => image.current?.click()}>
-              <Translate>Add</Translate>
+              <i18n.Translate>Add</i18n.Translate>
             </button>
           )}
           {value && (
             <button class="button" onClick={() => onChange(undefined!)}>
-              <Translate>Remove</Translate>
+              <i18n.Translate>Remove</i18n.Translate>
             </button>
           )}
         </div>
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx
index 613b2f1e6..ef5a0995c 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx
@@ -19,31 +19,34 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 import { Fragment, h } from "preact";
-import { useTranslator } from "../../i18n/index.js";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Input } from "./Input.js";
 
 export function InputLocation({ name }: { name: string }) {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <>
-      <Input name={`${name}.country`} label={i18n`Country`} />
+      <Input name={`${name}.country`} label={i18n.str`Country`} />
       <Input
         name={`${name}.address_lines`}
         inputType="multiline"
-        label={i18n`Address`}
+        label={i18n.str`Address`}
         toStr={(v: string[] | undefined) => (!v ? "" : v.join("\n"))}
         fromStr={(v: string) => v.split("\n")}
       />
-      <Input name={`${name}.building_number`} label={i18n`Building number`} />
-      <Input name={`${name}.building_name`} label={i18n`Building name`} />
-      <Input name={`${name}.street`} label={i18n`Street`} />
-      <Input name={`${name}.post_code`} label={i18n`Post code`} />
-      <Input name={`${name}.town_location`} label={i18n`Town location`} />
-      <Input name={`${name}.town`} label={i18n`Town`} />
-      <Input name={`${name}.district`} label={i18n`District`} />
+      <Input
+        name={`${name}.building_number`}
+        label={i18n.str`Building number`}
+      />
+      <Input name={`${name}.building_name`} label={i18n.str`Building name`} />
+      <Input name={`${name}.street`} label={i18n.str`Street`} />
+      <Input name={`${name}.post_code`} label={i18n.str`Post code`} />
+      <Input name={`${name}.town_location`} label={i18n.str`Town location`} />
+      <Input name={`${name}.town`} label={i18n.str`Town`} />
+      <Input name={`${name}.district`} label={i18n.str`District`} />
       <Input
         name={`${name}.country_subdivision`}
-        label={i18n`Country subdivision`}
+        label={i18n.str`Country subdivision`}
       />
     </>
   );
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
index 085febea4..7bf39152b 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
@@ -18,9 +18,9 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { h, VNode, Fragment } from "preact";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { Fragment, h, VNode } from "preact";
 import { useCallback, useState } from "preact/hooks";
-import { Translate, Translator, useTranslator } from "../../i18n/index.js";
 import { COUNTRY_TABLE } from "../../utils/constants.js";
 import { undefinedIfEmpty } from "../../utils/table.js";
 import { FormErrors, FormProvider } from "./FormProvider.js";
@@ -69,24 +69,30 @@ function checkAddressChecksum(address: string) {
   return true;
 }
 
-function validateBitcoin(addr: string, i18n: Translator): string | undefined {
+function validateBitcoin(
+  addr: string,
+  i18n: ReturnType<typeof useTranslationContext>["i18n"],
+): string | undefined {
   try {
     const valid = /^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$/.test(addr);
     if (valid) return undefined;
   } catch (e) {
     console.log(e);
   }
-  return i18n`This is not a valid bitcoin address.`;
+  return i18n.str`This is not a valid bitcoin address.`;
 }
 
-function validateEthereum(addr: string, i18n: Translator): string | undefined {
+function validateEthereum(
+  addr: string,
+  i18n: ReturnType<typeof useTranslationContext>["i18n"],
+): string | undefined {
   try {
     const valid = isEthereumAddress(addr);
     if (valid) return undefined;
   } catch (e) {
     console.log(e);
   }
-  return i18n`This is not a valid Ethereum address.`;
+  return i18n.str`This is not a valid Ethereum address.`;
 }
 
 /**
@@ -103,12 +109,15 @@ function validateEthereum(addr: string, i18n: 
Translator): string | undefined {
  * If the remainder is 1, the check digit test is passed and the IBAN might be 
valid.
  *
  */
-function validateIBAN(iban: string, i18n: Translator): string | undefined {
+function validateIBAN(
+  iban: string,
+  i18n: ReturnType<typeof useTranslationContext>["i18n"],
+): string | undefined {
   // Check total length
   if (iban.length < 4)
-    return i18n`IBAN numbers usually have more that 4 digits`;
+    return i18n.str`IBAN numbers usually have more that 4 digits`;
   if (iban.length > 34)
-    return i18n`IBAN numbers usually have less that 34 digits`;
+    return i18n.str`IBAN numbers usually have less that 34 digits`;
 
   const A_code = "A".charCodeAt(0);
   const Z_code = "Z".charCodeAt(0);
@@ -116,7 +125,7 @@ function validateIBAN(iban: string, i18n: Translator): 
string | undefined {
   // check supported country
   const code = IBAN.substr(0, 2);
   const found = code in COUNTRY_TABLE;
-  if (!found) return i18n`IBAN country code not found`;
+  if (!found) return i18n.str`IBAN country code not found`;
 
   // 2.- Move the four initial characters to the end of the string
   const step2 = IBAN.substr(4) + iban.substr(0, 4);
@@ -140,7 +149,8 @@ function validateIBAN(iban: string, i18n: Translator): 
string | undefined {
   }
 
   const checksum = calculate_iban_checksum(step3);
-  if (checksum !== 1) return i18n`IBAN number is not valid, checksum is wrong`;
+  if (checksum !== 1)
+    return i18n.str`IBAN number is not valid, checksum is wrong`;
   return undefined;
 }
 
@@ -175,7 +185,7 @@ export function InputPaytoForm<T>({
       payToPath = `/${value.path1}`;
     }
   }
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const ops = value.options!;
   const url = tryUrl(`payto://${value.target}${payToPath}`);
@@ -188,9 +198,9 @@ export function InputPaytoForm<T>({
   const paytoURL = !url ? "" : url.toString();
 
   const errors: FormErrors<Entity> = {
-    target: value.target === noTargetValue ? i18n`required` : undefined,
+    target: value.target === noTargetValue ? i18n.str`required` : undefined,
     path1: !value.path1
-      ? i18n`required`
+      ? i18n.str`required`
       : value.target === "iban"
       ? validateIBAN(value.path1, i18n)
       : value.target === "bitcoin"
@@ -201,12 +211,12 @@ export function InputPaytoForm<T>({
     path2:
       value.target === "x-taler-bank"
         ? !value.path2
-          ? i18n`required`
+          ? i18n.str`required`
           : undefined
         : undefined,
     options: undefinedIfEmpty({
       "receiver-name": !value.options?.["receiver-name"]
-        ? i18n`required`
+        ? i18n.str`required`
         : undefined,
     }),
   };
@@ -235,23 +245,23 @@ export function InputPaytoForm<T>({
       >
         <InputSelector<Entity>
           name="target"
-          label={i18n`Target type`}
-          tooltip={i18n`Method to use for wire transfer`}
+          label={i18n.str`Target type`}
+          tooltip={i18n.str`Method to use for wire transfer`}
           values={targets}
-          toStr={(v) => (v === noTargetValue ? i18n`Choose one...` : v)}
+          toStr={(v) => (v === noTargetValue ? i18n.str`Choose one...` : v)}
         />
 
         {value.target === "ach" && (
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Routing`}
-              tooltip={i18n`Routing number.`}
+              label={i18n.str`Routing`}
+              tooltip={i18n.str`Routing number.`}
             />
             <Input<Entity>
               name="path2"
-              label={i18n`Account`}
-              tooltip={i18n`Account number.`}
+              label={i18n.str`Account`}
+              tooltip={i18n.str`Account number.`}
             />
           </Fragment>
         )}
@@ -259,8 +269,8 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Code`}
-              tooltip={i18n`Business Identifier Code.`}
+              label={i18n.str`Code`}
+              tooltip={i18n.str`Business Identifier Code.`}
             />
           </Fragment>
         )}
@@ -268,8 +278,8 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Account`}
-              tooltip={i18n`Bank Account Number.`}
+              label={i18n.str`Account`}
+              tooltip={i18n.str`Bank Account Number.`}
               inputExtra={{ style: { textTransform: "uppercase" } }}
             />
           </Fragment>
@@ -278,8 +288,8 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Account`}
-              tooltip={i18n`Unified Payment Interface.`}
+              label={i18n.str`Account`}
+              tooltip={i18n.str`Unified Payment Interface.`}
             />
           </Fragment>
         )}
@@ -287,8 +297,8 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Address`}
-              tooltip={i18n`Bitcoin protocol.`}
+              label={i18n.str`Address`}
+              tooltip={i18n.str`Bitcoin protocol.`}
             />
           </Fragment>
         )}
@@ -296,8 +306,8 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Address`}
-              tooltip={i18n`Ethereum protocol.`}
+              label={i18n.str`Address`}
+              tooltip={i18n.str`Ethereum protocol.`}
             />
           </Fragment>
         )}
@@ -305,8 +315,8 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Address`}
-              tooltip={i18n`Interledger protocol.`}
+              label={i18n.str`Address`}
+              tooltip={i18n.str`Interledger protocol.`}
             />
           </Fragment>
         )}
@@ -315,13 +325,13 @@ export function InputPaytoForm<T>({
           <Fragment>
             <Input<Entity>
               name="path1"
-              label={i18n`Host`}
-              tooltip={i18n`Bank host.`}
+              label={i18n.str`Host`}
+              tooltip={i18n.str`Bank host.`}
             />
             <Input<Entity>
               name="path2"
-              label={i18n`Account`}
-              tooltip={i18n`Bank account.`}
+              label={i18n.str`Account`}
+              tooltip={i18n.str`Bank account.`}
             />
           </Fragment>
         )}
@@ -329,8 +339,8 @@ export function InputPaytoForm<T>({
         {value.target !== noTargetValue && (
           <Input
             name="options.receiver-name"
-            label={i18n`Name`}
-            tooltip={i18n`Bank account owner's name.`}
+            label={i18n.str`Name`}
+            tooltip={i18n.str`Bank account owner's name.`}
           />
         )}
 
@@ -357,7 +367,7 @@ export function InputPaytoForm<T>({
                 />
               </div>
             ))}
-            {!paytos.length && i18n`No accounts yet.`}
+            {!paytos.length && i18n.str`No accounts yet.`}
           </div>
         </div>
 
@@ -365,11 +375,11 @@ export function InputPaytoForm<T>({
           <div class="buttons is-right mt-5">
             <button
               class="button is-info"
-              data-tooltip={i18n`add tax to the tax list`}
+              data-tooltip={i18n.str`add tax to the tax list`}
               disabled={hasErrors}
               onClick={submit}
             >
-              <Translate>Add</Translate>
+              <i18n.Translate>Add</i18n.Translate>
             </button>
           </div>
         )}
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
index fceee9d56..7f65fb8ae 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
@@ -18,11 +18,11 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import emptyImage from "../../assets/empty.png";
 import { MerchantBackend, WithId } from "../../declaration.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { FormErrors, FormProvider } from "./FormProvider.js";
 import { InputWithAddon } from "./InputWithAddon.js";
 
@@ -50,7 +50,7 @@ export function InputSearchProduct({
   const errors: FormErrors<ProductSearch> = {
     name: undefined,
   };
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (selected) {
     return (
@@ -63,10 +63,11 @@ export function InputSearchProduct({
         <div class="media-content">
           <div class="content">
             <p class="media-meta">
-              <Translate>Product id</Translate>: <b>{selected.id}</b>
+              <i18n.Translate>Product id</i18n.Translate>: <b>{selected.id}</b>
             </p>
             <p>
-              <Translate>Description</Translate>: {selected.description}
+              <i18n.Translate>Description</i18n.Translate>:{" "}
+              {selected.description}
             </p>
             <div class="buttons is-right mt-5">
               <button
@@ -90,8 +91,8 @@ export function InputSearchProduct({
     >
       <InputWithAddon<ProductSearch>
         name="name"
-        label={i18n`Product`}
-        tooltip={i18n`search products by it's description or id`}
+        label={i18n.str`Product`}
+        tooltip={i18n.str`search products by it's description or id`}
         addonAfter={
           <span class="icon">
             <i class="mdi mdi-magnify" />
@@ -120,6 +121,7 @@ interface ProductListProps {
 }
 
 function ProductList({ name, onSelect, list }: ProductListProps) {
+  const { i18n } = useTranslationContext();
   if (!name) {
     /* FIXME
       this BR is added to occupy the space that will be added when the 
@@ -146,7 +148,9 @@ function ProductList({ name, onSelect, list }: 
ProductListProps) {
         <div class="dropdown-content">
           {!filtered.length ? (
             <div class="dropdown-item">
-              <Translate>no products found with that description</Translate>
+              <i18n.Translate>
+                no products found with that description
+              </i18n.Translate>
             </div>
           ) : (
             filtered.map((p) => (
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx
index 799978683..d900168b4 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx
@@ -18,14 +18,15 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { InputProps, useField } from "./useField.js";
 
 export type Props<T> = InputProps<T>;
 
 const TokenStatus = ({ prev, post }: any) => {
+  const { i18n } = useTranslationContext();
   if (
     (prev === undefined || prev === null) &&
     (post === undefined || post === null)
@@ -33,11 +34,11 @@ const TokenStatus = ({ prev, post }: any) => {
     return null;
   return prev === post ? null : post === null ? (
     <span class="tag is-danger is-align-self-center ml-2">
-      <Translate>Deleting</Translate>
+      <i18n.Translate>Deleting</i18n.Translate>
     </span>
   ) : (
     <span class="tag is-warning is-align-self-center ml-2">
-      <Translate>Changing</Translate>
+      <i18n.Translate>Changing</i18n.Translate>
     </span>
   );
 };
@@ -55,7 +56,7 @@ export function InputSecured<T>({
   const [active, setActive] = useState(false);
   const [newValue, setNuewValue] = useState("");
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
@@ -84,7 +85,7 @@ export function InputSecured<T>({
                     <i class="mdi mdi-lock-reset" />
                   </div>
                   <span>
-                    <Translate>Manage access token</Translate>
+                    <i18n.Translate>Manage access token</i18n.Translate>
                   </span>
                 </button>
                 <TokenStatus prev={initial} post={value} />
@@ -125,7 +126,7 @@ export function InputSecured<T>({
                       <i class="mdi mdi-lock-outline" />
                     </div>
                     <span>
-                      <Translate>Update</Translate>
+                      <i18n.Translate>Update</i18n.Translate>
                     </span>
                   </button>
                 </div>
@@ -154,7 +155,7 @@ export function InputSecured<T>({
                       <i class="mdi mdi-lock-open-variant" />
                     </div>
                     <span>
-                      <Translate>Remove</Translate>
+                      <i18n.Translate>Remove</i18n.Translate>
                     </span>
                   </button>
                 </div>
@@ -171,7 +172,7 @@ export function InputSecured<T>({
                       <i class="mdi mdi-lock-open-variant" />
                     </div>
                     <span>
-                      <Translate>Cancel</Translate>
+                      <i18n.Translate>Cancel</i18n.Translate>
                     </span>
                   </button>
                 </div>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
index 57aa5968d..1052aaaae 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
@@ -18,17 +18,16 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h } from "preact";
-import { MerchantBackend, Timestamp } from "../../declaration.js";
-import { InputProps, useField } from "./useField.js";
-import { FormProvider, FormErrors } from "./FormProvider.js";
 import { useLayoutEffect, useState } from "preact/hooks";
-import { Input } from "./Input.js";
-import { InputGroup } from "./InputGroup.js";
-import { InputNumber } from "./InputNumber.js";
+import { MerchantBackend, Timestamp } from "../../declaration.js";
+import { FormErrors, FormProvider } from "./FormProvider.js";
 import { InputDate } from "./InputDate.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
+import { InputGroup } from "./InputGroup.js";
 import { InputLocation } from "./InputLocation.js";
+import { InputNumber } from "./InputNumber.js";
+import { InputProps, useField } from "./useField.js";
 
 export interface Props<T> extends InputProps<T> {
   alreadyExist?: boolean;
@@ -64,7 +63,7 @@ export function InputStock<T>({
     incoming: 0,
     lost: 0,
   });
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   useLayoutEffect(() => {
     if (!formValue) {
@@ -97,7 +96,7 @@ export function InputStock<T>({
               {!alreadyExist ? (
                 <button
                   class="button"
-                  data-tooltip={i18n`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,
@@ -107,17 +106,17 @@ export function InputStock<T>({
                   }}
                 >
                   <span>
-                    <Translate>Manage stock</Translate>
+                    <i18n.Translate>Manage stock</i18n.Translate>
                   </span>
                 </button>
               ) : (
                 <button
                   class="button"
-                  data-tooltip={i18n`this product has been configured without 
stock control`}
+                  data-tooltip={i18n.str`this product has been configured 
without stock control`}
                   disabled
                 >
                   <span>
-                    <Translate>Infinite</Translate>
+                    <i18n.Translate>Infinite</i18n.Translate>
                   </span>
                 </button>
               )}
@@ -134,7 +133,7 @@ export function InputStock<T>({
   const stockAddedErrors: FormErrors<typeof addedStock> = {
     lost:
       currentStock + addedStock.incoming < addedStock.lost
-        ? i18n`lost cannot be greater than current and incoming (max ${
+        ? i18n.str`lost cannot be greater than current and incoming (max ${
             currentStock + addedStock.incoming
           })`
         : undefined,
@@ -142,8 +141,8 @@ export function InputStock<T>({
 
   // const stockUpdateDescription = stockAddedErrors.lost ? '' : (
   //   !!addedStock.incoming || !!addedStock.lost ?
-  //     i18n`current stock will change from ${currentStock} to ${currentStock 
+ addedStock.incoming - addedStock.lost}` :
-  //     i18n`current stock will stay at ${currentStock}`
+  //     i18n.str`current stock will change from ${currentStock} to 
${currentStock + addedStock.incoming - addedStock.lost}` :
+  //     i18n.str`current stock will stay at ${currentStock}`
   // )
 
   return (
@@ -174,8 +173,8 @@ export function InputStock<T>({
                   object={addedStock}
                   valueHandler={setAddedStock as any}
                 >
-                  <InputNumber name="incoming" label={i18n`Incoming`} />
-                  <InputNumber name="lost" label={i18n`Lost`} />
+                  <InputNumber name="incoming" label={i18n.str`Incoming`} />
+                  <InputNumber name="lost" label={i18n.str`Lost`} />
                 </FormProvider>
 
                 {/* <div class="field is-horizontal">
@@ -190,17 +189,17 @@ export function InputStock<T>({
             ) : (
               <InputNumber<Entity>
                 name="current"
-                label={i18n`Current`}
+                label={i18n.str`Current`}
                 side={
                   <button
                     class="button is-danger"
-                    data-tooltip={i18n`remove stock control for this product`}
+                    data-tooltip={i18n.str`remove stock control for this 
product`}
                     onClick={(): void => {
                       valueHandler(undefined as any);
                     }}
                   >
                     <span>
-                      <Translate>without stock</Translate>
+                      <i18n.Translate>without stock</i18n.Translate>
                     </span>
                   </button>
                 }
@@ -209,11 +208,14 @@ export function InputStock<T>({
 
             <InputDate<Entity>
               name="nextRestock"
-              label={i18n`Next restock`}
+              label={i18n.str`Next restock`}
               withTimestampSupport
             />
 
-            <InputGroup<Entity> name="address" label={i18n`Delivery address`}>
+            <InputGroup<Entity>
+              name="address"
+              label={i18n.str`Delivery address`}
+            >
               <InputLocation name="address" />
             </InputGroup>
           </FormProvider>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
index d95463790..552ee73e2 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
@@ -18,11 +18,11 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useCallback, useState } from "preact/hooks";
 import * as yup from "yup";
 import { MerchantBackend } from "../../declaration.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { TaxSchema as schema } from "../../schemas/index.js";
 import { FormErrors, FormProvider } from "./FormProvider.js";
 import { Input } from "./Input.js";
@@ -67,7 +67,7 @@ export function InputTaxes<T>({
     valueHandler({});
   }, [value]);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   //FIXME: translating plural singular
   return (
@@ -110,34 +110,35 @@ export function InputTaxes<T>({
                 />
               </div>
             ))}
-            {!taxes.length && i18n`No taxes configured for this product.`}
+            {!taxes.length && i18n.str`No taxes configured for this product.`}
           </div>
         </div>
 
         <Input<Entity>
           name="tax"
-          label={i18n`Amount`}
-          tooltip={i18n`Taxes can be in currencies that differ from the main 
currency used by the merchant.`}
+          label={i18n.str`Amount`}
+          tooltip={i18n.str`Taxes can be in currencies that differ from the 
main currency used by the merchant.`}
         >
-          <Translate>
-            Enter currency and value separated with a colon, e.g. "USD:2.3".
-          </Translate>
+          <i18n.Translate>
+            Enter currency and value separated with a colon, e.g.
+            &quot;USD:2.3&quot;.
+          </i18n.Translate>
         </Input>
 
         <Input<Entity>
           name="name"
-          label={i18n`Description`}
-          tooltip={i18n`Legal name of the tax, e.g. VAT or import duties.`}
+          label={i18n.str`Description`}
+          tooltip={i18n.str`Legal name of the tax, e.g. VAT or import duties.`}
         />
 
         <div class="buttons is-right mt-5">
           <button
             class="button is-info"
-            data-tooltip={i18n`add tax to the tax list`}
+            data-tooltip={i18n.str`add tax to the tax list`}
             disabled={hasErrors}
             onClick={submit}
           >
-            <Translate>Add</Translate>
+            <i18n.Translate>Add</i18n.Translate>
           </button>
         </div>
       </FormProvider>
diff --git 
a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
 
b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
index 9dfa33840..c497773d9 100644
--- 
a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
@@ -19,9 +19,9 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useBackendContext } from "../../context/backend.js";
-import { useTranslator } from "../../i18n/index.js";
 import { Entity } from "../../paths/admin/create/CreatePage.js";
 import { Input } from "../form/Input.js";
 import { InputCurrency } from "../form/InputCurrency.js";
@@ -39,7 +39,7 @@ export function DefaultInstanceFormFields({
   readonlyId?: boolean;
   showId: boolean;
 }): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const backend = useBackendContext();
   return (
     <Fragment>
@@ -48,86 +48,86 @@ export function DefaultInstanceFormFields({
           name="id"
           addonBefore={`${backend.url}/instances/`}
           readonly={readonlyId}
-          label={i18n`Identifier`}
-          tooltip={i18n`Name of the instance in URLs. The 'default' instance 
is special in that it is used to administer other instances.`}
+          label={i18n.str`Identifier`}
+          tooltip={i18n.str`Name of the instance in URLs. The 'default' 
instance is special in that it is used to administer other instances.`}
         />
       )}
 
       <Input<Entity>
         name="name"
-        label={i18n`Business name`}
-        tooltip={i18n`Legal name of the business represented by this 
instance.`}
+        label={i18n.str`Business name`}
+        tooltip={i18n.str`Legal name of the business represented by this 
instance.`}
       />
 
       <Input<Entity>
         name="email"
-        label={i18n`Email`}
-        tooltip={i18n`Contact email`}
+        label={i18n.str`Email`}
+        tooltip={i18n.str`Contact email`}
       />
 
       <Input<Entity>
         name="website"
-        label={i18n`Website URL`}
-        tooltip={i18n`URL.`}
+        label={i18n.str`Website URL`}
+        tooltip={i18n.str`URL.`}
       />
 
       <InputImage<Entity>
         name="logo"
-        label={i18n`Logo`}
-        tooltip={i18n`Logo image.`}
+        label={i18n.str`Logo`}
+        tooltip={i18n.str`Logo image.`}
       />
 
       <InputPaytoForm<Entity>
         name="payto_uris"
-        label={i18n`Bank account`}
-        tooltip={i18n`URI specifying bank account for crediting revenue.`}
+        label={i18n.str`Bank account`}
+        tooltip={i18n.str`URI specifying bank account for crediting revenue.`}
       />
 
       <InputCurrency<Entity>
         name="default_max_deposit_fee"
-        label={i18n`Default max deposit fee`}
-        tooltip={i18n`Maximum deposit fees this merchant is willing to pay per 
order by default.`}
+        label={i18n.str`Default max deposit fee`}
+        tooltip={i18n.str`Maximum deposit fees this merchant is willing to pay 
per order by default.`}
       />
 
       <InputCurrency<Entity>
         name="default_max_wire_fee"
-        label={i18n`Default max wire fee`}
-        tooltip={i18n`Maximum wire fees this merchant is willing to pay per 
wire transfer by default.`}
+        label={i18n.str`Default max wire fee`}
+        tooltip={i18n.str`Maximum wire fees this merchant is willing to pay 
per wire transfer by default.`}
       />
 
       <Input<Entity>
         name="default_wire_fee_amortization"
-        label={i18n`Default wire fee amortization`}
-        tooltip={i18n`Number of orders excess wire transfer fees will be 
divided by to compute per order surcharge.`}
+        label={i18n.str`Default wire fee amortization`}
+        tooltip={i18n.str`Number of orders excess wire transfer fees will be 
divided by to compute per order surcharge.`}
       />
 
       <InputGroup
         name="address"
-        label={i18n`Address`}
-        tooltip={i18n`Physical location of the merchant.`}
+        label={i18n.str`Address`}
+        tooltip={i18n.str`Physical location of the merchant.`}
       >
         <InputLocation name="address" />
       </InputGroup>
 
       <InputGroup
         name="jurisdiction"
-        label={i18n`Jurisdiction`}
-        tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}
+        label={i18n.str`Jurisdiction`}
+        tooltip={i18n.str`Jurisdiction for legal disputes with the merchant.`}
       >
         <InputLocation name="jurisdiction" />
       </InputGroup>
 
       <InputDuration<Entity>
         name="default_pay_delay"
-        label={i18n`Default payment delay`}
+        label={i18n.str`Default payment delay`}
         withForever
-        tooltip={i18n`Time customers have to pay an order before the offer 
expires by default.`}
+        tooltip={i18n.str`Time customers have to pay an order before the offer 
expires by default.`}
       />
 
       <InputDuration<Entity>
         name="default_wire_transfer_delay"
-        label={i18n`Default wire transfer delay`}
-        tooltip={i18n`Maximum time an exchange is allowed to delay wiring 
funds to the merchant, enabling it to aggregate smaller payments into larger 
wire transfers and reducing wire fees.`}
+        label={i18n.str`Default wire transfer delay`}
+        tooltip={i18n.str`Maximum time an exchange is allowed to delay wiring 
funds to the merchant, enabling it to aggregate smaller payments into larger 
wire transfers and reducing wire fees.`}
         withForever
       />
     </Fragment>
diff --git 
a/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx
index d618d6480..a1de8541e 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import langIcon from "../../assets/icons/languageicon.svg";
-import { useTranslationContext } from "../../context/translation.js";
-import { strings as messages } from "../../i18n/strings";
+import { strings as messages } from "../../i18n/strings.js";
 
 type LangsNames = {
   [P in keyof typeof messages]: string;
diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
index ea49be99a..92d144b1a 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -19,13 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
-import { useCallback } from "preact/hooks";
 import { useBackendContext } from "../../context/backend.js";
 import { useConfigContext } from "../../context/config.js";
-import { useInstanceContext } from "../../context/instance.js";
 import { useInstanceKYCDetails } from "../../hooks/instance.js";
-import { Translate } from "../../i18n/index.js";
 import { LangSelector } from "./LangSelector.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : 
undefined;
@@ -48,7 +46,7 @@ export function Sidebar({
 }: Props): VNode {
   const config = useConfigContext();
   const backend = useBackendContext();
-
+  const { i18n } = useTranslationContext();
   const kycStatus = useInstanceKYCDetails();
   const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
 
@@ -81,7 +79,7 @@ export function Sidebar({
         {instance ? (
           <Fragment>
             <p class="menu-label">
-              <Translate>Instance</Translate>
+              <i18n.Translate>Instance</i18n.Translate>
             </p>
             <ul class="menu-list">
               <li>
@@ -90,7 +88,7 @@ export function Sidebar({
                     <i class="mdi mdi-square-edit-outline" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>Settings</Translate>
+                    <i18n.Translate>Settings</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -100,7 +98,7 @@ export function Sidebar({
                     <i class="mdi mdi-cash-register" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>Orders</Translate>
+                    <i18n.Translate>Orders</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -110,7 +108,7 @@ export function Sidebar({
                     <i class="mdi mdi-shopping" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>Products</Translate>
+                    <i18n.Translate>Products</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -120,7 +118,7 @@ export function Sidebar({
                     <i class="mdi mdi-bank" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>Transfers</Translate>
+                    <i18n.Translate>Transfers</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -130,7 +128,7 @@ export function Sidebar({
                     <i class="mdi mdi-newspaper" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>Templates</Translate>
+                    <i18n.Translate>Templates</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -156,7 +154,7 @@ export function Sidebar({
           </Fragment>
         ) : undefined}
         <p class="menu-label">
-          <Translate>Connection</Translate>
+          <i18n.Translate>Connection</i18n.Translate>
         </p>
         <ul class="menu-list">
           <li>
@@ -190,7 +188,7 @@ export function Sidebar({
           {admin && !mimic && (
             <Fragment>
               <p class="menu-label">
-                <Translate>Instances</Translate>
+                <i18n.Translate>Instances</i18n.Translate>
               </p>
               <li>
                 <a href={"/instance/new"} class="has-icon">
@@ -198,7 +196,7 @@ export function Sidebar({
                     <i class="mdi mdi-plus" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>New</Translate>
+                    <i18n.Translate>New</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -208,7 +206,7 @@ export function Sidebar({
                     <i class="mdi mdi-format-list-bulleted" />
                   </span>
                   <span class="menu-item-label">
-                    <Translate>List</Translate>
+                    <i18n.Translate>List</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -223,7 +221,7 @@ export function Sidebar({
                 <i class="mdi mdi-logout default" />
               </span>
               <span class="menu-item-label">
-                <Translate>Log out</Translate>
+                <i18n.Translate>Log out</i18n.Translate>
               </span>
             </a>
           </li>
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
index 23935ec98..ccbd9442f 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -15,7 +15,6 @@
  */
 
 import { ComponentChildren, Fragment, h, VNode } from "preact";
-import Match from "preact-router/match";
 import { useEffect, useState } from "preact/hooks";
 import { AdminPaths } from "../../AdminRoutes.js";
 import { InstancePaths } from "../../InstanceRoutes.js";
@@ -58,6 +57,7 @@ function getAdminTitle(path: string, instance: string) {
 
 interface MenuProps {
   title?: string;
+  path: string;
   instance: string;
   admin?: boolean;
   onLogout?: () => void;
@@ -81,64 +81,59 @@ export function Menu({
   onLogout,
   title,
   instance,
+  path,
   admin,
   setInstanceName,
 }: MenuProps): VNode {
   const [mobileOpen, setMobileOpen] = useState(false);
 
+  const titleWithSubtitle = title
+    ? title
+    : !admin
+    ? getInstanceTitle(path, instance)
+    : getAdminTitle(path, instance);
+  const adminInstance = instance === "default";
+  const mimic = admin && !adminInstance;
   return (
-    <Match>
-      {({ path }: any) => {
-        const titleWithSubtitle = title
-          ? title
-          : !admin
-          ? getInstanceTitle(path, instance)
-          : getAdminTitle(path, instance);
-        const adminInstance = instance === "default";
-        const mimic = admin && !adminInstance;
-        return (
-          <WithTitle title={titleWithSubtitle}>
-            <div
-              class={mobileOpen ? "has-aside-mobile-expanded" : ""}
-              onClick={() => setMobileOpen(false)}
-            >
-              <NavigationBar
-                onMobileMenu={() => setMobileOpen(!mobileOpen)}
-                title={titleWithSubtitle}
-              />
-
-              {onLogout && (
-                <Sidebar
-                  onLogout={onLogout}
-                  admin={admin}
-                  mimic={mimic}
-                  instance={instance}
-                  mobile={mobileOpen}
-                />
-              )}
-
-              {mimic && (
-                <nav class="level">
-                  <div class="level-item has-text-centered 
has-background-warning">
-                    <p class="is-size-5">
-                      You are viewing the instance <b>"{instance}"</b>.{" "}
-                      <a
-                        href="#/instances"
-                        onClick={(e) => {
-                          setInstanceName("default");
-                        }}
-                      >
-                        go back
-                      </a>
-                    </p>
-                  </div>
-                </nav>
-              )}
+    <WithTitle title={titleWithSubtitle}>
+      <div
+        class={mobileOpen ? "has-aside-mobile-expanded" : ""}
+        onClick={() => setMobileOpen(false)}
+      >
+        <NavigationBar
+          onMobileMenu={() => setMobileOpen(!mobileOpen)}
+          title={titleWithSubtitle}
+        />
+
+        {onLogout && (
+          <Sidebar
+            onLogout={onLogout}
+            admin={admin}
+            mimic={mimic}
+            instance={instance}
+            mobile={mobileOpen}
+          />
+        )}
+
+        {mimic && (
+          <nav class="level">
+            <div class="level-item has-text-centered has-background-warning">
+              <p class="is-size-5">
+                You are viewing the instance <b>&quot;{instance}&quot;</b>.{" 
"}
+                <a
+                  href="#/instances"
+                  onClick={(e) => {
+                    setInstanceName("default");
+                  }}
+                >
+                  go back
+                </a>
+              </p>
             </div>
-          </WithTitle>
-        );
-      }}
-    </Match>
+          </nav>
+        )}
+      </div>
+    </WithTitle>
   );
 }
 
diff --git a/packages/merchant-backoffice-ui/src/components/modal/index.tsx 
b/packages/merchant-backoffice-ui/src/components/modal/index.tsx
index 6e5575f63..c9c7d0ce5 100644
--- a/packages/merchant-backoffice-ui/src/components/modal/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/modal/index.tsx
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { ComponentChildren, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { useInstanceContext } from "../../context/instance.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
-import { Loading, Spinner } from "../exception/loading.js";
+import { Spinner } from "../exception/loading.js";
 import { FormProvider } from "../form/FormProvider.js";
 import { Input } from "../form/Input.js";
 
@@ -49,6 +49,7 @@ export function ConfirmModal({
   disabled,
   label = "Confirm",
 }: Props): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class={active ? "modal is-active" : "modal"}>
       <div class="modal-background " onClick={onCancel} />
@@ -65,14 +66,14 @@ export function ConfirmModal({
         <footer class="modal-card-foot">
           <div class="buttons is-right" style={{ width: "100%" }}>
             <button class="button " onClick={onCancel}>
-              <Translate>Cancel</Translate>
+              <i18n.Translate>Cancel</i18n.Translate>
             </button>
             <button
               class={danger ? "button is-danger " : "button is-info "}
               disabled={disabled}
               onClick={onConfirm}
             >
-              <Translate>{label}</Translate>
+              <i18n.Translate>{label}</i18n.Translate>
             </button>
           </div>
         </footer>
@@ -94,6 +95,7 @@ export function ContinueModal({
   children,
   disabled,
 }: Props): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class={active ? "modal is-active" : "modal"}>
       <div class="modal-background " onClick={onCancel} />
@@ -110,7 +112,7 @@ export function ContinueModal({
               disabled={disabled}
               onClick={onConfirm}
             >
-              <Translate>Continue</Translate>
+              <i18n.Translate>Continue</i18n.Translate>
             </button>
           </div>
         </footer>
@@ -147,6 +149,7 @@ export function ClearConfirmModal({
   onConfirm,
   children,
 }: Props & { onClear?: () => void }): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="modal is-active">
       <div class="modal-background " onClick={onCancel} />
@@ -163,19 +166,19 @@ export function ClearConfirmModal({
               onClick={onClear}
               disabled={onClear === undefined}
             >
-              <Translate>Clear</Translate>
+              <i18n.Translate>Clear</i18n.Translate>
             </button>
           )}
           <div class="buttons is-right" style={{ width: "100%" }}>
             <button class="button " onClick={onCancel}>
-              <Translate>Cancel</Translate>
+              <i18n.Translate>Cancel</i18n.Translate>
             </button>
             <button
               class="button is-info"
               onClick={onConfirm}
               disabled={onConfirm === undefined}
             >
-              <Translate>Confirm</Translate>
+              <i18n.Translate>Confirm</i18n.Translate>
             </button>
           </div>
         </footer>
@@ -210,7 +213,7 @@ export function DeleteModal({
       onConfirm={() => onConfirm(element.id)}
     >
       <p>
-        If you delete the instance named <b>"{element.name}"</b> (ID:{" "}
+        If you delete the instance named <b>&quot;{element.name}&quot;</b> 
(ID:{" "}
         <b>{element.id}</b>), the merchant will no longer be able to process
         orders or refunds
       </p>
@@ -241,12 +244,13 @@ export function PurgeModal({
       onConfirm={() => onConfirm(element.id)}
     >
       <p>
-        If you purge the instance named <b>"{element.name}"</b> (ID:{" "}
-        <b>{element.id}</b>), you will also delete all it's transaction data.
+        If you purge the instance named <b>&quot;{element.name}&quot;</b> 
(ID:{" "}
+        <b>{element.id}</b>), you will also delete all it&apos;s transaction
+        data.
       </p>
       <p>
         The instance will disappear from your list, and you will no longer be
-        able to access it's data.
+        able to access it&apos;s data.
       </p>
       <p class="warning">
         Purging an instance <b>cannot be undone</b>.
@@ -275,20 +279,22 @@ export function UpdateTokenModal({
     new_token: "",
     repeat_token: "",
   });
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token;
   const errors = {
     old_token: hasInputTheCorrectOldToken
-      ? i18n`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`cannot be empty`
+      ? i18n.str`cannot be empty`
       : form.new_token === form.old_token
-      ? i18n`cannot be the same as the old token`
+      ? i18n.str`cannot be the same as the old token`
       : undefined,
     repeat_token:
-      form.new_token !== form.repeat_token ? i18n`is not the same` : undefined,
+      form.new_token !== form.repeat_token
+        ? i18n.str`is not the same`
+        : undefined,
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -297,7 +303,7 @@ export function UpdateTokenModal({
 
   const instance = useInstanceContext();
 
-  const text = i18n`You are updating the access token from instance with id 
${instance.id}`;
+  const text = i18n.str`You are updating the access token from instance with 
id ${instance.id}`;
 
   return (
     <ClearConfirmModal
@@ -313,28 +319,28 @@ export function UpdateTokenModal({
             {oldToken && (
               <Input<State>
                 name="old_token"
-                label={i18n`Old access token`}
-                tooltip={i18n`access token currently in use`}
+                label={i18n.str`Old access token`}
+                tooltip={i18n.str`access token currently in use`}
                 inputType="password"
               />
             )}
             <Input<State>
               name="new_token"
-              label={i18n`New access token`}
-              tooltip={i18n`next access token to be used`}
+              label={i18n.str`New access token`}
+              tooltip={i18n.str`next access token to be used`}
               inputType="password"
             />
             <Input<State>
               name="repeat_token"
-              label={i18n`Repeat access token`}
-              tooltip={i18n`confirm the same access token`}
+              label={i18n.str`Repeat access token`}
+              tooltip={i18n.str`confirm the same access token`}
               inputType="password"
             />
           </FormProvider>
           <p>
-            <Translate>
+            <i18n.Translate>
               Clearing the access token will mean public access to the instance
-            </Translate>
+            </i18n.Translate>
           </p>
         </div>
         <div class="column" />
@@ -353,16 +359,18 @@ export function SetTokenNewInstanceModal({
     new_token: "",
     repeat_token: "",
   });
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const errors = {
     new_token: !form.new_token
-      ? i18n`cannot be empty`
+      ? i18n.str`cannot be empty`
       : form.new_token === form.old_token
-      ? i18n`cannot be the same as the old access token`
+      ? i18n.str`cannot be the same as the old access token`
       : undefined,
     repeat_token:
-      form.new_token !== form.repeat_token ? i18n`is not the same` : undefined,
+      form.new_token !== form.repeat_token
+        ? i18n.str`is not the same`
+        : undefined,
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -374,7 +382,7 @@ export function SetTokenNewInstanceModal({
       <div class="modal-background " onClick={onCancel} />
       <div class="modal-card">
         <header class="modal-card-head">
-          <p class="modal-card-title">{i18n`You are setting the access token 
for the new instance`}</p>
+          <p class="modal-card-title">{i18n.str`You are setting the access 
token for the new instance`}</p>
           <button class="delete " aria-label="close" onClick={onCancel} />
         </header>
         <section class="modal-card-body is-main-section">
@@ -388,22 +396,22 @@ export function SetTokenNewInstanceModal({
               >
                 <Input<State>
                   name="new_token"
-                  label={i18n`New access token`}
-                  tooltip={i18n`next access token to be used`}
+                  label={i18n.str`New access token`}
+                  tooltip={i18n.str`next access token to be used`}
                   inputType="password"
                 />
                 <Input<State>
                   name="repeat_token"
-                  label={i18n`Repeat access token`}
-                  tooltip={i18n`confirm the same access token`}
+                  label={i18n.str`Repeat access token`}
+                  tooltip={i18n.str`confirm the same access token`}
                   inputType="password"
                 />
               </FormProvider>
               <p>
-                <Translate>
+                <i18n.Translate>
                   With external authorization method no check will be done by
                   the merchant backend
-                </Translate>
+                </i18n.Translate>
               </p>
             </div>
             <div class="column" />
@@ -416,19 +424,19 @@ export function SetTokenNewInstanceModal({
               onClick={onClear}
               disabled={onClear === undefined}
             >
-              <Translate>Set external authorization</Translate>
+              <i18n.Translate>Set external authorization</i18n.Translate>
             </button>
           )}
           <div class="buttons is-right" style={{ width: "100%" }}>
             <button class="button " onClick={onCancel}>
-              <Translate>Cancel</Translate>
+              <i18n.Translate>Cancel</i18n.Translate>
             </button>
             <button
               class="button is-info"
               onClick={() => onConfirm(form.new_token!)}
               disabled={hasErrors}
             >
-              <Translate>Set access token</Translate>
+              <i18n.Translate>Set access token</i18n.Translate>
             </button>
           </div>
         </footer>
@@ -443,14 +451,14 @@ export function SetTokenNewInstanceModal({
 }
 
 export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="modal is-active">
       <div class="modal-background " onClick={onCancel} />
       <div class="modal-card">
         <header class="modal-card-head">
           <p class="modal-card-title">
-            <Translate>Operation in progress...</Translate>
+            <i18n.Translate>Operation in progress...</i18n.Translate>
           </p>
         </header>
         <section class="modal-card-body">
@@ -459,12 +467,12 @@ export function LoadingModal({ onCancel }: { onCancel: () 
=> void }): VNode {
             <Spinner />
             <div class="column" />
           </div>
-          <p>{i18n`The operation will be automatically canceled after 
${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
+          <p>{i18n.str`The operation will be automatically canceled after 
${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
         </section>
         <footer class="modal-card-foot">
           <div class="buttons is-right" style={{ width: "100%" }}>
             <button class="button " onClick={onCancel}>
-              <Translate>Cancel</Translate>
+              <i18n.Translate>Cancel</i18n.Translate>
             </button>
           </div>
         </footer>
diff --git 
a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx 
b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx
index 629e50b8c..0e8baac06 100644
--- a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx
+++ b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx
@@ -19,9 +19,9 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { useTranslator } from "../../i18n/index.js";
 import "../../scss/DurationPicker.scss";
 
 export interface Props {
@@ -46,13 +46,13 @@ export function DurationPicker({
   const ms = ss * 60;
   const hs = ms * 60;
   const ds = hs * 24;
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="rdp-picker">
       {days && (
         <DurationColumn
-          unit={i18n`days`}
+          unit={i18n.str`days`}
           max={99}
           value={Math.floor(value / ds)}
           onDecrease={value >= ds ? () => onChange(value - ds) : undefined}
@@ -62,7 +62,7 @@ export function DurationPicker({
       )}
       {hours && (
         <DurationColumn
-          unit={i18n`hours`}
+          unit={i18n.str`hours`}
           max={23}
           min={1}
           value={Math.floor(value / hs) % 24}
@@ -73,7 +73,7 @@ export function DurationPicker({
       )}
       {minutes && (
         <DurationColumn
-          unit={i18n`minutes`}
+          unit={i18n.str`minutes`}
           max={59}
           min={1}
           value={Math.floor(value / ms) % 60}
@@ -84,7 +84,7 @@ export function DurationPicker({
       )}
       {seconds && (
         <DurationColumn
-          unit={i18n`seconds`}
+          unit={i18n.str`seconds`}
           max={59}
           value={Math.floor(value / ss) % 60}
           onDecrease={value >= ss ? () => onChange(value - ss) : undefined}
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
 
b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
index da47f1be3..0cb2d555e 100644
--- 
a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
@@ -13,14 +13,14 @@
  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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { FormProvider, FormErrors } from "../form/FormProvider.js";
-import { InputNumber } from "../form/InputNumber.js";
-import { InputSearchProduct } from "../form/InputSearchProduct.js";
 import { MerchantBackend, WithId } from "../../declaration.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
 import { ProductMap } from "../../paths/instance/orders/create/CreatePage.js";
+import { FormErrors, FormProvider } from "../form/FormProvider.js";
+import { InputNumber } from "../form/InputNumber.js";
+import { InputSearchProduct } from "../form/InputSearchProduct.js";
 
 type Form = {
   product: MerchantBackend.Products.ProductDetail & WithId;
@@ -45,21 +45,23 @@ export function InventoryProductForm({
   const [state, setState] = useState<Partial<Form>>(initialState);
   const [errors, setErrors] = useState<FormErrors<Form>>({});
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const productWithInfiniteStock =
     state.product && state.product.total_stock === -1;
 
   const submit = (): void => {
     if (!state.product) {
-      setErrors({ product: i18n`You must enter a valid product identifier.` });
+      setErrors({
+        product: i18n.str`You must enter a valid product identifier.`,
+      });
       return;
     }
     if (productWithInfiniteStock) {
       onAddProduct(state.product, 1);
     } else {
       if (!state.quantity || state.quantity <= 0) {
-        setErrors({ quantity: i18n`Quantity must be greater than 0!` });
+        setErrors({ quantity: i18n.str`Quantity must be greater than 0!` });
         return;
       }
       const currentStock =
@@ -71,7 +73,7 @@ export function InventoryProductForm({
         if (state.quantity + p.quantity > currentStock) {
           const left = currentStock - p.quantity;
           setErrors({
-            quantity: i18n`This quantity exceeds remaining stock. Currently, 
only ${left} units remain unreserved in stock.`,
+            quantity: i18n.str`This quantity exceeds remaining stock. 
Currently, only ${left} units remain unreserved in stock.`,
           });
           return;
         }
@@ -80,7 +82,7 @@ export function InventoryProductForm({
         if (state.quantity > currentStock) {
           const left = currentStock;
           setErrors({
-            quantity: i18n`This quantity exceeds remaining stock. Currently, 
only ${left} units remain unreserved in stock.`,
+            quantity: i18n.str`This quantity exceeds remaining stock. 
Currently, only ${left} units remain unreserved in stock.`,
           });
           return;
         }
@@ -104,15 +106,15 @@ export function InventoryProductForm({
             {!productWithInfiniteStock && (
               <InputNumber<Form>
                 name="quantity"
-                label={i18n`Quantity`}
-                tooltip={i18n`how many products will be added`}
+                label={i18n.str`Quantity`}
+                tooltip={i18n.str`how many products will be added`}
               />
             )}
           </div>
           <div class="column">
             <div class="buttons is-right">
               <button class="button is-success" onClick={submit}>
-                <Translate>Add from inventory</Translate>
+                <i18n.Translate>Add from inventory</i18n.Translate>
               </button>
             </div>
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
 
b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
index fe9692c02..3e7262657 100644
--- 
a/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx
@@ -13,19 +13,19 @@
  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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useCallback, useEffect, useState } from "preact/hooks";
 import * as yup from "yup";
+import { MerchantBackend } from "../../declaration.js";
+import { useListener } from "../../hooks/listener.js";
+import { NonInventoryProductSchema as schema } from "../../schemas/index.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 { MerchantBackend } from "../../declaration.js";
-import { useListener } from "../../hooks/listener.js";
-import { Translate, useTranslator } from "../../i18n/index.js";
-import { NonInventoryProductSchema as schema } from "../../schemas/index.js";
 
 type Entity = MerchantBackend.Product;
 
@@ -62,17 +62,17 @@ export function NonInventoryProductFrom({
     return Promise.resolve();
   });
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
       <div class="buttons">
         <button
           class="button is-success"
-          data-tooltip={i18n`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)}
         >
-          <Translate>Add custom product</Translate>
+          <i18n.Translate>Add custom product</i18n.Translate>
         </button>
       </div>
       {showCreateProduct && (
@@ -83,7 +83,7 @@ export function NonInventoryProductFrom({
           />
           <div class="modal-card">
             <header class="modal-card-head">
-              <p class="modal-card-title">{i18n`Complete information of the 
product`}</p>
+              <p class="modal-card-title">{i18n.str`Complete information of 
the product`}</p>
               <button
                 class="delete "
                 aria-label="close"
@@ -102,14 +102,14 @@ export function NonInventoryProductFrom({
                   class="button "
                   onClick={() => setShowCreateProduct(false)}
                 >
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
                 <button
                   class="button is-info "
                   disabled={!submitForm}
                   onClick={submitForm}
                 >
-                  <Translate>Confirm</Translate>
+                  <i18n.Translate>Confirm</i18n.Translate>
                 </button>
               </div>
             </footer>
@@ -170,7 +170,7 @@ export function ProductForm({ onSubscribe, initial }: 
ProductProps): VNode {
     onSubscribe(hasErrors ? undefined : submit);
   }, [submit, hasErrors]);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div>
@@ -182,33 +182,33 @@ export function ProductForm({ onSubscribe, initial }: 
ProductProps): VNode {
       >
         <InputImage<NonInventoryProduct>
           name="image"
-          label={i18n`Image`}
-          tooltip={i18n`photo of the product`}
+          label={i18n.str`Image`}
+          tooltip={i18n.str`photo of the product`}
         />
         <Input<NonInventoryProduct>
           name="description"
           inputType="multiline"
-          label={i18n`Description`}
-          tooltip={i18n`full product description`}
+          label={i18n.str`Description`}
+          tooltip={i18n.str`full product description`}
         />
         <Input<NonInventoryProduct>
           name="unit"
-          label={i18n`Unit`}
-          tooltip={i18n`name of the product unit`}
+          label={i18n.str`Unit`}
+          tooltip={i18n.str`name of the product unit`}
         />
         <InputCurrency<NonInventoryProduct>
           name="price"
-          label={i18n`Price`}
-          tooltip={i18n`amount in the current currency`}
+          label={i18n.str`Price`}
+          tooltip={i18n.str`amount in the current currency`}
         />
 
         <InputNumber<NonInventoryProduct>
           name="quantity"
-          label={i18n`Quantity`}
-          tooltip={i18n`how many products will be added`}
+          label={i18n.str`Quantity`}
+          tooltip={i18n.str`how many products will be added`}
         />
 
-        <InputTaxes<NonInventoryProduct> name="taxes" label={i18n`Taxes`} />
+        <InputTaxes<NonInventoryProduct> name="taxes" label={i18n.str`Taxes`} 
/>
       </FormProvider>
     </div>
   );
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx 
b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
index a6bb090a5..973f88677 100644
--- a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
@@ -19,17 +19,17 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h } from "preact";
 import { useCallback, useEffect, useState } from "preact/hooks";
 import * as yup from "yup";
 import { useBackendContext } from "../../context/backend.js";
 import { MerchantBackend } from "../../declaration.js";
-import { useTranslator } from "../../i18n/index.js";
 import {
   ProductCreateSchema as createSchema,
   ProductUpdateSchema as updateSchema,
 } from "../../schemas/index.js";
-import { FormProvider, FormErrors } from "../form/FormProvider.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";
@@ -115,7 +115,7 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
   }, [submit, hasErrors]);
 
   const backend = useBackendContext();
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div>
@@ -129,46 +129,46 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
           <InputWithAddon<Entity>
             name="product_id"
             addonBefore={`${backend.url}/product/`}
-            label={i18n`ID`}
-            tooltip={i18n`product identification to use in URLs (for internal 
use only)`}
+            label={i18n.str`ID`}
+            tooltip={i18n.str`product identification to use in URLs (for 
internal use only)`}
           />
         )}
         <InputImage<Entity>
           name="image"
-          label={i18n`Image`}
-          tooltip={i18n`illustration of the product for customers`}
+          label={i18n.str`Image`}
+          tooltip={i18n.str`illustration of the product for customers`}
         />
         <Input<Entity>
           name="description"
           inputType="multiline"
-          label={i18n`Description`}
-          tooltip={i18n`product description for customers`}
+          label={i18n.str`Description`}
+          tooltip={i18n.str`product description for customers`}
         />
         <InputNumber<Entity>
           name="minimum_age"
-          label={i18n`Age restricted`}
-          tooltip={i18n`is this product restricted for customer below certain 
age?`}
+          label={i18n.str`Age restricted`}
+          tooltip={i18n.str`is this product restricted for customer below 
certain age?`}
         />
         <Input<Entity>
           name="unit"
-          label={i18n`Unit`}
-          tooltip={i18n`unit describing quantity of product sold (e.g. 2 
kilograms, 5 liters, 3 items, 5 meters) for customers`}
+          label={i18n.str`Unit`}
+          tooltip={i18n.str`unit describing quantity of product sold (e.g. 2 
kilograms, 5 liters, 3 items, 5 meters) for customers`}
         />
         <InputCurrency<Entity>
           name="price"
-          label={i18n`Price`}
-          tooltip={i18n`sale price for customers, including taxes, for above 
units of the product`}
+          label={i18n.str`Price`}
+          tooltip={i18n.str`sale price for customers, including taxes, for 
above units of the product`}
         />
         <InputStock
           name="stock"
-          label={i18n`Stock`}
+          label={i18n.str`Stock`}
           alreadyExist={alreadyExist}
-          tooltip={i18n`product inventory for products with finite supply (for 
internal use only)`}
+          tooltip={i18n.str`product inventory for products with finite supply 
(for internal use only)`}
         />
         <InputTaxes<Entity>
           name="taxes"
-          label={i18n`Taxes`}
-          tooltip={i18n`taxes included in the product price, exposed to 
customers`}
+          label={i18n.str`Taxes`}
+          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 774da8975..29556ea70 100644
--- a/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx
@@ -16,8 +16,8 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
 import emptyImage from "../../assets/empty.png";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { MerchantBackend } from "../../declaration.js";
-import { Translate } from "../../i18n/index.js";
 
 interface Props {
   list: MerchantBackend.Product[];
@@ -28,25 +28,26 @@ interface Props {
   }[];
 }
 export function ProductList({ list, actions = [] }: Props): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>image</Translate>
+              <i18n.Translate>image</i18n.Translate>
             </th>
             <th>
-              <Translate>description</Translate>
+              <i18n.Translate>description</i18n.Translate>
             </th>
             <th>
-              <Translate>quantity</Translate>
+              <i18n.Translate>quantity</i18n.Translate>
             </th>
             <th>
-              <Translate>unit price</Translate>
+              <i18n.Translate>unit price</i18n.Translate>
             </th>
             <th>
-              <Translate>total price</Translate>
+              <i18n.Translate>total price</i18n.Translate>
             </th>
             <th />
           </tr>
diff --git a/packages/merchant-backoffice-ui/src/context/fetch.ts 
b/packages/merchant-backoffice-ui/src/context/fetch.ts
index 3106a6abe..88c9bc30c 100644
--- a/packages/merchant-backoffice-ui/src/context/fetch.ts
+++ b/packages/merchant-backoffice-ui/src/context/fetch.ts
@@ -29,7 +29,7 @@ interface Type {
   useSWRInfinite: typeof useSWRInfinite;
 }
 
-const Context = createContext<Type>({} as any);
+const Context = createContext<Type>({} as Type);
 
 export const useFetchContext = (): Type => useContext(Context);
 export const FetchContextProvider = ({
diff --git a/packages/merchant-backoffice-ui/src/context/listener.ts 
b/packages/merchant-backoffice-ui/src/context/listener.ts
deleted file mode 100644
index cf51bb718..000000000
--- a/packages/merchant-backoffice-ui/src/context/listener.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2023 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 { createContext } from "preact";
-import { useContext } from "preact/hooks";
-
-interface Type {
-  id: string;
-  token?: string;
-  admin?: boolean;
-  changeToken: (t?: string) => void;
-}
-
-const Context = createContext<Type>({} as any);
-
-export const ListenerContextProvider = Context.Provider;
-export const useListenerContext = (): Type => useContext(Context);
diff --git a/packages/merchant-backoffice-ui/src/context/translation.ts 
b/packages/merchant-backoffice-ui/src/context/translation.ts
deleted file mode 100644
index 027eac14c..000000000
--- a/packages/merchant-backoffice-ui/src/context/translation.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2023 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 { createContext, h, VNode } from "preact";
-import { useContext, useEffect } from "preact/hooks";
-import { useLang } from "../hooks";
-import * as jedLib from "jed";
-import { strings } from "../i18n/strings";
-
-interface Type {
-  lang: string;
-  handler: any;
-  changeLanguage: (l: string) => void;
-}
-const initial = {
-  lang: "en",
-  handler: null,
-  changeLanguage: () => {
-    // do not change anything
-  },
-};
-const Context = createContext<Type>(initial);
-
-interface Props {
-  initial?: string;
-  children: any;
-  forceLang?: string;
-}
-
-export const TranslationProvider = ({
-  initial,
-  children,
-  forceLang,
-}: Props): VNode => {
-  const [lang, changeLanguage] = useLang(initial);
-  useEffect(() => {
-    if (forceLang) {
-      changeLanguage(forceLang);
-    }
-  });
-  const handler = new jedLib.Jed(strings[lang]);
-  return h(Context.Provider, {
-    value: { lang, handler, changeLanguage },
-    children,
-  });
-};
-
-export const useTranslationContext = (): Type => useContext(Context);
diff --git a/packages/merchant-backoffice-ui/src/i18n/index.tsx 
b/packages/merchant-backoffice-ui/src/i18n/index.tsx
deleted file mode 100644
index da8d2dece..000000000
--- a/packages/merchant-backoffice-ui/src/i18n/index.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2023 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/>
- */
-
-/**
- * Translation helpers for React components and template literals.
- */
-
-/**
- * Imports
- */
-import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
-
-import { useTranslationContext } from "../context/translation.js";
-
-export type Translator = (
-  stringSeq: TemplateStringsArray,
-  ...values: any[]
-) => string;
-export function useTranslator(): Translator {
-  const ctx = useTranslationContext();
-  const jed = ctx.handler;
-  return function str(
-    stringSeq: TemplateStringsArray,
-    ...values: any[]
-  ): string {
-    const s = toI18nString(stringSeq);
-    if (!s) return s;
-    const tr = jed
-      .translate(s)
-      .ifPlural(1, s)
-      .fetch(...values);
-    return tr;
-  };
-}
-
-/**
- * Convert template strings to a msgid
- */
-function toI18nString(stringSeq: ReadonlyArray<string>): string {
-  let s = "";
-  for (let i = 0; i < stringSeq.length; i++) {
-    s += stringSeq[i];
-    if (i < stringSeq.length - 1) {
-      s += `%${i + 1}$s`;
-    }
-  }
-  return s;
-}
-
-interface TranslateSwitchProps {
-  target: number;
-  children: ComponentChildren;
-}
-
-function stringifyChildren(children: ComponentChildren): string {
-  let n = 1;
-  const ss = (children instanceof Array ? children : [children]).map((c) => {
-    if (typeof c === "string") {
-      return c;
-    }
-    return `%${n++}$s`;
-  });
-  const s = ss.join("").replace(/ +/g, " ").trim();
-  return s;
-}
-
-interface TranslateProps {
-  children: ComponentChildren;
-  /**
-   * Component that the translated element should be wrapped in.
-   * Defaults to "div".
-   */
-  wrap?: any;
-
-  /**
-   * Props to give to the wrapped component.
-   */
-  wrapProps?: any;
-}
-
-function getTranslatedChildren(
-  translation: string,
-  children: ComponentChildren,
-): ComponentChild[] {
-  const tr = translation.split(/%(\d+)\$s/);
-  const childArray = children instanceof Array ? children : [children];
-  // Merge consecutive string children.
-  const placeholderChildren = Array<ComponentChild>();
-  for (let i = 0; i < childArray.length; i++) {
-    const x = childArray[i];
-    if (x === undefined) {
-      continue;
-    } else if (typeof x === "string") {
-      continue;
-    } else {
-      placeholderChildren.push(x);
-    }
-  }
-  const result = Array<ComponentChild>();
-  for (let i = 0; i < tr.length; i++) {
-    if (i % 2 == 0) {
-      // Text
-      result.push(tr[i]);
-    } else {
-      const childIdx = Number.parseInt(tr[i], 10) - 1;
-      result.push(placeholderChildren[childIdx]);
-    }
-  }
-  return result;
-}
-
-/**
- * Translate text node children of this component.
- * If a child component might produce a text node, it must be wrapped
- * in a another non-text element.
- *
- * Example:
- * ```
- * <Translate>
- * Hello.  Your score is <span><PlayerScore player={player} /></span>
- * </Translate>
- * ```
- */
-export function Translate({ children }: TranslateProps): VNode {
-  const s = stringifyChildren(children);
-  const ctx = useTranslationContext();
-  const translation: string = ctx.handler.ngettext(s, s, 1);
-  const result = getTranslatedChildren(translation, children);
-  return <Fragment>{result}</Fragment>;
-}
-
-/**
- * Switch translation based on singular or plural based on the target prop.
- * Should only contain TranslateSingular and TransplatePlural as children.
- *
- * Example:
- * ```
- * <TranslateSwitch target={n}>
- *  <TranslateSingular>I have {n} apple.</TranslateSingular>
- *  <TranslatePlural>I have {n} apples.</TranslatePlural>
- * </TranslateSwitch>
- * ```
- */
-export function TranslateSwitch({ children, target }: TranslateSwitchProps) {
-  let singular: VNode<TranslationPluralProps> | undefined;
-  let plural: VNode<TranslationPluralProps> | undefined;
-  // const children = this.props.children;
-  if (children) {
-    (children instanceof Array ? children : [children]).forEach(
-      (child: any) => {
-        if (child.type === TranslatePlural) {
-          plural = child;
-        }
-        if (child.type === TranslateSingular) {
-          singular = child;
-        }
-      },
-    );
-  }
-  if (!singular || !plural) {
-    console.error("translation not found");
-    return h("span", {}, ["translation not found"]);
-  }
-  singular.props.target = target;
-  plural.props.target = target;
-  // We're looking up the translation based on the
-  // singular, even if we must use the plural form.
-  return singular;
-}
-
-interface TranslationPluralProps {
-  children: ComponentChildren;
-  target: number;
-}
-
-/**
- * See [[TranslateSwitch]].
- */
-export function TranslatePlural({
-  children,
-  target,
-}: TranslationPluralProps): VNode {
-  const s = stringifyChildren(children);
-  const ctx = useTranslationContext();
-  const translation = ctx.handler.ngettext(s, s, 1);
-  const result = getTranslatedChildren(translation, children);
-  return <Fragment>{result}</Fragment>;
-}
-
-/**
- * See [[TranslateSwitch]].
- */
-export function TranslateSingular({
-  children,
-  target,
-}: TranslationPluralProps): VNode {
-  const s = stringifyChildren(children);
-  const ctx = useTranslationContext();
-  const translation = ctx.handler.ngettext(s, s, target);
-  const result = getTranslatedChildren(translation, children);
-  return <Fragment>{result}</Fragment>;
-}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx
index 052d61544..91b6b4b56 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx
@@ -20,6 +20,7 @@
  */
 
 import { h, VNode, FunctionalComponent } from "preact";
+import { ConfigContextProvider } from "../../../context/config.js";
 import { CreatePage as TestedComponent } from "./CreatePage.js";
 
 export default {
@@ -35,7 +36,16 @@ function createExample<Props>(
   Component: FunctionalComponent<Props>,
   props: Partial<Props>,
 ) {
-  const r = (args: any) => <Component {...args} />;
+  const r = (args: any) => (
+    <ConfigContextProvider
+      value={{
+        currency: "ARS",
+        version: "1",
+      }}
+    >
+      <Component {...args} />
+    </ConfigContextProvider>
+  );
   r.args = props;
   return r;
 }
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 6fcabb18b..bf5f5d7c9 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -19,20 +19,19 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import * as yup from "yup";
 import { AsyncButton } from "../../../components/exception/AsyncButton.js";
 import {
   FormErrors,
   FormProvider,
 } from "../../../components/form/FormProvider.js";
+import { DefaultInstanceFormFields } from 
"../../../components/instance/DefaultInstanceFormFields.js";
 import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
 import { MerchantBackend } from "../../../declaration.js";
-import { Translate, useTranslator } from "../../../i18n/index.js";
-import { DefaultInstanceFormFields } from 
"../../../components/instance/DefaultInstanceFormFields.js";
 import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants.js";
-import { Amounts } from "@gnu-taler/taler-util";
 import { undefinedIfEmpty } from "../../../utils/table.js";
 
 export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & {
@@ -61,55 +60,57 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
   const [isTokenDialogActive, updateIsTokenDialogActive] =
     useState<boolean>(false);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const errors: FormErrors<Entity> = {
     id: !value.id
-      ? i18n`required`
+      ? i18n.str`required`
       : !INSTANCE_ID_REGEX.test(value.id)
-      ? i18n`is not valid`
+      ? i18n.str`is not valid`
       : undefined,
-    name: !value.name ? i18n`required` : undefined,
+    name: !value.name ? i18n.str`required` : undefined,
     payto_uris:
       !value.payto_uris || !value.payto_uris.length
-        ? i18n`required`
+        ? i18n.str`required`
         : undefinedIfEmpty(
             value.payto_uris.map((p) => {
-              return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined;
+              return !PAYTO_REGEX.test(p) ? i18n.str`is not valid` : undefined;
             }),
           ),
     default_max_deposit_fee: !value.default_max_deposit_fee
-      ? i18n`required`
+      ? i18n.str`required`
       : !Amounts.parse(value.default_max_deposit_fee)
-      ? i18n`invalid format`
+      ? i18n.str`invalid format`
       : undefined,
     default_max_wire_fee: !value.default_max_wire_fee
-      ? i18n`required`
+      ? i18n.str`required`
       : !Amounts.parse(value.default_max_wire_fee)
-      ? i18n`invalid format`
+      ? i18n.str`invalid format`
       : undefined,
     default_wire_fee_amortization:
       value.default_wire_fee_amortization === undefined
-        ? i18n`required`
+        ? i18n.str`required`
         : isNaN(value.default_wire_fee_amortization)
-        ? i18n`is not a number`
+        ? i18n.str`is not a number`
         : value.default_wire_fee_amortization < 1
-        ? i18n`must be 1 or greater`
+        ? i18n.str`must be 1 or greater`
         : undefined,
-    default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined,
+    default_pay_delay: !value.default_pay_delay
+      ? i18n.str`required`
+      : undefined,
     default_wire_transfer_delay: !value.default_wire_transfer_delay
-      ? i18n`required`
+      ? i18n.str`required`
       : undefined,
     address: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n`max 7 lines`
+          ? i18n.str`max 7 lines`
           : undefined,
     }),
     jurisdiction: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n`max 7 lines`
+          ? i18n.str`max 7 lines`
           : undefined,
     }),
   };
@@ -174,14 +175,14 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
               <h1 class="title">
                 <button
                   class="button is-danger has-tooltip-bottom"
-                  data-tooltip={i18n`change authorization configuration`}
+                  data-tooltip={i18n.str`change authorization configuration`}
                   onClick={() => updateIsTokenDialogActive(true)}
                 >
                   <div class="icon is-centered">
                     <i class="mdi mdi-lock-reset" />
                   </div>
                   <span>
-                    <Translate>Set access token</Translate>
+                    <i18n.Translate>Set access token</i18n.Translate>
                   </span>
                 </button>
               </h1>
@@ -205,7 +206,7 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
               <AsyncButton
@@ -213,11 +214,11 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
                 disabled={!isTokenSet || hasErrors}
                 data-tooltip={
                   hasErrors
-                    ? i18n`Need to complete marked fields and choose 
authorization method`
+                    ? i18n.str`Need to complete marked fields and choose 
authorization method`
                     : "confirm operation"
                 }
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </AsyncButton>
             </div>
           </div>
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 ed2f3f068..4da6916a0 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx
@@ -17,12 +17,12 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../components/menu/index.js";
 import { MerchantBackend } from "../../../declaration.js";
 import { useAdminAPI } from "../../../hooks/instance.js";
-import { useTranslator } from "../../../i18n/index.js";
 import { Notification } from "../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
 import { InstanceCreatedSuccessfully } from "./InstanceCreatedSuccessfully.js";
@@ -38,7 +38,7 @@ export default function Create({ onBack, onConfirm, forceId 
}: Props): VNode {
   const { createInstance } = useAdminAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const [createdOk, setCreatedOk] = useState<Entity | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (createdOk) {
     return (
@@ -62,7 +62,7 @@ export default function Create({ onBack, onConfirm, forceId 
}: Props): VNode {
             })
             .catch((error) => {
               setNotif({
-                message: i18n`Failed to create instance`,
+                message: i18n.str`Failed to create instance`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/stories.ts 
b/packages/merchant-backoffice-ui/src/paths/admin/create/stories.tsx
similarity index 76%
rename from packages/merchant-backoffice-ui/src/paths/admin/create/stories.ts
rename to packages/merchant-backoffice-ui/src/paths/admin/create/stories.tsx
index 45b94ec8c..0012f9b9b 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/stories.ts
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/stories.tsx
@@ -20,6 +20,7 @@
  */
 
 import { h, VNode, FunctionalComponent } from "preact";
+import { ConfigContextProvider } from "../../../context/config.js";
 import { CreatePage as TestedComponent } from "./CreatePage.js";
 
 export default {
@@ -32,17 +33,20 @@ export default {
 };
 
 function createExample<Props>(
-  Component: FunctionalComponent<Props>,
+  Internal: FunctionalComponent<Props>,
   props: Partial<Props>,
 ) {
-  const r = (args: any) => h(Component, args);
-  // const r = (args: any) => <Component {...args} />;
-  r.args = props;
-  return r;
+  const component = (args: any) => (
+    <ConfigContextProvider
+      value={{
+        currency: "TESTKUDOS",
+        version: "1",
+      }}
+    >
+      <Internal {...(props as any)} />
+    </ConfigContextProvider>
+  );
+  return { component, props };
 }
 
 export const Example = createExample(TestedComponent, {});
-// export const Example = (a: any): VNode => <CreatePage {...a} />;
-// Example.args = {
-//   isLoading: false
-// }
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 546f34f3a..223db2fed 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx
@@ -19,10 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { StateUpdater, useEffect, useState } from "preact/hooks";
 import { MerchantBackend } from "../../../declaration.js";
-import { Translate, useTranslator } from "../../../i18n/index.js";
 
 interface Props {
   instances: MerchantBackend.Instances.Instance[];
@@ -68,7 +68,7 @@ export function CardTable({
     }
   }, [actionQueue, selected, onUpdate]);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="card has-table">
@@ -77,7 +77,7 @@ export function CardTable({
           <span class="icon">
             <i class="mdi mdi-desktop-mac" />
           </span>
-          <Translate>Instances</Translate>
+          <i18n.Translate>Instances</i18n.Translate>
         </p>
 
         <div class="card-header-icon" aria-label="more options">
@@ -90,11 +90,14 @@ export function CardTable({
               )
             }
           >
-            <Translate>Delete</Translate>
+            <i18n.Translate>Delete</i18n.Translate>
           </button>
         </div>
         <div class="card-header-icon" aria-label="more options">
-          <span class="has-tooltip-left" data-tooltip={i18n`add new instance`}>
+          <span
+            class="has-tooltip-left"
+            data-tooltip={i18n.str`add new instance`}
+          >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
                 <i class="mdi mdi-plus mdi-36px" />
@@ -149,6 +152,7 @@ function Table({
   onDelete,
   onPurge,
 }: TableProps): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -171,10 +175,10 @@ function Table({
               </label>
             </th>
             <th>
-              <Translate>ID</Translate>
+              <i18n.Translate>ID</i18n.Translate>
             </th>
             <th>
-              <Translate>Name</Translate>
+              <i18n.Translate>Name</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -213,7 +217,7 @@ function Table({
                       type="button"
                       onClick={(): void => onUpdate(i.id)}
                     >
-                      <Translate>Edit</Translate>
+                      <i18n.Translate>Edit</i18n.Translate>
                     </button>
                     {!i.deleted && (
                       <button
@@ -221,7 +225,7 @@ function Table({
                         type="button"
                         onClick={(): void => onDelete(i)}
                       >
-                        <Translate>Delete</Translate>
+                        <i18n.Translate>Delete</i18n.Translate>
                       </button>
                     )}
                     {i.deleted && (
@@ -230,7 +234,7 @@ function Table({
                         type="button"
                         onClick={(): void => onPurge(i)}
                       >
-                        <Translate>Purge</Translate>
+                        <i18n.Translate>Purge</i18n.Translate>
                       </button>
                     )}
                   </div>
@@ -245,6 +249,7 @@ function Table({
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -253,9 +258,9 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>
+        <i18n.Translate>
           There is no instances yet, add more pressing the + sign
-        </Translate>
+        </i18n.Translate>
       </p>
     </div>
   );
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
index 5180c671c..7376a88cb 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
 import { MerchantBackend } from "../../../declaration.js";
 import { CardTable as CardTableActive } from "./TableActive.js";
-import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../../i18n/index.js";
 
 interface Props {
   instances: MerchantBackend.Instances.Instance[];
@@ -48,7 +48,7 @@ export function View({
   const showIsActive = show === "active" ? "is-active" : "";
   const showIsDeleted = show === "deleted" ? "is-active" : "";
   const showAll = show === null ? "is-active" : "";
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const showingInstances = showIsDeleted
     ? instances.filter((i) => i.deleted)
@@ -66,30 +66,30 @@ export function View({
                 <li class={showIsActive}>
                   <div
                     class="has-tooltip-right"
-                    data-tooltip={i18n`Only show active instances`}
+                    data-tooltip={i18n.str`Only show active instances`}
                   >
                     <a onClick={() => setShow("active")}>
-                      <Translate>Active</Translate>
+                      <i18n.Translate>Active</i18n.Translate>
                     </a>
                   </div>
                 </li>
                 <li class={showIsDeleted}>
                   <div
                     class="has-tooltip-right"
-                    data-tooltip={i18n`Only show deleted instances`}
+                    data-tooltip={i18n.str`Only show deleted instances`}
                   >
                     <a onClick={() => setShow("deleted")}>
-                      <Translate>Deleted</Translate>
+                      <i18n.Translate>Deleted</i18n.Translate>
                     </a>
                   </div>
                 </li>
                 <li class={showAll}>
                   <div
                     class="has-tooltip-right"
-                    data-tooltip={i18n`Show all instances`}
+                    data-tooltip={i18n.str`Show all instances`}
                   >
                     <a onClick={() => setShow(null)}>
-                      <Translate>All</Translate>
+                      <i18n.Translate>All</i18n.Translate>
                     </a>
                   </div>
                 </li>
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
index 36d58dd41..9a81b72d4 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../components/exception/loading.js";
@@ -27,7 +28,6 @@ import { DeleteModal, PurgeModal } from 
"../../../components/modal/index.js";
 import { MerchantBackend } from "../../../declaration.js";
 import { HttpError } from "../../../hooks/backend.js";
 import { useAdminAPI, useBackendInstances } from "../../../hooks/instance.js";
-import { useTranslator } from "../../../i18n/index.js";
 import { Notification } from "../../../utils/types.js";
 import { View } from "./View.js";
 
@@ -56,7 +56,7 @@ export default function Instances({
     useState<MerchantBackend.Instances.Instance | null>(null);
   const { deleteInstance, purgeInstance } = useAdminAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
@@ -84,12 +84,12 @@ export default function Instances({
               await deleteInstance(deleting.id);
               // pushNotification({ message: 'delete_success', type: 'SUCCESS' 
})
               setNotif({
-                message: i18n`Instance "${deleting.name}" (ID: ${deleting.id}) 
has been deleted`,
+                message: i18n.str`Instance "${deleting.name}" (ID: 
${deleting.id}) has been deleted`,
                 type: "SUCCESS",
               });
             } catch (error) {
               setNotif({
-                message: i18n`Failed to delete instance`,
+                message: i18n.str`Failed to delete instance`,
                 type: "ERROR",
                 description: error instanceof Error ? error.message : 
undefined,
               });
@@ -107,12 +107,12 @@ export default function Instances({
             try {
               await purgeInstance(purging.id);
               setNotif({
-                message: i18n`Instance "${purging.name}" (ID: ${purging.id}) 
has been disabled`,
+                message: i18n.str`Instance "${purging.name}" (ID: 
${purging.id}) has been disabled`,
                 type: "SUCCESS",
               });
             } catch (error) {
               setNotif({
-                message: i18n`Failed to purge instance`,
+                message: i18n.str`Failed to purge instance`,
                 type: "ERROR",
                 description: error instanceof Error ? error.message : 
undefined,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
index 59aa7a1d2..4ab415e85 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { FormProvider } from "../../../components/form/FormProvider.js";
 import { Input } from "../../../components/form/Input.js";
 import { MerchantBackend } from "../../../declaration.js";
-import { useTranslator } from "../../../i18n/index.js";
 
 type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage;
 interface Props {
@@ -49,7 +49,7 @@ function convert(
 export function DetailPage({ selected }: Props): VNode {
   const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div>
@@ -73,11 +73,11 @@ export function DetailPage({ selected }: Props): VNode {
           <div class="column" />
           <div class="column is-6">
             <FormProvider<Entity> object={value} valueHandler={valueHandler}>
-              <Input<Entity> name="name" readonly label={i18n`Name`} />
+              <Input<Entity> name="name" readonly label={i18n.str`Name`} />
               <Input<Entity>
                 name="payto_uris"
                 readonly
-                label={i18n`Account address`}
+                label={i18n.str`Account address`}
               />
             </FormProvider>
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
index 9ac1c4381..cdbe732a4 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
@@ -20,6 +20,7 @@
  */
 
 import { h, VNode, FunctionalComponent } from "preact";
+import { ConfigContextProvider } from "../../../context/config.js";
 import { DetailPage as TestedComponent } from "./DetailPage.js";
 
 export default {
@@ -32,12 +33,20 @@ export default {
 };
 
 function createExample<Props>(
-  Component: FunctionalComponent<Props>,
+  Internal: FunctionalComponent<Props>,
   props: Partial<Props>,
 ) {
-  const r = (args: any) => <Component {...args} />;
-  r.args = props;
-  return r;
+  const component = (args: any) => (
+    <ConfigContextProvider
+      value={{
+        currency: "TESTKUDOS",
+        version: "1",
+      }}
+    >
+      <Internal {...(props as any)} />
+    </ConfigContextProvider>
+  );
+  return { component, props };
 }
 
 export const Example = createExample(TestedComponent, {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/index.stories.ts 
b/packages/merchant-backoffice-ui/src/paths/instance/index.stories.ts
index 3670c2807..fcb75cd7c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/index.stories.ts
+++ b/packages/merchant-backoffice-ui/src/paths/instance/index.stories.ts
@@ -15,4 +15,3 @@
  */
 
 export * as details from "./details/stories.js";
-
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
index c10121fb3..7966a2cdd 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
@@ -19,16 +19,16 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 
 export interface Props {
   status: MerchantBackend.Instances.AccountKycRedirects;
 }
 
 export function ListPage({ status }: Props): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <section class="section is-main-section">
@@ -38,7 +38,7 @@ export function ListPage({ status }: Props): VNode {
             <span class="icon">
               <i class="mdi mdi-clock" />
             </span>
-            <Translate>Pending KYC verification</Translate>
+            <i18n.Translate>Pending KYC verification</i18n.Translate>
           </p>
 
           <div class="card-header-icon" aria-label="more options" />
@@ -63,7 +63,7 @@ export function ListPage({ status }: Props): VNode {
               <span class="icon">
                 <i class="mdi mdi-clock" />
               </span>
-              <Translate>Timed out</Translate>
+              <i18n.Translate>Timed out</i18n.Translate>
             </p>
 
             <div class="card-header-icon" aria-label="more options" />
@@ -93,19 +93,20 @@ interface TimedOutTableProps {
 }
 
 function PendingTable({ entries }: PendingTableProps): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>Exchange</Translate>
+              <i18n.Translate>Exchange</i18n.Translate>
             </th>
             <th>
-              <Translate>Target account</Translate>
+              <i18n.Translate>Target account</i18n.Translate>
             </th>
             <th>
-              <Translate>KYC URL</Translate>
+              <i18n.Translate>KYC URL</i18n.Translate>
             </th>
           </tr>
         </thead>
@@ -130,19 +131,20 @@ function PendingTable({ entries }: PendingTableProps): 
VNode {
 }
 
 function TimedOutTable({ entries }: TimedOutTableProps): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>Exchange</Translate>
+              <i18n.Translate>Exchange</i18n.Translate>
             </th>
             <th>
-              <Translate>Code</Translate>
+              <i18n.Translate>Code</i18n.Translate>
             </th>
             <th>
-              <Translate>Http Status</Translate>
+              <i18n.Translate>Http Status</i18n.Translate>
             </th>
           </tr>
         </thead>
@@ -163,6 +165,7 @@ function TimedOutTable({ entries }: TimedOutTableProps): 
VNode {
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -171,7 +174,7 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>No pending kyc verification!</Translate>
+        <i18n.Translate>No pending kyc verification!</i18n.Translate>
       </p>
     </div>
   );
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 145df717d..0c2e92418 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
@@ -19,29 +19,29 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { add, isAfter, isBefore, isFuture } from "date-fns";
 import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { add, isAfter, isBefore, isFuture } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import {
-  FormProvider,
   FormErrors,
+  FormProvider,
 } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
+import { InputBoolean } from "../../../../components/form/InputBoolean.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputDate } from "../../../../components/form/InputDate.js";
 import { InputGroup } from "../../../../components/form/InputGroup.js";
 import { InputLocation } from "../../../../components/form/InputLocation.js";
+import { InputNumber } from "../../../../components/form/InputNumber.js";
+import { InventoryProductForm } from 
"../../../../components/product/InventoryProductForm.js";
+import { NonInventoryProductFrom } from 
"../../../../components/product/NonInventoryProductForm.js";
 import { ProductList } from "../../../../components/product/ProductList.js";
 import { useConfigContext } from "../../../../context/config.js";
 import { Duration, MerchantBackend, WithId } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { OrderCreateSchema as schema } from "../../../../schemas/index.js";
 import { rate } from "../../../../utils/amount.js";
-import { InventoryProductForm } from 
"../../../../components/product/InventoryProductForm.js";
-import { NonInventoryProductFrom } from 
"../../../../components/product/NonInventoryProductForm.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { InputBoolean } from "../../../../components/form/InputBoolean.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 interface Props {
@@ -140,65 +140,65 @@ export function CreatePage({
   const inventoryList = Object.values(value.inventoryProducts || {});
   const productList = Object.values(value.products || {});
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const errors: FormErrors<Entity> = {
     pricing: undefinedIfEmpty({
-      summary: !value.pricing?.summary ? i18n`required` : undefined,
+      summary: !value.pricing?.summary ? i18n.str`required` : undefined,
       order_price: !value.pricing?.order_price
-        ? i18n`required`
+        ? i18n.str`required`
         : Amounts.isZero(value.pricing.order_price)
-        ? i18n`must be greater than 0`
+        ? i18n.str`must be greater than 0`
         : undefined,
     }),
     extra:
       value.extra && !stringIsValidJSON(value.extra)
-        ? i18n`not a valid json`
+        ? i18n.str`not a valid json`
         : undefined,
     payments: undefinedIfEmpty({
       refund_deadline: !value.payments?.refund_deadline
         ? undefined
         : !isFuture(value.payments.refund_deadline)
-        ? i18n`should be in the future`
+        ? i18n.str`should be in the future`
         : value.payments.pay_deadline &&
           isBefore(value.payments.refund_deadline, value.payments.pay_deadline)
-        ? i18n`refund deadline cannot be before pay deadline`
+        ? i18n.str`refund deadline cannot be before pay deadline`
         : value.payments.wire_transfer_deadline &&
           isBefore(
             value.payments.wire_transfer_deadline,
             value.payments.refund_deadline,
           )
-        ? i18n`wire transfer deadline cannot be before refund deadline`
+        ? i18n.str`wire transfer deadline cannot be before refund deadline`
         : undefined,
       pay_deadline: !value.payments?.pay_deadline
         ? undefined
         : !isFuture(value.payments.pay_deadline)
-        ? i18n`should be in the future`
+        ? i18n.str`should be in the future`
         : value.payments.wire_transfer_deadline &&
           isBefore(
             value.payments.wire_transfer_deadline,
             value.payments.pay_deadline,
           )
-        ? i18n`wire transfer deadline cannot be before pay deadline`
+        ? i18n.str`wire transfer deadline cannot be before pay deadline`
         : undefined,
       auto_refund_deadline: !value.payments?.auto_refund_deadline
         ? undefined
         : !isFuture(value.payments.auto_refund_deadline)
-        ? i18n`should be in the future`
+        ? i18n.str`should be in the future`
         : !value.payments?.refund_deadline
-        ? i18n`should have a refund deadline`
+        ? i18n.str`should have a refund deadline`
         : !isAfter(
             value.payments.refund_deadline,
             value.payments.auto_refund_deadline,
           )
-        ? i18n`auto refund cannot be after refund deadline`
+        ? i18n.str`auto refund cannot be after refund deadline`
         : undefined,
     }),
     shipping: undefinedIfEmpty({
       delivery_date: !value.shipping?.delivery_date
         ? undefined
         : !isFuture(value.shipping.delivery_date)
-        ? i18n`should be in the future`
+        ? i18n.str`should be in the future`
         : undefined,
     }),
   };
@@ -349,7 +349,7 @@ export function CreatePage({
             {/* // FIXME: translating plural singular */}
             <InputGroup
               name="inventory_products"
-              label={i18n`Manage products in order`}
+              label={i18n.str`Manage products in order`}
               alternative={
                 allProducts.length > 0 && (
                   <p>
@@ -358,7 +358,7 @@ export function CreatePage({
                   </p>
                 )
               }
-              tooltip={i18n`Manage list of products in the order.`}
+              tooltip={i18n.str`Manage list of products in the order.`}
             >
               <InventoryProductForm
                 currentProducts={value.inventoryProducts || {}}
@@ -379,8 +379,8 @@ export function CreatePage({
                   list={allProducts}
                   actions={[
                     {
-                      name: i18n`Remove`,
-                      tooltip: i18n`Remove this product from the order.`,
+                      name: i18n.str`Remove`,
+                      tooltip: i18n.str`Remove this product from the order.`,
                       handler: (e, index) => {
                         if (e.product_id) {
                           removeProductFromTheInventoryList(e.product_id);
@@ -404,13 +404,13 @@ export function CreatePage({
                 <Fragment>
                   <InputCurrency
                     name="pricing.products_price"
-                    label={i18n`Total price`}
+                    label={i18n.str`Total price`}
                     readonly
-                    tooltip={i18n`total product price added up`}
+                    tooltip={i18n.str`total product price added up`}
                   />
                   <InputCurrency
                     name="pricing.order_price"
-                    label={i18n`Total price`}
+                    label={i18n.str`Total price`}
                     addonAfter={
                       discountOrRise > 0 &&
                       (discountOrRise < 1
@@ -419,103 +419,103 @@ export function CreatePage({
                           )}`
                         : `rise of %${Math.round((discountOrRise - 1) * 100)}`)
                     }
-                    tooltip={i18n`Amount to be paid by the customer`}
+                    tooltip={i18n.str`Amount to be paid by the customer`}
                   />
                 </Fragment>
               ) : (
                 <InputCurrency
                   name="pricing.order_price"
-                  label={i18n`Order price`}
-                  tooltip={i18n`final order price`}
+                  label={i18n.str`Order price`}
+                  tooltip={i18n.str`final order price`}
                 />
               )}
 
               <Input
                 name="pricing.summary"
                 inputType="multiline"
-                label={i18n`Summary`}
-                tooltip={i18n`Title of the order to be shown to the customer`}
+                label={i18n.str`Summary`}
+                tooltip={i18n.str`Title of the order to be shown to the 
customer`}
               />
 
               <InputGroup
                 name="shipping"
-                label={i18n`Shipping and Fulfillment`}
+                label={i18n.str`Shipping and Fulfillment`}
                 initialActive
               >
                 <InputDate
                   name="shipping.delivery_date"
-                  label={i18n`Delivery date`}
-                  tooltip={i18n`Deadline for physical delivery assured by the 
merchant.`}
+                  label={i18n.str`Delivery date`}
+                  tooltip={i18n.str`Deadline for physical delivery assured by 
the merchant.`}
                 />
                 {value.shipping?.delivery_date && (
                   <InputGroup
                     name="shipping.delivery_location"
-                    label={i18n`Location`}
-                    tooltip={i18n`address where the products will be 
delivered`}
+                    label={i18n.str`Location`}
+                    tooltip={i18n.str`address where the products will be 
delivered`}
                   >
                     <InputLocation name="shipping.delivery_location" />
                   </InputGroup>
                 )}
                 <Input
                   name="shipping.fullfilment_url"
-                  label={i18n`Fulfillment URL`}
-                  tooltip={i18n`URL to which the user will be redirected after 
successful payment.`}
+                  label={i18n.str`Fulfillment URL`}
+                  tooltip={i18n.str`URL to which the user will be redirected 
after successful payment.`}
                 />
               </InputGroup>
 
               <InputGroup
                 name="payments"
-                label={i18n`Taler payment options`}
-                tooltip={i18n`Override default Taler payment settings for this 
order`}
+                label={i18n.str`Taler payment options`}
+                tooltip={i18n.str`Override default Taler payment settings for 
this order`}
               >
                 <InputDate
                   name="payments.pay_deadline"
-                  label={i18n`Payment deadline`}
-                  tooltip={i18n`Deadline for the customer to pay for the offer 
before it expires. Inventory products will be reserved until this deadline.`}
+                  label={i18n.str`Payment deadline`}
+                  tooltip={i18n.str`Deadline for the customer to pay for the 
offer before it expires. Inventory products will be reserved until this 
deadline.`}
                 />
                 <InputDate
                   name="payments.refund_deadline"
-                  label={i18n`Refund deadline`}
-                  tooltip={i18n`Time until which the order can be refunded by 
the merchant.`}
+                  label={i18n.str`Refund deadline`}
+                  tooltip={i18n.str`Time until which the order can be refunded 
by the merchant.`}
                 />
                 <InputDate
                   name="payments.wire_transfer_deadline"
-                  label={i18n`Wire transfer deadline`}
-                  tooltip={i18n`Deadline for the exchange to make the wire 
transfer.`}
+                  label={i18n.str`Wire transfer deadline`}
+                  tooltip={i18n.str`Deadline for the exchange to make the wire 
transfer.`}
                 />
                 <InputDate
                   name="payments.auto_refund_deadline"
-                  label={i18n`Auto-refund deadline`}
-                  tooltip={i18n`Time until which the wallet will automatically 
check for refunds without user interaction.`}
+                  label={i18n.str`Auto-refund deadline`}
+                  tooltip={i18n.str`Time until which the wallet will 
automatically check for refunds without user interaction.`}
                 />
 
                 <InputCurrency
                   name="payments.max_fee"
-                  label={i18n`Maximum deposit fee`}
-                  tooltip={i18n`Maximum deposit fees the merchant is willing 
to cover for this order. Higher deposit fees must be covered in full by the 
consumer.`}
+                  label={i18n.str`Maximum deposit fee`}
+                  tooltip={i18n.str`Maximum deposit fees the merchant is 
willing to cover for this order. Higher deposit fees must be covered in full by 
the consumer.`}
                 />
                 <InputCurrency
                   name="payments.max_wire_fee"
-                  label={i18n`Maximum wire fee`}
-                  tooltip={i18n`Maximum aggregate wire fees the merchant is 
willing to cover for this order. Wire fees exceeding this amount are to be 
covered by the customers.`}
+                  label={i18n.str`Maximum wire fee`}
+                  tooltip={i18n.str`Maximum aggregate wire fees the merchant 
is willing to cover for this order. Wire fees exceeding this amount are to be 
covered by the customers.`}
                 />
                 <InputNumber
                   name="payments.wire_fee_amortization"
-                  label={i18n`Wire fee amortization`}
-                  tooltip={i18n`Factor by which wire fees exceeding the above 
threshold are divided to determine the share of excess wire fees to be paid 
explicitly by the consumer.`}
+                  label={i18n.str`Wire fee amortization`}
+                  tooltip={i18n.str`Factor by which wire fees exceeding the 
above threshold are divided to determine the share of excess wire fees to be 
paid explicitly by the consumer.`}
                 />
                 <InputBoolean
                   name="payments.createToken"
-                  label={i18n`Create token`}
-                  tooltip={i18n`Uncheck this option if the merchant backend 
generated an order ID with enough entropy to prevent adversarial claims.`}
+                  label={i18n.str`Create token`}
+                  tooltip={i18n.str`Uncheck this option if the merchant 
backend generated an order ID with enough entropy to prevent adversarial 
claims.`}
                 />
                 <InputNumber
                   name="payments.minimum_age"
-                  label={i18n`Minimum age required`}
-                  tooltip={i18n`Any value greater than 0 will limit the coins 
able be used to pay this contract. If empty the age restriction will be defined 
by the products`}
+                  label={i18n.str`Minimum age required`}
+                  tooltip={i18n.str`Any value greater than 0 will limit the 
coins able be used to pay this contract. If empty the age restriction will be 
defined by the products`}
                   help={
                     minAgeByProducts > 0
-                      ? i18n`Min age defined by the producs is 
${minAgeByProducts}`
+                      ? i18n.str`Min age defined by the producs is 
${minAgeByProducts}`
                       : undefined
                   }
                 />
@@ -523,14 +523,14 @@ export function CreatePage({
 
               <InputGroup
                 name="extra"
-                label={i18n`Additional information`}
-                tooltip={i18n`Custom information to be included in the 
contract for this order.`}
+                label={i18n.str`Additional information`}
+                tooltip={i18n.str`Custom information to be included in the 
contract for this order.`}
               >
                 <Input
                   name="extra"
                   inputType="multiline"
                   label={`Value`}
-                  tooltip={i18n`You must enter a value in JavaScript Object 
Notation (JSON).`}
+                  tooltip={i18n.str`You must enter a value in JavaScript 
Object Notation (JSON).`}
                 />
               </InputGroup>
             </FormProvider>
@@ -538,7 +538,7 @@ export function CreatePage({
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
               <button
@@ -546,7 +546,7 @@ export function CreatePage({
                 onClick={submit}
                 disabled={hasErrors}
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </button>
             </div>
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
index 6d3ac311a..88c33e2f8 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
@@ -13,11 +13,11 @@
  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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { CreatedSuccessfully } from 
"../../../../components/notifications/CreatedSuccessfully.js";
 import { useOrderAPI } from "../../../../hooks/order.js";
-import { Translate } from "../../../../i18n/index.js";
 import { Entity } from "./index.js";
 
 interface Props {
@@ -33,7 +33,7 @@ export function OrderCreatedSuccessfully({
 }: Props): VNode {
   const { getPaymentURL } = useOrderAPI();
   const [url, setURL] = useState<string | undefined>(undefined);
-
+  const { i18n } = useTranslationContext();
   useEffect(() => {
     getPaymentURL(entity.response.order_id).then((response) => {
       setURL(response.data);
@@ -48,7 +48,7 @@ export function OrderCreatedSuccessfully({
       <div class="field is-horizontal">
         <div class="field-label is-normal">
           <label class="label">
-            <Translate>Amount</Translate>
+            <i18n.Translate>Amount</i18n.Translate>
           </label>
         </div>
         <div class="field-body is-flex-grow-3">
@@ -66,7 +66,7 @@ export function OrderCreatedSuccessfully({
       <div class="field is-horizontal">
         <div class="field-label is-normal">
           <label class="label">
-            <Translate>Summary</Translate>
+            <i18n.Translate>Summary</i18n.Translate>
           </label>
         </div>
         <div class="field-body is-flex-grow-3">
@@ -84,7 +84,7 @@ export function OrderCreatedSuccessfully({
       <div class="field is-horizontal">
         <div class="field-label is-normal">
           <label class="label">
-            <Translate>Order ID</Translate>
+            <i18n.Translate>Order ID</i18n.Translate>
           </label>
         </div>
         <div class="field-body is-flex-grow-3">
@@ -98,7 +98,7 @@ export function OrderCreatedSuccessfully({
       <div class="field is-horizontal">
         <div class="field-label is-normal">
           <label class="label">
-            <Translate>Payment URL</Translate>
+            <i18n.Translate>Payment URL</i18n.Translate>
           </label>
         </div>
         <div class="field-body is-flex-grow-3">
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 e8927dd70..1fba3c3e9 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
@@ -20,6 +20,7 @@
  */
 
 import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -34,7 +35,6 @@ import { TextField } from 
"../../../../components/form/TextField.js";
 import { ProductList } from "../../../../components/product/ProductList.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { mergeRefunds } from "../../../../utils/amount.js";
 import { RefundModal } from "../list/Table.js";
 import { Event, Timeline } from "./Timeline.js";
@@ -56,84 +56,84 @@ type Unpaid = 
MerchantBackend.Orders.CheckPaymentUnpaidResponse;
 type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse;
 
 function ContractTerms({ value }: { value: CT }) {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
-    <InputGroup name="contract_terms" label={i18n`Contract Terms`}>
+    <InputGroup name="contract_terms" label={i18n.str`Contract Terms`}>
       <FormProvider<CT> object={value} valueHandler={null}>
         <Input<CT>
           readonly
           name="summary"
-          label={i18n`Summary`}
-          tooltip={i18n`human-readable description of the whole purchase`}
+          label={i18n.str`Summary`}
+          tooltip={i18n.str`human-readable description of the whole purchase`}
         />
         <InputCurrency<CT>
           readonly
           name="amount"
-          label={i18n`Amount`}
-          tooltip={i18n`total price for the transaction`}
+          label={i18n.str`Amount`}
+          tooltip={i18n.str`total price for the transaction`}
         />
         {value.fulfillment_url && (
           <Input<CT>
             readonly
             name="fulfillment_url"
-            label={i18n`Fulfillment URL`}
-            tooltip={i18n`URL for this purchase`}
+            label={i18n.str`Fulfillment URL`}
+            tooltip={i18n.str`URL for this purchase`}
           />
         )}
         <Input<CT>
           readonly
           name="max_fee"
-          label={i18n`Max fee`}
-          tooltip={i18n`maximum total deposit fee accepted by the merchant for 
this contract`}
+          label={i18n.str`Max fee`}
+          tooltip={i18n.str`maximum total deposit fee accepted by the merchant 
for this contract`}
         />
         <Input<CT>
           readonly
           name="max_wire_fee"
-          label={i18n`Max wire fee`}
-          tooltip={i18n`maximum wire fee accepted by the merchant`}
+          label={i18n.str`Max wire fee`}
+          tooltip={i18n.str`maximum wire fee accepted by the merchant`}
         />
         <Input<CT>
           readonly
           name="wire_fee_amortization"
-          label={i18n`Wire fee amortization`}
-          tooltip={i18n`over how many customer transactions does the merchant 
expect to amortize wire fees on average`}
+          label={i18n.str`Wire fee amortization`}
+          tooltip={i18n.str`over how many customer transactions does the 
merchant expect to amortize wire fees on average`}
         />
         <InputDate<CT>
           readonly
           name="timestamp"
-          label={i18n`Created at`}
-          tooltip={i18n`time when this contract was generated`}
+          label={i18n.str`Created at`}
+          tooltip={i18n.str`time when this contract was generated`}
         />
         <InputDate<CT>
           readonly
           name="refund_deadline"
-          label={i18n`Refund deadline`}
-          tooltip={i18n`after this deadline has passed no refunds will be 
accepted`}
+          label={i18n.str`Refund deadline`}
+          tooltip={i18n.str`after this deadline has passed no refunds will be 
accepted`}
         />
         <InputDate<CT>
           readonly
           name="pay_deadline"
-          label={i18n`Payment deadline`}
-          tooltip={i18n`after this deadline, the merchant won't accept 
payments for the contract`}
+          label={i18n.str`Payment deadline`}
+          tooltip={i18n.str`after this deadline, the merchant won't accept 
payments for the contract`}
         />
         <InputDate<CT>
           readonly
           name="wire_transfer_deadline"
-          label={i18n`Wire transfer deadline`}
-          tooltip={i18n`transfer deadline for the exchange`}
+          label={i18n.str`Wire transfer deadline`}
+          tooltip={i18n.str`transfer deadline for the exchange`}
         />
         <InputDate<CT>
           readonly
           name="delivery_date"
-          label={i18n`Delivery date`}
-          tooltip={i18n`time indicating when the order should be delivered`}
+          label={i18n.str`Delivery date`}
+          tooltip={i18n.str`time indicating when the order should be 
delivered`}
         />
         {value.delivery_date && (
           <InputGroup
             name="delivery_location"
-            label={i18n`Location`}
-            tooltip={i18n`where the order will be delivered`}
+            label={i18n.str`Location`}
+            tooltip={i18n.str`where the order will be delivered`}
           >
             <InputLocation name="payments.delivery_location" />
           </InputGroup>
@@ -141,14 +141,14 @@ function ContractTerms({ value }: { value: CT }) {
         <InputDuration<CT>
           readonly
           name="auto_refund"
-          label={i18n`Auto-refund delay`}
-          tooltip={i18n`how long the wallet should try to get an automatic 
refund for the purchase`}
+          label={i18n.str`Auto-refund delay`}
+          tooltip={i18n.str`how long the wallet should try to get an automatic 
refund for the purchase`}
         />
         <Input<CT>
           readonly
           name="extra"
-          label={i18n`Extra info`}
-          tooltip={i18n`extra data that is only interpreted by the merchant 
frontend`}
+          label={i18n.str`Extra info`}
+          tooltip={i18n.str`extra data that is only interpreted by the 
merchant frontend`}
         />
       </FormProvider>
     </InputGroup>
@@ -203,7 +203,7 @@ function ClaimedPage({
   }
 
   const [value, valueHandler] = useState<Partial<Claimed>>(order);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div>
@@ -216,9 +216,9 @@ function ClaimedPage({
                 <div class="level">
                   <div class="level-left">
                     <div class="level-item">
-                      <Translate>Order</Translate> #{id}
+                      <i18n.Translate>Order</i18n.Translate> #{id}
                       <div class="tag is-info ml-4">
-                        <Translate>claimed</Translate>
+                        <i18n.Translate>claimed</i18n.Translate>
                       </div>
                     </div>
                   </div>
@@ -244,7 +244,7 @@ function ClaimedPage({
                       >
                         <p>
                           <b>
-                            <Translate>claimed at</Translate>:
+                            <i18n.Translate>claimed at</i18n.Translate>:
                           </b>{" "}
                           {format(
                             new Date(order.contract_terms.timestamp.t_s * 
1000),
@@ -262,13 +262,13 @@ function ClaimedPage({
               <div class="columns">
                 <div class="column is-4">
                   <div class="title">
-                    <Translate>Timeline</Translate>
+                    <i18n.Translate>Timeline</i18n.Translate>
                   </div>
                   <Timeline events={events} />
                 </div>
                 <div class="column is-8">
                   <div class="title">
-                    <Translate>Payment details</Translate>
+                    <i18n.Translate>Payment details</i18n.Translate>
                   </div>
                   <FormProvider<Claimed>
                     object={value}
@@ -278,17 +278,17 @@ function ClaimedPage({
                       name="contract_terms.summary"
                       readonly
                       inputType="multiline"
-                      label={i18n`Summary`}
+                      label={i18n.str`Summary`}
                     />
                     <InputCurrency
                       name="contract_terms.amount"
                       readonly
-                      label={i18n`Amount`}
+                      label={i18n.str`Amount`}
                     />
                     <Input<Claimed>
                       name="order_status"
                       readonly
-                      label={i18n`Order status`}
+                      label={i18n.str`Order status`}
                     />
                   </FormProvider>
                 </div>
@@ -298,7 +298,7 @@ function ClaimedPage({
             {order.contract_terms.products.length ? (
               <Fragment>
                 <div class="title">
-                  <Translate>Product list</Translate>
+                  <i18n.Translate>Product list</i18n.Translate>
                 </div>
                 <ProductList list={order.contract_terms.products} />
               </Fragment>
@@ -426,7 +426,7 @@ function PaidPage({
   const refundurl = 
`${proto}://refund/${refundHost}/${order.contract_terms.order_id}/`;
   const refundable =
     new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000;
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const amount = Amounts.parseOrThrow(order.contract_terms.amount);
   const refund_taken = order.refund_details.reduce((prev, cur) => {
@@ -446,18 +446,18 @@ function PaidPage({
                 <div class="level">
                   <div class="level-left">
                     <div class="level-item">
-                      <Translate>Order</Translate> #{id}
+                      <i18n.Translate>Order</i18n.Translate> #{id}
                       <div class="tag is-success ml-4">
-                        <Translate>paid</Translate>
+                        <i18n.Translate>paid</i18n.Translate>
                       </div>
                       {order.wired ? (
                         <div class="tag is-success ml-4">
-                          <Translate>wired</Translate>
+                          <i18n.Translate>wired</i18n.Translate>
                         </div>
                       ) : null}
                       {order.refunded ? (
                         <div class="tag is-danger ml-4">
-                          <Translate>refunded</Translate>
+                          <i18n.Translate>refunded</i18n.Translate>
                         </div>
                       ) : null}
                     </div>
@@ -477,8 +477,8 @@ function PaidPage({
                             class="has-tooltip-left"
                             data-tooltip={
                               refundable
-                                ? i18n`refund order`
-                                : i18n`not refundable`
+                                ? i18n.str`refund order`
+                                : i18n.str`not refundable`
                             }
                           >
                             <button
@@ -486,7 +486,7 @@ function PaidPage({
                               disabled={!refundable}
                               onClick={() => onRefund(id)}
                             >
-                              <Translate>refund</Translate>
+                              <i18n.Translate>refund</i18n.Translate>
                             </button>
                           </span>
                         </div>
@@ -533,41 +533,41 @@ function PaidPage({
               <div class="columns">
                 <div class="column is-4">
                   <div class="title">
-                    <Translate>Timeline</Translate>
+                    <i18n.Translate>Timeline</i18n.Translate>
                   </div>
                   <Timeline events={events} />
                 </div>
                 <div class="column is-8">
                   <div class="title">
-                    <Translate>Payment details</Translate>
+                    <i18n.Translate>Payment details</i18n.Translate>
                   </div>
                   <FormProvider<Paid>
                     object={value}
                     valueHandler={valueHandler}
                   >
-                    {/* <InputCurrency<Paid> name="deposit_total" readonly 
label={i18n`Deposit total`} /> */}
+                    {/* <InputCurrency<Paid> name="deposit_total" readonly 
label={i18n.str`Deposit total`} /> */}
                     {order.refunded && (
                       <InputCurrency<Paid>
                         name="refund_amount"
                         readonly
-                        label={i18n`Refunded amount`}
+                        label={i18n.str`Refunded amount`}
                       />
                     )}
                     {order.refunded && (
                       <InputCurrency<Paid>
                         name="refund_taken"
                         readonly
-                        label={i18n`Refund taken`}
+                        label={i18n.str`Refund taken`}
                       />
                     )}
                     <Input<Paid>
                       name="order_status"
                       readonly
-                      label={i18n`Order status`}
+                      label={i18n.str`Order status`}
                     />
                     <TextField<Paid>
                       name="order_status_url"
-                      label={i18n`Status URL`}
+                      label={i18n.str`Status URL`}
                     >
                       <a
                         target="_blank"
@@ -580,7 +580,7 @@ function PaidPage({
                     {order.refunded && (
                       <TextField<Paid>
                         name="order_status_url"
-                        label={i18n`Refund URI`}
+                        label={i18n.str`Refund URI`}
                       >
                         <a target="_blank" rel="noreferrer" href={refundurl}>
                           {refundurl}
@@ -595,7 +595,7 @@ function PaidPage({
             {order.contract_terms.products.length ? (
               <Fragment>
                 <div class="title">
-                  <Translate>Product list</Translate>
+                  <i18n.Translate>Product list</i18n.Translate>
                 </div>
                 <ProductList list={order.contract_terms.products} />
               </Fragment>
@@ -620,7 +620,7 @@ function UnpaidPage({
   order: MerchantBackend.Orders.CheckPaymentUnpaidResponse;
 }) {
   const [value, valueHandler] = useState<Partial<Unpaid>>(order);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div>
       <section class="hero is-hero-bar">
@@ -629,11 +629,11 @@ function UnpaidPage({
             <div class="level-left">
               <div class="level-item">
                 <h1 class="title">
-                  <Translate>Order</Translate> #{id}
+                  <i18n.Translate>Order</i18n.Translate> #{id}
                 </h1>
               </div>
               <div class="tag is-dark">
-                <Translate>unpaid</Translate>
+                <i18n.Translate>unpaid</i18n.Translate>
               </div>
             </div>
           </div>
@@ -651,7 +651,7 @@ function UnpaidPage({
                 >
                   <p>
                     <b>
-                      <Translate>pay at</Translate>:
+                      <i18n.Translate>pay at</i18n.Translate>:
                     </b>{" "}
                     <a
                       href={order.order_status_url}
@@ -663,7 +663,7 @@ function UnpaidPage({
                   </p>
                   <p>
                     <b>
-                      <Translate>created at</Translate>:
+                      <i18n.Translate>created at</i18n.Translate>:
                     </b>{" "}
                     {order.creation_time.t_s === "never"
                       ? "never"
@@ -687,26 +687,29 @@ function UnpaidPage({
               <Input<Unpaid>
                 readonly
                 name="summary"
-                label={i18n`Summary`}
-                tooltip={i18n`human-readable description of the whole 
purchase`}
+                label={i18n.str`Summary`}
+                tooltip={i18n.str`human-readable description of the whole 
purchase`}
               />
               <InputCurrency<Unpaid>
                 readonly
                 name="total_amount"
-                label={i18n`Amount`}
-                tooltip={i18n`total price for the transaction`}
+                label={i18n.str`Amount`}
+                tooltip={i18n.str`total price for the transaction`}
               />
               <Input<Unpaid>
                 name="order_status"
                 readonly
-                label={i18n`Order status`}
+                label={i18n.str`Order status`}
               />
               <Input<Unpaid>
                 name="order_status_url"
                 readonly
-                label={i18n`Order status URL`}
+                label={i18n.str`Order status URL`}
               />
-              <TextField<Unpaid> name="taler_pay_uri" label={i18n`Payment 
URI`}>
+              <TextField<Unpaid>
+                name="taler_pay_uri"
+                label={i18n.str`Payment URI`}
+              >
                 <a target="_blank" rel="noreferrer" href={value.taler_pay_uri}>
                   {value.taler_pay_uri}
                 </a>
@@ -722,7 +725,7 @@ function UnpaidPage({
 
 export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode {
   const [showRefund, setShowRefund] = useState<string | undefined>(undefined);
-
+  const { i18n } = useTranslationContext();
   const DetailByStatus = function () {
     switch (selected.order_status) {
       case "claimed":
@@ -734,10 +737,10 @@ export function DetailPage({ id, selected, onRefund, 
onBack }: Props): VNode {
       default:
         return (
           <div>
-            <Translate>
+            <i18n.Translate>
               Unknown order status. This is an error, please contact the
               administrator.
-            </Translate>
+            </i18n.Translate>
           </div>
         );
     }
@@ -761,7 +764,7 @@ export function DetailPage({ id, selected, onRefund, onBack 
}: Props): VNode {
         <div class="column is-four-fifths">
           <div class="buttons is-right mt-5">
             <button class="button" onClick={onBack}>
-              <Translate>Back</Translate>
+              <i18n.Translate>Back</i18n.Translate>
             </button>
           </div>
         </div>
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 4633688ba..bb0240982 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
@@ -13,13 +13,13 @@
  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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { HttpError } from "../../../../hooks/backend.js";
-import { useOrderDetails, useOrderAPI } from "../../../../hooks/order.js";
-import { useTranslator } from "../../../../i18n/index.js";
+import { useOrderAPI, useOrderDetails } from "../../../../hooks/order.js";
 import { Notification } from "../../../../utils/types.js";
 import { DetailPage } from "./DetailPage.js";
 
@@ -43,7 +43,7 @@ export default function Update({
   const result = useOrderDetails(oid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
@@ -61,13 +61,13 @@ export default function Update({
           refundOrder(id, value)
             .then(() =>
               setNotif({
-                message: i18n`refund created successfully`,
+                message: i18n.str`refund created successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n`could not create the refund`,
+                message: i18n.str`could not create the refund`,
                 type: "ERROR",
                 description: error.message,
               }),
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 bca90e352..3639ae912 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
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { DatePicker } from "../../../../components/picker/DatePicker.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { CardTable } from "./Table.js";
 
 export interface ListPageProps {
@@ -74,8 +74,8 @@ export function ListPage({
   isNotWiredActive,
   onCreate,
 }: ListPageProps): VNode {
-  const i18n = useTranslator();
-  const dateTooltip = i18n`select date to show nearby orders`;
+  const { i18n } = useTranslationContext();
+  const dateTooltip = i18n.str`select date to show nearby orders`;
   const [pickDate, setPickDate] = useState(false);
   const [orderId, setOrderId] = useState<string>("");
 
@@ -91,13 +91,13 @@ export function ListPage({
                   type="text"
                   value={orderId}
                   onChange={(e) => setOrderId(e.currentTarget.value)}
-                  placeholder={i18n`order id`}
+                  placeholder={i18n.str`order id`}
                 />
                 {errorOrderId && <p class="help is-danger">{errorOrderId}</p>}
               </div>
               <span
                 class="has-tooltip-bottom"
-                data-tooltip={i18n`jump to order with the given order ID`}
+                data-tooltip={i18n.str`jump to order with the given order ID`}
               >
                 <button
                   class="button"
@@ -119,40 +119,40 @@ export function ListPage({
               <li class={isAllActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n`remove all filters`}
+                  data-tooltip={i18n.str`remove all filters`}
                 >
                   <a onClick={onShowAll}>
-                    <Translate>All</Translate>
+                    <i18n.Translate>All</i18n.Translate>
                   </a>
                 </div>
               </li>
               <li class={isPaidActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n`only show paid orders`}
+                  data-tooltip={i18n.str`only show paid orders`}
                 >
                   <a onClick={onShowPaid}>
-                    <Translate>Paid</Translate>
+                    <i18n.Translate>Paid</i18n.Translate>
                   </a>
                 </div>
               </li>
               <li class={isRefundedActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n`only show orders with refunds`}
+                  data-tooltip={i18n.str`only show orders with refunds`}
                 >
                   <a onClick={onShowRefunded}>
-                    <Translate>Refunded</Translate>
+                    <i18n.Translate>Refunded</i18n.Translate>
                   </a>
                 </div>
               </li>
               <li class={isNotWiredActive}>
                 <div
                   class="has-tooltip-left"
-                  data-tooltip={i18n`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}>
-                    <Translate>Not wired</Translate>
+                    <i18n.Translate>Not wired</i18n.Translate>
                   </a>
                 </div>
               </li>
@@ -165,7 +165,10 @@ export function ListPage({
               {jumpToDate && (
                 <div class="control">
                   <a class="button" onClick={() => onSelectDate(undefined)}>
-                    <span class="icon" data-tooltip={i18n`clear date filter`}>
+                    <span
+                      class="icon"
+                      data-tooltip={i18n.str`clear date filter`}
+                    >
                       <i class="mdi mdi-close" />
                     </span>
                   </a>
@@ -178,7 +181,7 @@ export function ListPage({
                     type="text"
                     readonly
                     value={!jumpToDate ? "" : format(jumpToDate, "yyyy/MM/dd")}
-                    placeholder={i18n`date (YYYY/MM/DD)`}
+                    placeholder={i18n.str`date (YYYY/MM/DD)`}
                     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 a1ec8d291..b0a836f81 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
@@ -20,6 +20,7 @@
  */
 
 import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { StateUpdater, useState } from "preact/hooks";
@@ -34,7 +35,6 @@ import { InputSelector } from 
"../../../../components/form/InputSelector.js";
 import { ConfirmModal } from "../../../../components/modal/index.js";
 import { useConfigContext } from "../../../../context/config.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { mergeRefunds } from "../../../../utils/amount.js";
 
 type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId;
@@ -63,7 +63,7 @@ export function CardTable({
 }: Props): VNode {
   const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="card has-table">
@@ -72,13 +72,13 @@ export function CardTable({
           <span class="icon">
             <i class="mdi mdi-cash-register" />
           </span>
-          <Translate>Orders</Translate>
+          <i18n.Translate>Orders</i18n.Translate>
         </p>
 
         <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`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" />
@@ -135,6 +135,7 @@ function Table({
   hasMoreAfter,
   hasMoreBefore,
 }: TableProps): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       {onLoadMoreBefore && (
@@ -143,20 +144,20 @@ function Table({
           disabled={!hasMoreBefore}
           onClick={onLoadMoreBefore}
         >
-          <Translate>load newer orders</Translate>
+          <i18n.Translate>load newer orders</i18n.Translate>
         </button>
       )}
       <table class="table is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th style={{ minWidth: 100 }}>
-              <Translate>Date</Translate>
+              <i18n.Translate>Date</i18n.Translate>
             </th>
             <th style={{ minWidth: 100 }}>
-              <Translate>Amount</Translate>
+              <i18n.Translate>Amount</i18n.Translate>
             </th>
             <th style={{ minWidth: 400 }}>
-              <Translate>Summary</Translate>
+              <i18n.Translate>Summary</i18n.Translate>
             </th>
             <th style={{ minWidth: 50 }} />
           </tr>
@@ -196,7 +197,7 @@ function Table({
                         type="button"
                         onClick={(): void => onRefund(i)}
                       >
-                        <Translate>Refund</Translate>
+                        <i18n.Translate>Refund</i18n.Translate>
                       </button>
                     )}
                     {!i.paid && (
@@ -205,7 +206,7 @@ function Table({
                         type="button"
                         onClick={(): void => onCopyURL(i)}
                       >
-                        <Translate>copy url</Translate>
+                        <i18n.Translate>copy url</i18n.Translate>
                       </button>
                     )}
                   </div>
@@ -221,7 +222,7 @@ function Table({
           disabled={!hasMoreAfter}
           onClick={onLoadMoreAfter}
         >
-          <Translate>load older orders</Translate>
+          <i18n.Translate>load older orders</i18n.Translate>
         </button>
       )}
     </div>
@@ -229,6 +230,7 @@ function Table({
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -237,7 +239,9 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>No orders have been found matching your query!</Translate>
+        <i18n.Translate>
+          No orders have been found matching your query!
+        </i18n.Translate>
       </p>
     </div>
   );
@@ -256,7 +260,7 @@ export function RefundModal({
 }: RefundModalProps): VNode {
   type State = { mainReason?: string; description?: string; refund?: string };
   const [form, setValue] = useState<State>({});
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   // const [errors, setErrors] = useState<FormErrors<State>>({});
 
   const refunds = (
@@ -281,20 +285,20 @@ export function RefundModal({
     : orderPrice;
 
   const isRefundable = Amounts.isNonZero(totalRefundable);
-  const duplicatedText = i18n`duplicated`;
+  const duplicatedText = i18n.str`duplicated`;
 
   const errors: FormErrors<State> = {
-    mainReason: !form.mainReason ? i18n`required` : undefined,
+    mainReason: !form.mainReason ? i18n.str`required` : undefined,
     description:
       !form.description && form.mainReason !== duplicatedText
-        ? i18n`required`
+        ? i18n.str`required`
         : undefined,
     refund: !form.refund
-      ? i18n`required`
+      ? i18n.str`required`
       : !Amounts.parse(form.refund)
-      ? i18n`invalid format`
+      ? i18n.str`invalid format`
       : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
-      ? i18n`this value exceed the refundable amount`
+      ? i18n.str`this value exceed the refundable amount`
       : undefined,
   };
   const hasErrors = Object.keys(errors).some(
@@ -339,13 +343,13 @@ export function RefundModal({
                 <thead>
                   <tr>
                     <th>
-                      <Translate>date</Translate>
+                      <i18n.Translate>date</i18n.Translate>
                     </th>
                     <th>
-                      <Translate>amount</Translate>
+                      <i18n.Translate>amount</i18n.Translate>
                     </th>
                     <th>
-                      <Translate>reason</Translate>
+                      <i18n.Translate>reason</i18n.Translate>
                     </th>
                   </tr>
                 </thead>
@@ -381,28 +385,28 @@ export function RefundModal({
         >
           <InputCurrency<State>
             name="refund"
-            label={i18n`Refund`}
-            tooltip={i18n`amount to be refunded`}
+            label={i18n.str`Refund`}
+            tooltip={i18n.str`amount to be refunded`}
           >
-            <Translate>Max refundable:</Translate>{" "}
+            <i18n.Translate>Max refundable:</i18n.Translate>{" "}
             {Amounts.stringify(totalRefundable)}
           </InputCurrency>
           <InputSelector
             name="mainReason"
-            label={i18n`Reason`}
+            label={i18n.str`Reason`}
             values={[
-              i18n`Choose one...`,
+              i18n.str`Choose one...`,
               duplicatedText,
-              i18n`requested by the customer`,
-              i18n`other`,
+              i18n.str`requested by the customer`,
+              i18n.str`other`,
             ]}
-            tooltip={i18n`why this order is being refunded`}
+            tooltip={i18n.str`why this order is being refunded`}
           />
           {form.mainReason && form.mainReason !== duplicatedText ? (
             <Input<State>
-              label={i18n`Description`}
+              label={i18n.str`Description`}
               name="description"
-              tooltip={i18n`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 315d78c63..e29c57a7c 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
@@ -19,7 +19,8 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode, Fragment } from "preact";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
@@ -31,10 +32,9 @@ import {
   useOrderAPI,
   useOrderDetails,
 } from "../../../../hooks/order.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
-import { RefundModal } from "./Table.js";
 import { ListPage } from "./ListPage.js";
+import { RefundModal } from "./Table.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -64,7 +64,7 @@ export default function OrderList({
 
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const [errorOrderId, setErrorOrderId] = useState<string | undefined>(
     undefined,
   );
@@ -86,7 +86,7 @@ export default function OrderList({
 
   async function testIfOrderExistAndSelect(orderId: string): Promise<void> {
     if (!orderId) {
-      setErrorOrderId(i18n`Enter an order id`);
+      setErrorOrderId(i18n.str`Enter an order id`);
       return;
     }
     try {
@@ -94,7 +94,7 @@ export default function OrderList({
       onSelect(orderId);
       setErrorOrderId(undefined);
     } catch {
-      setErrorOrderId(i18n`order not found`);
+      setErrorOrderId(i18n.str`order not found`);
     }
   }
 
@@ -136,13 +136,13 @@ export default function OrderList({
             refundOrder(orderToBeRefunded.order_id, value)
               .then(() =>
                 setNotif({
-                  message: i18n`refund created successfully`,
+                  message: i18n.str`refund created successfully`,
                   type: "SUCCESS",
                 }),
               )
               .catch((error) =>
                 setNotif({
-                  message: i18n`could not create the refund`,
+                  message: i18n.str`could not create the refund`,
                   type: "ERROR",
                   description: error.message,
                 }),
@@ -151,7 +151,7 @@ export default function OrderList({
           }
           onLoadError={(error) => {
             setNotif({
-              message: i18n`could not create the refund`,
+              message: i18n.str`could not create the refund`,
               type: "ERROR",
               description: error.message,
             });
@@ -161,7 +161,7 @@ export default function OrderList({
           onUnauthorized={onUnauthorized}
           onNotFound={() => {
             setNotif({
-              message: i18n`could not get the order to refund`,
+              message: i18n.str`could not get the order to refund`,
               type: "ERROR",
               // description: error.message
             });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx
index f6d7000ef..c18646be0 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
 import { ProductForm } from "../../../../components/product/ProductForm.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { useListener } from "../../../../hooks/listener.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 
 type Entity = MerchantBackend.Products.ProductAddDetail & {
   product_id: string;
@@ -43,7 +43,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     },
   );
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div>
@@ -56,19 +56,19 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
               <AsyncButton
                 onClick={submitForm}
                 data-tooltip={
                   !submitForm
-                    ? i18n`Need to complete marked fields`
+                    ? i18n.str`Need to complete marked fields`
                     : "confirm operation"
                 }
                 disabled={!submitForm}
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </AsyncButton>
             </div>
           </div>
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 62ecaf512..f82d85e94 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
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { useProductAPI } from "../../../../hooks/product.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
 
@@ -36,7 +36,7 @@ interface Props {
 export default function CreateProduct({ onConfirm, onBack }: Props): VNode {
   const { createProduct } = useProductAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
@@ -48,7 +48,7 @@ export default function CreateProduct({ onConfirm, onBack }: 
Props): VNode {
             .then(() => onConfirm())
             .catch((error) => {
               setNotif({
-                message: i18n`could not create product`,
+                message: i18n.str`could not create product`,
                 type: "ERROR",
                 description: error.message,
               });
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 515b36895..97d5afee5 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
@@ -19,19 +19,19 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { StateUpdater, useState } from "preact/hooks";
+import emptyImage from "../../../../assets/empty.png";
 import {
-  FormProvider,
   FormErrors,
+  FormProvider,
 } from "../../../../components/form/FormProvider.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import emptyImage from "../../../../assets/empty.png";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
-import { Amounts } from "@gnu-taler/taler-util";
 
 type Entity = MerchantBackend.Products.ProductDetail & WithId;
 
@@ -57,7 +57,7 @@ export function CardTable({
   const [rowSelection, rowSelectionHandler] = useState<string | undefined>(
     undefined,
   );
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="card has-table">
       <header class="card-header">
@@ -65,12 +65,12 @@ export function CardTable({
           <span class="icon">
             <i class="mdi mdi-shopping" />
           </span>
-          <Translate>Products</Translate>
+          <i18n.Translate>Products</i18n.Translate>
         </p>
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n`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">
@@ -121,32 +121,32 @@ function Table({
   onUpdate,
   onDelete,
 }: TableProps): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>Image</Translate>
+              <i18n.Translate>Image</i18n.Translate>
             </th>
             <th>
-              <Translate>Description</Translate>
+              <i18n.Translate>Description</i18n.Translate>
             </th>
             <th>
-              <Translate>Sell</Translate>
+              <i18n.Translate>Sell</i18n.Translate>
             </th>
             <th>
-              <Translate>Taxes</Translate>
+              <i18n.Translate>Taxes</i18n.Translate>
             </th>
             <th>
-              <Translate>Profit</Translate>
+              <i18n.Translate>Profit</i18n.Translate>
             </th>
             <th>
-              <Translate>Stock</Translate>
+              <i18n.Translate>Stock</i18n.Translate>
             </th>
             <th>
-              <Translate>Sold</Translate>
+              <i18n.Translate>Sold</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -207,7 +207,7 @@ function Table({
                     }
                     style={{ cursor: "pointer" }}
                   >
-                    {isFree ? i18n`free` : `${i.price} / ${i.unit}`}
+                    {isFree ? i18n.str`free` : `${i.price} / ${i.unit}`}
                   </td>
                   <td
                     onClick={() =>
@@ -245,26 +245,26 @@ function Table({
                     <div class="buttons is-right">
                       <span
                         class="has-tooltip-bottom"
-                        data-tooltip={i18n`go to product update page`}
+                        data-tooltip={i18n.str`go to product update page`}
                       >
                         <button
                           class="button is-small is-success "
                           type="button"
                           onClick={(): void => onSelect(i)}
                         >
-                          <Translate>Update</Translate>
+                          <i18n.Translate>Update</i18n.Translate>
                         </button>
                       </span>
                       <span
                         class="has-tooltip-left"
-                        data-tooltip={i18n`remove this product from the 
database`}
+                        data-tooltip={i18n.str`remove this product from the 
database`}
                       >
                         <button
                           class="button is-small is-danger"
                           type="button"
                           onClick={(): void => onDelete(i)}
                         >
-                          <Translate>Delete</Translate>
+                          <i18n.Translate>Delete</i18n.Translate>
                         </button>
                       </span>
                     </div>
@@ -316,7 +316,7 @@ function FastProductWithInfiniteStockUpdateForm({
   onCancel,
 }: FastProductUpdateFormProps) {
   const [value, valueHandler] = useState<UpdatePrice>({ price: product.price 
});
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
@@ -327,18 +327,18 @@ function FastProductWithInfiniteStockUpdateForm({
       >
         <InputCurrency<FastProductUpdate>
           name="price"
-          label={i18n`Price`}
-          tooltip={i18n`update the product with new price`}
+          label={i18n.str`Price`}
+          tooltip={i18n.str`update the product with new price`}
         />
       </FormProvider>
 
       <div class="buttons is-right mt-5">
         <button class="button" onClick={onCancel}>
-          <Translate>Cancel</Translate>
+          <i18n.Translate>Cancel</i18n.Translate>
         </button>
         <span
           class="has-tooltip-left"
-          data-tooltip={i18n`update product with new price`}
+          data-tooltip={i18n.str`update product with new price`}
         >
           <button
             class="button is-info"
@@ -349,7 +349,7 @@ function FastProductWithInfiniteStockUpdateForm({
               })
             }
           >
-            <Translate>Confirm</Translate>
+            <i18n.Translate>Confirm</i18n.Translate>
           </button>
         </span>
       </div>
@@ -383,7 +383,7 @@ function FastProductWithManagedStockUpdateForm({
   const hasErrors = Object.keys(errors).some(
     (k) => (errors as any)[k] !== undefined,
   );
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
@@ -395,31 +395,31 @@ function FastProductWithManagedStockUpdateForm({
       >
         <InputNumber<FastProductUpdate>
           name="incoming"
-          label={i18n`Incoming`}
-          tooltip={i18n`add more elements to the inventory`}
+          label={i18n.str`Incoming`}
+          tooltip={i18n.str`add more elements to the inventory`}
         />
         <InputNumber<FastProductUpdate>
           name="lost"
-          label={i18n`Lost`}
-          tooltip={i18n`report elements lost in the inventory`}
+          label={i18n.str`Lost`}
+          tooltip={i18n.str`report elements lost in the inventory`}
         />
         <InputCurrency<FastProductUpdate>
           name="price"
-          label={i18n`Price`}
-          tooltip={i18n`new price for the product`}
+          label={i18n.str`Price`}
+          tooltip={i18n.str`new price for the product`}
         />
       </FormProvider>
 
       <div class="buttons is-right mt-5">
         <button class="button" onClick={onCancel}>
-          <Translate>Cancel</Translate>
+          <i18n.Translate>Cancel</i18n.Translate>
         </button>
         <span
           class="has-tooltip-left"
           data-tooltip={
             hasErrors
-              ? i18n`the are value with errors`
-              : i18n`update product with new stock and price`
+              ? i18n.str`the are value with errors`
+              : i18n.str`update product with new stock and price`
           }
         >
           <button
@@ -434,7 +434,7 @@ function FastProductWithManagedStockUpdateForm({
               })
             }
           >
-            <Translate>Confirm</Translate>
+            <i18n.Translate>Confirm</i18n.Translate>
           </button>
         </span>
       </div>
@@ -451,6 +451,7 @@ function FastProductUpdateForm(props: 
FastProductUpdateFormProps) {
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -459,9 +460,9 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>
+        <i18n.Translate>
           There is no products yet, add more pressing the + sign
-        </Translate>
+        </i18n.Translate>
       </p>
     </div>
   );
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 7e9118d24..41a07a7aa 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
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
@@ -29,7 +30,6 @@ import {
   useInstanceProducts,
   useProductAPI,
 } from "../../../../hooks/product.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { CardTable } from "./Table.js";
 
@@ -51,7 +51,7 @@ export default function ProductList({
   const { deleteProduct, updateProduct } = useProductAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
@@ -69,13 +69,13 @@ export default function ProductList({
           updateProduct(id, prod)
             .then(() =>
               setNotif({
-                message: i18n`product updated successfully`,
+                message: i18n.str`product updated successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n`could not update the product`,
+                message: i18n.str`could not update the product`,
                 type: "ERROR",
                 description: error.message,
               }),
@@ -86,13 +86,13 @@ export default function ProductList({
           deleteProduct(prod.id)
             .then(() =>
               setNotif({
-                message: i18n`product delete successfully`,
+                message: i18n.str`product delete successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n`could not delete the product`,
+                message: i18n.str`could not delete the product`,
                 type: "ERROR",
                 description: error.message,
               }),
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx
index 841c0222f..d179ef87c 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
 import { ProductForm } from "../../../../components/product/ProductForm.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { MerchantBackend } from "../../../../declaration.js";
 import { useListener } from "../../../../hooks/listener.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 
 type Entity = MerchantBackend.Products.ProductDetail & { product_id: string };
 
@@ -42,7 +42,7 @@ export function UpdatePage({ product, onUpdate, onBack }: 
Props): VNode {
     },
   );
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div>
@@ -53,7 +53,7 @@ export function UpdatePage({ product, onUpdate, onBack }: 
Props): VNode {
               <div class="level-left">
                 <div class="level-item">
                   <span class="is-size-4">
-                    <Translate>Product id:</Translate>
+                    <i18n.Translate>Product id:</i18n.Translate>
                     <b>{product.product_id}</b>
                   </span>
                 </div>
@@ -75,19 +75,19 @@ export function UpdatePage({ product, onUpdate, onBack }: 
Props): VNode {
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
               <AsyncButton
                 onClick={submitForm}
                 data-tooltip={
                   !submitForm
-                    ? i18n`Need to complete marked fields`
+                    ? i18n.str`Need to complete marked fields`
                     : "confirm operation"
                 }
                 disabled={!submitForm}
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </AsyncButton>
             </div>
           </div>
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 3988fc9f0..e141dc52c 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
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
@@ -26,7 +27,6 @@ import { NotificationCard } from 
"../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { HttpError } from "../../../../hooks/backend.js";
 import { useProductAPI, useProductDetails } from 
"../../../../hooks/product.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { UpdatePage } from "./UpdatePage.js";
 
@@ -51,7 +51,7 @@ export default function UpdateProduct({
   const result = useProductDetails(pid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
@@ -69,7 +69,7 @@ export default function UpdateProduct({
             .then(onConfirm)
             .catch((error) => {
               setNotif({
-                message: i18n`could not create product`,
+                message: i18n.str`could not create product`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
index 2c3e963b8..de2319636 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
@@ -19,24 +19,23 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { StateUpdater, useEffect, useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
 import {
   FormErrors,
   FormProvider,
 } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { ExchangeBackend, MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
-import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
-import { canonicalizeBaseUrl, ExchangeKeysJson } from "@gnu-taler/taler-util";
+import { request } from "../../../../hooks/backend.js";
 import {
   PAYTO_WIRE_METHOD_LOOKUP,
   URL_REGEX,
 } from "../../../../utils/constants.js";
-import { request } from "../../../../hooks/backend.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
 
 type Entity = MerchantBackend.Tips.ReserveCreateRequest;
 
@@ -66,7 +65,7 @@ function ViewStep({
   submitForm,
   setReserve,
 }: ViewProps): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const [wireMethods, setWireMethods] = useState<Array<string>>([]);
   const [exchangeQueryError, setExchangeQueryError] = useState<
     string | undefined
@@ -82,12 +81,12 @@ function ViewStep({
         initial_balance: !reserve.initial_balance
           ? "cannot be empty"
           : !(parseInt(reserve.initial_balance.split(":")[1], 10) > 0)
-          ? i18n`it should be greater than 0`
+          ? i18n.str`it should be greater than 0`
           : undefined,
         exchange_url: !reserve.exchange_url
-          ? i18n`cannot be empty`
+          ? i18n.str`cannot be empty`
           : !URL_REGEX.test(reserve.exchange_url)
-          ? i18n`must be a valid URL`
+          ? i18n.str`must be a valid URL`
           : !exchangeQueryError
           ? undefined
           : exchangeQueryError,
@@ -106,20 +105,20 @@ function ViewStep({
           >
             <InputCurrency<Entity>
               name="initial_balance"
-              label={i18n`Initial balance`}
-              tooltip={i18n`balance prior to deposit`}
+              label={i18n.str`Initial balance`}
+              tooltip={i18n.str`balance prior to deposit`}
             />
             <Input<Entity>
               name="exchange_url"
-              label={i18n`Exchange URL`}
-              tooltip={i18n`URL of exchange`}
+              label={i18n.str`Exchange URL`}
+              tooltip={i18n.str`URL of exchange`}
             />
           </FormProvider>
 
           <div class="buttons is-right mt-5">
             {onBack && (
               <button class="button" onClick={onBack}>
-                <Translate>Cancel</Translate>
+                <i18n.Translate>Cancel</i18n.Translate>
               </button>
             )}
             <AsyncButton
@@ -143,12 +142,12 @@ function ViewStep({
               }}
               data-tooltip={
                 hasErrors
-                  ? i18n`Need to complete marked fields`
+                  ? i18n.str`Need to complete marked fields`
                   : "confirm operation"
               }
               disabled={hasErrors}
             >
-              <Translate>Next</Translate>
+              <i18n.Translate>Next</i18n.Translate>
             </AsyncButton>
           </div>
         </Fragment>
@@ -157,7 +156,9 @@ function ViewStep({
 
     case Steps.WIRE_METHOD: {
       const errors: FormErrors<Entity> = {
-        wire_method: !reserve.wire_method ? i18n`cannot be empty` : undefined,
+        wire_method: !reserve.wire_method
+          ? i18n.str`cannot be empty`
+          : undefined,
       };
 
       const hasErrors = Object.keys(errors).some(
@@ -172,22 +173,22 @@ function ViewStep({
           >
             <InputCurrency<Entity>
               name="initial_balance"
-              label={i18n`Initial balance`}
-              tooltip={i18n`balance prior to deposit`}
+              label={i18n.str`Initial balance`}
+              tooltip={i18n.str`balance prior to deposit`}
               readonly
             />
             <Input<Entity>
               name="exchange_url"
-              label={i18n`Exchange URL`}
-              tooltip={i18n`URL of exchange`}
+              label={i18n.str`Exchange URL`}
+              tooltip={i18n.str`URL of exchange`}
               readonly
             />
             <InputSelector<Entity>
               name="wire_method"
-              label={i18n`Wire method`}
-              tooltip={i18n`method to use for wire transfer`}
+              label={i18n.str`Wire method`}
+              tooltip={i18n.str`method to use for wire transfer`}
               values={wireMethods}
-              placeholder={i18n`Select one wire method`}
+              placeholder={i18n.str`Select one wire method`}
             />
           </FormProvider>
           <div class="buttons is-right mt-5">
@@ -196,19 +197,19 @@ function ViewStep({
                 class="button"
                 onClick={() => setCurrentStep(Steps.EXCHANGE)}
               >
-                <Translate>Back</Translate>
+                <i18n.Translate>Back</i18n.Translate>
               </button>
             )}
             <AsyncButton
               onClick={submitForm}
               data-tooltip={
                 hasErrors
-                  ? i18n`Need to complete marked fields`
+                  ? i18n.str`Need to complete marked fields`
                   : "confirm operation"
               }
               disabled={hasErrors}
             >
-              <Translate>Confirm</Translate>
+              <i18n.Translate>Confirm</i18n.Translate>
             </AsyncButton>
           </div>
         </Fragment>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
index 9bb228e1f..92427f3dc 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
@@ -14,11 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
+import { QR } from "../../../../components/exception/QR.js";
 import { CreatedSuccessfully as Template } from 
"../../../../components/notifications/CreatedSuccessfully.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate } from "../../../../i18n/index.js";
-import { QR } from "../../../../components/exception/QR.js";
 
 type Entity = {
   request: MerchantBackend.Tips.ReserveCreateRequest;
@@ -37,7 +37,7 @@ export function CreatedSuccessfully({
   onCreateAnother,
 }: Props): VNode {
   const link = 
`${entity.response.payto_uri}?message=${entity.response.reserve_pub}&amount=${entity.request.initial_balance}`;
-
+  const { i18n } = useTranslationContext();
   return (
     <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
       <div class="field is-horizontal">
@@ -85,16 +85,16 @@ export function CreatedSuccessfully({
         </div>
       </div>
       <p class="is-size-5">
-        <Translate>
+        <i18n.Translate>
           To complete the setup of the reserve, you must now initiate a wire
           transfer using the given wire transfer subject and crediting the
           specified amount to the indicated account of the exchange.
-        </Translate>
+        </i18n.Translate>
       </p>
       <p class="is-size-5">
-        <Translate>
+        <i18n.Translate>
           If your system supports RFC 8905, you can do this by opening this 
URI:
-        </Translate>
+        </i18n.Translate>
       </p>
       <pre>
         <a target="_blank" rel="noreferrer" href={link}>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
index 8b3ffb4ac..0d39fc298 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
@@ -19,12 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { useReservesAPI } from "../../../../hooks/reserves.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
 import { CreatePage } from "./CreatePage.js";
@@ -35,7 +35,7 @@ interface Props {
 export default function CreateReserve({ onBack, onConfirm }: Props): VNode {
   const { createReserve } = useReservesAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const [createdOk, setCreatedOk] = useState<
     | {
@@ -59,7 +59,7 @@ export default function CreateReserve({ onBack, onConfirm }: 
Props): VNode {
             .then((r) => setCreatedOk({ request, response: r.data }))
             .catch((error) => {
               setNotif({
-                message: i18n`could not create reserve`,
+                message: i18n.str`could not create reserve`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
index b0b291859..1d54198c8 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
@@ -20,6 +20,7 @@
  */
 
 import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -29,13 +30,9 @@ import { Input } from "../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputDate } from "../../../../components/form/InputDate.js";
 import { TextField } from "../../../../components/form/TextField.js";
-import {
-  ContinueModal,
-  SimpleModal,
-} from "../../../../components/modal/index.js";
+import { SimpleModal } from "../../../../components/modal/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { useTipDetails } from "../../../../hooks/reserves.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { TipInfo } from "./TipInfo.js";
 
 type Entity = MerchantBackend.Tips.ReserveDetail;
@@ -48,7 +45,7 @@ interface Props {
 }
 
 export function DetailPage({ id, selected, onBack }: Props): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const didExchangeAckTransfer = Amounts.isNonZero(
     Amounts.parseOrThrow(selected.exchange_initial_amount),
   );
@@ -62,22 +59,22 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
           <FormProvider object={{ ...selected, id }} valueHandler={null}>
             <InputDate<Entity>
               name="creation_time"
-              label={i18n`Created at`}
+              label={i18n.str`Created at`}
               readonly
             />
             <InputDate<Entity>
               name="expiration_time"
-              label={i18n`Valid until`}
+              label={i18n.str`Valid until`}
               readonly
             />
             <InputCurrency<Entity>
               name="merchant_initial_amount"
-              label={i18n`Created balance`}
+              label={i18n.str`Created balance`}
               readonly
             />
             <TextField<Entity>
               name="exchange_url"
-              label={i18n`Exchange URL`}
+              label={i18n.str`Exchange URL`}
               readonly
             >
               <a target="_blank" rel="noreferrer" href={selected.exchange_url}>
@@ -89,27 +86,27 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
               <Fragment>
                 <InputCurrency<Entity>
                   name="exchange_initial_amount"
-                  label={i18n`Exchange balance`}
+                  label={i18n.str`Exchange balance`}
                   readonly
                 />
                 <InputCurrency<Entity>
                   name="pickup_amount"
-                  label={i18n`Picked up`}
+                  label={i18n.str`Picked up`}
                   readonly
                 />
                 <InputCurrency<Entity>
                   name="committed_amount"
-                  label={i18n`Committed`}
+                  label={i18n.str`Committed`}
                   readonly
                 />
               </Fragment>
             )}
             <Input<Entity>
               name="payto_uri"
-              label={i18n`Account address`}
+              label={i18n.str`Account address`}
               readonly
             />
-            <Input name="id" label={i18n`Subject`} readonly />
+            <Input name="id" label={i18n.str`Subject`} readonly />
           </FormProvider>
 
           {didExchangeAckTransfer ? (
@@ -120,7 +117,7 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
                     <span class="icon">
                       <i class="mdi mdi-cash-register" />
                     </span>
-                    <Translate>Tips</Translate>
+                    <i18n.Translate>Tips</i18n.Translate>
                   </p>
                 </header>
                 <div class="card-content">
@@ -139,18 +136,18 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
           ) : (
             <Fragment>
               <p class="is-size-5">
-                <Translate>
+                <i18n.Translate>
                   To complete the setup of the reserve, you must now initiate a
                   wire transfer using the given wire transfer subject and
                   crediting the specified amount to the indicated account of 
the
                   exchange.
-                </Translate>
+                </i18n.Translate>
               </p>
               <p class="is-size-5">
-                <Translate>
+                <i18n.Translate>
                   If your system supports RFC 8905, you can do this by opening
                   this URI:
-                </Translate>
+                </i18n.Translate>
               </p>
               <pre>
                 <a target="_blank" rel="noreferrer" href={link}>
@@ -163,7 +160,7 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
 
           <div class="buttons is-right mt-5">
             <button class="button" onClick={onBack}>
-              <Translate>Back</Translate>
+              <i18n.Translate>Back</i18n.Translate>
             </button>
           </div>
         </div>
@@ -174,6 +171,7 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -182,7 +180,9 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>No tips has been authorized from this reserve</Translate>
+        <i18n.Translate>
+          No tips has been authorized from this reserve
+        </i18n.Translate>
       </p>
     </div>
   );
@@ -193,22 +193,23 @@ interface TableProps {
 }
 
 function Table({ tips }: TableProps): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>Authorized</Translate>
+              <i18n.Translate>Authorized</i18n.Translate>
             </th>
             <th>
-              <Translate>Picked up</Translate>
+              <i18n.Translate>Picked up</i18n.Translate>
             </th>
             <th>
-              <Translate>Reason</Translate>
+              <i18n.Translate>Reason</i18n.Translate>
             </th>
             <th>
-              <Translate>Expiration</Translate>
+              <i18n.Translate>Expiration</i18n.Translate>
             </th>
           </tr>
         </thead>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
index 5200abedf..3a591c555 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
@@ -19,8 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
+import * as yup from "yup";
 import {
   FormErrors,
   FormProvider,
@@ -32,10 +34,8 @@ import {
   ContinueModal,
 } from "../../../../components/modal/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { AuthorizeTipSchema } from "../../../../schemas/index.js";
 import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
-import * as yup from "yup";
 
 interface AuthorizeTipModalProps {
   onCancel: () => void;
@@ -54,7 +54,7 @@ export function AuthorizeTipModal({
   // const result = useOrderDetails(id)
   type State = MerchantBackend.Tips.TipCreateRequest;
   const [form, setValue] = useState<Partial<State>>({});
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   // const [errors, setErrors] = useState<FormErrors<State>>({})
   let errors: FormErrors<State> = {};
@@ -104,19 +104,19 @@ export function AuthorizeTipModal({
       >
         <InputCurrency<State>
           name="amount"
-          label={i18n`Amount`}
-          tooltip={i18n`amount of tip`}
+          label={i18n.str`Amount`}
+          tooltip={i18n.str`amount of tip`}
         />
         <Input<State>
           name="justification"
-          label={i18n`Justification`}
+          label={i18n.str`Justification`}
           inputType="multiline"
-          tooltip={i18n`reason for the tip`}
+          tooltip={i18n.str`reason for the tip`}
         />
         <Input<State>
           name="next_url"
-          label={i18n`URL after tip`}
-          tooltip={i18n`URL to visit after tip payment`}
+          label={i18n.str`URL after tip`}
+          tooltip={i18n.str`URL to visit after tip payment`}
         />
       </FormProvider>
     </ConfirmModal>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
index 86b79d1dd..36768855d 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
@@ -19,10 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 
 type Entity = MerchantBackend.Tips.ReserveStatusEntry & WithId;
 
@@ -51,7 +51,7 @@ export function CardTable({
     return prev;
   }, new Array<Array<Entity>>([], []));
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
@@ -62,7 +62,7 @@ export function CardTable({
               <span class="icon">
                 <i class="mdi mdi-cash" />
               </span>
-              <Translate>Reserves not yet funded</Translate>
+              <i18n.Translate>Reserves not yet funded</i18n.Translate>
             </p>
           </header>
           <div class="card-content">
@@ -86,11 +86,14 @@ export function CardTable({
             <span class="icon">
               <i class="mdi mdi-cash" />
             </span>
-            <Translate>Reserves ready</Translate>
+            <i18n.Translate>Reserves ready</i18n.Translate>
           </p>
           <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`add new 
reserve`}>
+            <span
+              class="has-tooltip-left"
+              data-tooltip={i18n.str`add new reserve`}
+            >
               <button class="button is-info" type="button" onClick={onCreate}>
                 <span class="icon is-small">
                   <i class="mdi mdi-plus mdi-36px" />
@@ -127,26 +130,26 @@ interface TableProps {
 }
 
 function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode 
{
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>Created at</Translate>
+              <i18n.Translate>Created at</i18n.Translate>
             </th>
             <th>
-              <Translate>Expires at</Translate>
+              <i18n.Translate>Expires at</i18n.Translate>
             </th>
             <th>
-              <Translate>Initial</Translate>
+              <i18n.Translate>Initial</i18n.Translate>
             </th>
             <th>
-              <Translate>Picked up</Translate>
+              <i18n.Translate>Picked up</i18n.Translate>
             </th>
             <th>
-              <Translate>Committed</Translate>
+              <i18n.Translate>Committed</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -196,7 +199,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: 
TableProps): VNode {
                   <div class="buttons is-right">
                     <button
                       class="button is-small is-danger has-tooltip-left"
-                      data-tooltip={i18n`delete selected reserve from the 
database`}
+                      data-tooltip={i18n.str`delete selected reserve from the 
database`}
                       type="button"
                       onClick={(): void => onDelete(i)}
                     >
@@ -204,7 +207,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: 
TableProps): VNode {
                     </button>
                     <button
                       class="button is-small is-info has-tooltip-left"
-                      data-tooltip={i18n`authorize new tip from selected 
reserve`}
+                      data-tooltip={i18n.str`authorize new tip from selected 
reserve`}
                       type="button"
                       onClick={(): void => onNewTip(i)}
                     >
@@ -222,6 +225,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: 
TableProps): VNode {
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -230,10 +234,10 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>
+        <i18n.Translate>
           There is no ready reserves yet, add more pressing the + sign or fund
           them
-        </Translate>
+        </i18n.Translate>
       </p>
     </div>
   );
@@ -244,20 +248,20 @@ function TableWithoutFund({
   onSelect,
   onDelete,
 }: TableProps): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>Created at</Translate>
+              <i18n.Translate>Created at</i18n.Translate>
             </th>
             <th>
-              <Translate>Expires at</Translate>
+              <i18n.Translate>Expires at</i18n.Translate>
             </th>
             <th>
-              <Translate>Expected Balance</Translate>
+              <i18n.Translate>Expected Balance</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -296,7 +300,7 @@ function TableWithoutFund({
                     <button
                       class="button is-small is-danger jb-modal 
has-tooltip-left"
                       type="button"
-                      data-tooltip={i18n`delete selected reserve from the 
database`}
+                      data-tooltip={i18n.str`delete selected reserve from the 
database`}
                       onClick={(): void => onDelete(i)}
                     >
                       Delete
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
index 182b3f72c..9c3255ee8 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
@@ -29,10 +30,9 @@ import {
   useInstanceReserves,
   useReservesAPI,
 } from "../../../../hooks/reserves.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
-import { CardTable } from "./Table.js";
 import { AuthorizeTipModal } from "./AutorizeTipModal.js";
+import { CardTable } from "./Table.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -57,7 +57,7 @@ export default function ListTips({
   const result = useInstanceReserves();
   const { deleteReserve, authorizeTipReserve } = useReservesAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const [reserveForTip, setReserveForTip] = useState<string | undefined>(
     undefined,
   );
@@ -93,7 +93,7 @@ export default function ListTips({
               });
             } catch (error) {
               setNotif({
-                message: i18n`could not create the tip`,
+                message: i18n.str`could not create the tip`,
                 type: "ERROR",
                 description: error instanceof Error ? error.message : 
undefined,
               });
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 dba4b5d14..2a47c22a0 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
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
@@ -33,7 +34,6 @@ import { InputNumber } from 
"../../../../components/form/InputNumber.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = MerchantBackend.Template.TemplateAddDetails;
@@ -44,7 +44,7 @@ interface Props {
 }
 
 export function CreatePage({ onCreate, onBack }: Props): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const backend = useBackendContext();
 
   const [state, setState] = useState<Partial<Entity>>({
@@ -57,23 +57,23 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
   });
 
   const errors: FormErrors<Entity> = {
-    template_id: !state.template_id ? i18n`should not be empty` : undefined,
+    template_id: !state.template_id ? i18n.str`should not be empty` : 
undefined,
     template_description: !state.template_description
-      ? i18n`should not be empty`
+      ? i18n.str`should not be empty`
       : undefined,
     template_contract: !state.template_contract
       ? undefined
       : undefinedIfEmpty({
           minimum_age:
             state.template_contract.minimum_age < 0
-              ? i18n`should be greater that 0`
+              ? i18n.str`should be greater that 0`
               : undefined,
           pay_duration: !state.template_contract.pay_duration
-            ? i18n`can't be empty`
+            ? i18n.str`can't be empty`
             : state.template_contract.pay_duration.d_us === "forever"
             ? undefined
             : state.template_contract.pay_duration.d_us < 1000
-            ? i18n`to short`
+            ? i18n.str`to short`
             : undefined,
         }),
   };
@@ -101,57 +101,57 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               <InputWithAddon<Entity>
                 name="template_id"
                 addonBefore={`${backend.url}/instances/templates/`}
-                label={i18n`Identifier`}
-                tooltip={i18n`Name of the template in URLs.`}
+                label={i18n.str`Identifier`}
+                tooltip={i18n.str`Name of the template in URLs.`}
               />
 
               <Input<Entity>
                 name="template_description"
-                label={i18n`Description`}
+                label={i18n.str`Description`}
                 help=""
-                tooltip={i18n`Describe what this template stands for`}
+                tooltip={i18n.str`Describe what this template stands for`}
               />
               <Input
                 name="template_contract.summary"
                 inputType="multiline"
-                label={i18n`Order summary`}
-                tooltip={i18n`Title of the order to be shown to the customer`}
+                label={i18n.str`Order summary`}
+                tooltip={i18n.str`Title of the order to be shown to the 
customer`}
               />
               <InputCurrency
                 name="template_contract.amount"
-                label={i18n`Order price`}
-                tooltip={i18n`Order price`}
+                label={i18n.str`Order price`}
+                tooltip={i18n.str`Order price`}
               />
               <InputNumber
                 name="template_contract.minimum_age"
-                label={i18n`Minimum age`}
+                label={i18n.str`Minimum age`}
                 help=""
-                tooltip={i18n`Is this contract restricted to some age?`}
+                tooltip={i18n.str`Is this contract restricted to some age?`}
               />
               <InputDuration
                 name="template_contract.pay_duration"
-                label={i18n`Payment timeout`}
+                label={i18n.str`Payment timeout`}
                 help=""
-                tooltip={i18n`How much time has the customer to complete the 
payment once the order was created.`}
+                tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
               />
             </FormProvider>
 
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
               <AsyncButton
                 disabled={hasErrors}
                 data-tooltip={
                   hasErrors
-                    ? i18n`Need to complete marked fields`
+                    ? i18n.str`Need to complete marked fields`
                     : "confirm operation"
                 }
                 onClick={submitForm}
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </AsyncButton>
             </div>
           </div>
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 bfedb7369..dcbf70106 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
@@ -19,14 +19,12 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceDetails } from "../../../../hooks/instance.js";
 import { useTemplateAPI } from "../../../../hooks/templates.js";
-import { useTransferAPI } from "../../../../hooks/transfer.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
 
@@ -39,7 +37,7 @@ interface Props {
 export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
   const { createTemplate } = useTemplateAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <>
@@ -51,7 +49,7 @@ export default function CreateTransfer({ onConfirm, onBack }: 
Props): VNode {
             .then(() => onConfirm())
             .catch((error) => {
               setNotif({
-                message: i18n`could not inform template`,
+                message: i18n.str`could not inform template`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
index dd983918f..9d289e957 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
@@ -21,7 +21,7 @@
 
 import { h, VNode } from "preact";
 import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslator } from "../../../../i18n/index.js";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { CardTable } from "./Table.js";
 
 export interface Props {
@@ -43,7 +43,7 @@ export function ListPage({
 }: Props): VNode {
   const form = { payto_uri: "" };
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <section class="section is-main-section">
       <CardTable
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 bc8477039..fd6ea5f6f 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
@@ -19,10 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { StateUpdater, useState } from "preact/hooks";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 
 type Entity = MerchantBackend.Template.TemplateEntry;
 
@@ -49,7 +49,7 @@ export function CardTable({
 }: Props): VNode {
   const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="card has-table">
@@ -58,10 +58,13 @@ export function CardTable({
           <span class="icon">
             <i class="mdi mdi-newspaper" />
           </span>
-          <Translate>Templates</Translate>
+          <i18n.Translate>Templates</i18n.Translate>
         </p>
         <div class="card-header-icon" aria-label="more options">
-          <span class="has-tooltip-left" data-tooltip={i18n`add new 
templates`}>
+          <span
+            class="has-tooltip-left"
+            data-tooltip={i18n.str`add new templates`}
+          >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
                 <i class="mdi mdi-plus mdi-36px" />
@@ -120,27 +123,27 @@ function Table({
   hasMoreAfter,
   hasMoreBefore,
 }: TableProps): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n`load more templates before the first one`}
+          data-tooltip={i18n.str`load more templates before the first one`}
           disabled={!hasMoreBefore}
           onClick={onLoadMoreBefore}
         >
-          <Translate>load newer templates</Translate>
+          <i18n.Translate>load newer templates</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>ID</Translate>
+              <i18n.Translate>ID</i18n.Translate>
             </th>
             <th>
-              <Translate>Description</Translate>
+              <i18n.Translate>Description</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -164,7 +167,7 @@ function Table({
                 <td>
                   <button
                     class="button is-danger is-small has-tooltip-left"
-                    data-tooltip={i18n`delete selected templates from the 
database`}
+                    data-tooltip={i18n.str`delete selected templates from the 
database`}
                     onClick={() => onDelete(i)}
                   >
                     Delete
@@ -178,11 +181,11 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n`load more templates after the last one`}
+          data-tooltip={i18n.str`load more templates after the last one`}
           disabled={!hasMoreAfter}
           onClick={onLoadMoreAfter}
         >
-          <Translate>load older templates</Translate>
+          <i18n.Translate>load older templates</i18n.Translate>
         </button>
       )}
     </div>
@@ -190,6 +193,7 @@ function Table({
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -198,9 +202,9 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>
+        <i18n.Translate>
           There is no templates yet, add more pressing the + sign
-        </Translate>
+        </i18n.Translate>
       </p>
     </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 0d58093d3..dcac23983 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
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
@@ -29,7 +30,6 @@ import {
   useInstanceTemplates,
   useTemplateAPI,
 } from "../../../../hooks/templates.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { ListPage } from "./ListPage.js";
 
@@ -49,7 +49,7 @@ export default function ListTemplates({
   onNotFound,
 }: Props): VNode {
   const [position, setPosition] = useState<string | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { deleteTemplate } = useTemplateAPI();
   const result = useInstanceTemplates({ position }, (id) => setPosition(id));
@@ -77,13 +77,13 @@ export default function ListTemplates({
           deleteTemplate(e.template_id)
             .then(() =>
               setNotif({
-                message: i18n`template delete successfully`,
+                message: i18n.str`template delete successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n`could not delete the template`,
+                message: i18n.str`could not delete the template`,
                 type: "ERROR",
                 description: error.message,
               }),
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 42d9e5825..a49e8000b 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
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
@@ -31,11 +32,8 @@ import { InputCurrency } from 
"../../../../components/form/InputCurrency.js";
 import { InputDuration } from "../../../../components/form/InputDuration.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { ProductForm } from "../../../../components/product/ProductForm.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { useListener } from "../../../../hooks/listener.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
@@ -47,28 +45,28 @@ interface Props {
 }
 
 export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const backend = useBackendContext();
 
   const [state, setState] = useState<Partial<Entity>>(template);
 
   const errors: FormErrors<Entity> = {
     template_description: !state.template_description
-      ? i18n`should not be empty`
+      ? i18n.str`should not be empty`
       : undefined,
     template_contract: !state.template_contract
       ? undefined
       : undefinedIfEmpty({
           minimum_age:
             state.template_contract.minimum_age < 0
-              ? i18n`should be greater that 0`
+              ? i18n.str`should be greater that 0`
               : undefined,
           pay_duration: !state.template_contract.pay_duration
-            ? i18n`can't be empty`
+            ? i18n.str`can't be empty`
             : state.template_contract.pay_duration.d_us === "forever"
             ? undefined
             : state.template_contract.pay_duration.d_us < 1000
-            ? i18n`to short`
+            ? i18n.str`to short`
             : undefined,
         }),
   };
@@ -112,57 +110,57 @@ export function UpdatePage({ template, onUpdate, onBack 
}: Props): VNode {
                   name="id"
                   addonBefore={`templates/`}
                   readonly
-                  label={i18n`Identifier`}
-                  tooltip={i18n`Name of the template in URLs.`}
+                  label={i18n.str`Identifier`}
+                  tooltip={i18n.str`Name of the template in URLs.`}
                 />
 
                 <Input<Entity>
                   name="template_description"
-                  label={i18n`Description`}
+                  label={i18n.str`Description`}
                   help=""
-                  tooltip={i18n`Describe what this template stands for`}
+                  tooltip={i18n.str`Describe what this template stands for`}
                 />
                 <Input
                   name="template_contract.summary"
                   inputType="multiline"
-                  label={i18n`Order summary`}
-                  tooltip={i18n`Title of the order to be shown to the 
customer`}
+                  label={i18n.str`Order summary`}
+                  tooltip={i18n.str`Title of the order to be shown to the 
customer`}
                 />
                 <InputCurrency
                   name="template_contract.amount"
-                  label={i18n`Order price`}
-                  tooltip={i18n`total product price added up`}
+                  label={i18n.str`Order price`}
+                  tooltip={i18n.str`total product price added up`}
                 />
                 <InputNumber
                   name="template_contract.minimum_age"
-                  label={i18n`Minimum age`}
+                  label={i18n.str`Minimum age`}
                   help=""
-                  tooltip={i18n`Is this contract restricted to some age?`}
+                  tooltip={i18n.str`Is this contract restricted to some age?`}
                 />
                 <InputDuration
                   name="template_contract.pay_duration"
-                  label={i18n`Payment timeout`}
+                  label={i18n.str`Payment timeout`}
                   help=""
-                  tooltip={i18n`How much time has the customer to complete the 
payment once the order was created.`}
+                  tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
                 />
               </FormProvider>
 
               <div class="buttons is-right mt-5">
                 {onBack && (
                   <button class="button" onClick={onBack}>
-                    <Translate>Cancel</Translate>
+                    <i18n.Translate>Cancel</i18n.Translate>
                   </button>
                 )}
                 <AsyncButton
                   disabled={hasErrors}
                   data-tooltip={
                     hasErrors
-                      ? i18n`Need to complete marked fields`
+                      ? i18n.str`Need to complete marked fields`
                       : "confirm operation"
                   }
                   onClick={submitForm}
                 >
-                  <Translate>Confirm</Translate>
+                  <i18n.Translate>Confirm</i18n.Translate>
                 </AsyncButton>
               </div>
             </div>
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 25dc9abdc..4a4cc4274 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
@@ -19,18 +19,17 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
 import { HttpError } from "../../../../hooks/backend.js";
-import { useProductAPI, useProductDetails } from 
"../../../../hooks/product.js";
 import {
   useTemplateAPI,
   useTemplateDetails,
 } from "../../../../hooks/templates.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { UpdatePage } from "./UpdatePage.js";
 
@@ -56,7 +55,7 @@ export default function UpdateTemplate({
   const result = useTemplateDetails(tid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
@@ -74,7 +73,7 @@ export default function UpdateTemplate({
             .then(onConfirm)
             .catch((error) => {
               setNotif({
-                message: i18n`could not update template`,
+                message: i18n.str`could not update template`,
                 type: "ERROR",
                 description: error.message,
               });
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 5b041df7c..5303d14d9 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
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
@@ -31,7 +32,6 @@ import { InputCurrency } from 
"../../../../components/form/InputCurrency.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { useConfigContext } from "../../../../context/config.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import {
   CROCKFORD_BASE32_REGEX,
   URL_REGEX,
@@ -46,7 +46,7 @@ interface Props {
 }
 
 export function CreatePage({ accounts, onCreate, onBack }: Props): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const { currency } = useConfigContext();
 
   const [state, setState] = useState<Partial<Entity>>({
@@ -58,18 +58,18 @@ export function CreatePage({ accounts, onCreate, onBack }: 
Props): VNode {
 
   const errors: FormErrors<Entity> = {
     wtid: !state.wtid
-      ? i18n`cannot be empty`
+      ? i18n.str`cannot be empty`
       : !CROCKFORD_BASE32_REGEX.test(state.wtid)
-      ? i18n`check the id, does not look valid`
+      ? i18n.str`check the id, does not look valid`
       : state.wtid.length !== 52
-      ? i18n`should have 52 characters, current ${state.wtid.length}`
+      ? i18n.str`should have 52 characters, current ${state.wtid.length}`
       : undefined,
-    payto_uri: !state.payto_uri ? i18n`cannot be empty` : undefined,
-    credit_amount: !state.credit_amount ? i18n`cannot be empty` : undefined,
+    payto_uri: !state.payto_uri ? i18n.str`cannot be empty` : undefined,
+    credit_amount: !state.credit_amount ? i18n.str`cannot be empty` : 
undefined,
     exchange_url: !state.exchange_url
-      ? i18n`cannot be empty`
+      ? i18n.str`cannot be empty`
       : !URL_REGEX.test(state.exchange_url)
-      ? i18n`URL doesn't have the right format`
+      ? i18n.str`URL doesn't have the right format`
       : undefined,
   };
 
@@ -95,46 +95,46 @@ export function CreatePage({ accounts, onCreate, onBack }: 
Props): VNode {
             >
               <InputSelector
                 name="payto_uri"
-                label={i18n`Credited bank account`}
+                label={i18n.str`Credited bank account`}
                 values={accounts}
-                placeholder={i18n`Select one account`}
-                tooltip={i18n`Bank account of the merchant where the payment 
was received`}
+                placeholder={i18n.str`Select one account`}
+                tooltip={i18n.str`Bank account of the merchant where the 
payment was received`}
               />
               <Input<Entity>
                 name="wtid"
-                label={i18n`Wire transfer ID`}
+                label={i18n.str`Wire transfer ID`}
                 help=""
-                tooltip={i18n`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"
-                label={i18n`Exchange URL`}
-                tooltip={i18n`Base URL of the exchange that made the transfer, 
should have been in the wire transfer subject`}
+                label={i18n.str`Exchange URL`}
+                tooltip={i18n.str`Base URL of the exchange that made the 
transfer, should have been in the wire transfer subject`}
                 help="http://exchange.taler:8081/";
               />
               <InputCurrency<Entity>
                 name="credit_amount"
-                label={i18n`Amount credited`}
-                tooltip={i18n`Actual amount that was wired to the merchant's 
bank account`}
+                label={i18n.str`Amount credited`}
+                tooltip={i18n.str`Actual amount that was wired to the 
merchant's bank account`}
               />
             </FormProvider>
 
             <div class="buttons is-right mt-5">
               {onBack && (
                 <button class="button" onClick={onBack}>
-                  <Translate>Cancel</Translate>
+                  <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
               <AsyncButton
                 disabled={hasErrors}
                 data-tooltip={
                   hasErrors
-                    ? i18n`Need to complete marked fields`
+                    ? i18n.str`Need to complete marked fields`
                     : "confirm operation"
                 }
                 onClick={submitForm}
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </AsyncButton>
             </div>
           </div>
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 db01a57b6..8f41593dd 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
@@ -19,13 +19,13 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { useInstanceDetails } from "../../../../hooks/instance.js";
 import { useTransferAPI } from "../../../../hooks/transfer.js";
-import { useTranslator } from "../../../../i18n/index.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
 
@@ -38,7 +38,7 @@ interface Props {
 export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
   const { informTransfer } = useTransferAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   const instance = useInstanceDetails();
   const accounts = !instance.ok
     ? []
@@ -55,7 +55,7 @@ export default function CreateTransfer({ onConfirm, onBack }: 
Props): VNode {
             .then(() => onConfirm())
             .catch((error) => {
               setNotif({
-                message: i18n`could not inform transfer`,
+                message: i18n.str`could not inform transfer`,
                 type: "ERROR",
                 description: error.message,
               });
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 cad989980..c77f7737b 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
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { FormProvider } from "../../../../components/form/FormProvider.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 import { CardTable } from "./Table.js";
 
 export interface Props {
@@ -61,7 +61,7 @@ export function ListPage({
 }: Props): VNode {
   const form = { payto_uri: payTo };
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <section class="section is-main-section">
       <div class="columns">
@@ -73,10 +73,10 @@ export function ListPage({
           >
             <InputSelector
               name="payto_uri"
-              label={i18n`Address`}
+              label={i18n.str`Address`}
               values={accounts}
-              placeholder={i18n`Select one account`}
-              tooltip={i18n`filter by account address`}
+              placeholder={i18n.str`Select one account`}
+              tooltip={i18n.str`filter by account address`}
             />
           </FormProvider>
         </div>
@@ -87,30 +87,30 @@ export function ListPage({
           <li class={isAllTransfers ? "is-active" : ""}>
             <div
               class="has-tooltip-right"
-              data-tooltip={i18n`remove all filters`}
+              data-tooltip={i18n.str`remove all filters`}
             >
               <a onClick={onShowAll}>
-                <Translate>All</Translate>
+                <i18n.Translate>All</i18n.Translate>
               </a>
             </div>
           </li>
           <li class={isVerifiedTransfers ? "is-active" : ""}>
             <div
               class="has-tooltip-right"
-              data-tooltip={i18n`only show wire transfers confirmed by the 
merchant`}
+              data-tooltip={i18n.str`only show wire transfers confirmed by the 
merchant`}
             >
               <a onClick={onShowVerified}>
-                <Translate>Verified</Translate>
+                <i18n.Translate>Verified</i18n.Translate>
               </a>
             </div>
           </li>
           <li class={isNonVerifiedTransfers ? "is-active" : ""}>
             <div
               class="has-tooltip-right"
-              data-tooltip={i18n`only show wire transfers claimed by the 
exchange`}
+              data-tooltip={i18n.str`only show wire transfers claimed by the 
exchange`}
             >
               <a onClick={onShowUnverified}>
-                <Translate>Unverified</Translate>
+                <i18n.Translate>Unverified</i18n.Translate>
               </a>
             </div>
           </li>
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 2341fb80a..e38d375f9 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
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { StateUpdater, useState } from "preact/hooks";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
 
 type Entity = MerchantBackend.Transfers.TransferDetails & WithId;
 
@@ -49,7 +49,7 @@ export function CardTable({
 }: Props): VNode {
   const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   return (
     <div class="card has-table">
@@ -58,10 +58,13 @@ export function CardTable({
           <span class="icon">
             <i class="mdi mdi-bank" />
           </span>
-          <Translate>Transfers</Translate>
+          <i18n.Translate>Transfers</i18n.Translate>
         </p>
         <div class="card-header-icon" aria-label="more options">
-          <span class="has-tooltip-left" data-tooltip={i18n`add new transfer`}>
+          <span
+            class="has-tooltip-left"
+            data-tooltip={i18n.str`add new transfer`}
+          >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
                 <i class="mdi mdi-plus mdi-36px" />
@@ -117,42 +120,42 @@ function Table({
   hasMoreAfter,
   hasMoreBefore,
 }: TableProps): VNode {
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n`load more transfers before the first one`}
+          data-tooltip={i18n.str`load more transfers before the first one`}
           disabled={!hasMoreBefore}
           onClick={onLoadMoreBefore}
         >
-          <Translate>load newer transfers</Translate>
+          <i18n.Translate>load newer transfers</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
         <thead>
           <tr>
             <th>
-              <Translate>ID</Translate>
+              <i18n.Translate>ID</i18n.Translate>
             </th>
             <th>
-              <Translate>Credit</Translate>
+              <i18n.Translate>Credit</i18n.Translate>
             </th>
             <th>
-              <Translate>Address</Translate>
+              <i18n.Translate>Address</i18n.Translate>
             </th>
             <th>
-              <Translate>Exchange URL</Translate>
+              <i18n.Translate>Exchange URL</i18n.Translate>
             </th>
             <th>
-              <Translate>Confirmed</Translate>
+              <i18n.Translate>Confirmed</i18n.Translate>
             </th>
             <th>
-              <Translate>Verified</Translate>
+              <i18n.Translate>Verified</i18n.Translate>
             </th>
             <th>
-              <Translate>Executed at</Translate>
+              <i18n.Translate>Executed at</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -165,23 +168,23 @@ function Table({
                 <td>{i.credit_amount}</td>
                 <td>{i.payto_uri}</td>
                 <td>{i.exchange_url}</td>
-                <td>{i.confirmed ? i18n`yes` : i18n`no`}</td>
-                <td>{i.verified ? i18n`yes` : i18n`no`}</td>
+                <td>{i.confirmed ? i18n.str`yes` : i18n.str`no`}</td>
+                <td>{i.verified ? i18n.str`yes` : i18n.str`no`}</td>
                 <td>
                   {i.execution_time
                     ? i.execution_time.t_s == "never"
-                      ? i18n`never`
+                      ? i18n.str`never`
                       : format(
                           i.execution_time.t_s * 1000,
                           "yyyy/MM/dd HH:mm:ss",
                         )
-                    : i18n`unknown`}
+                    : i18n.str`unknown`}
                 </td>
                 <td>
                   {i.verified === undefined ? (
                     <button
                       class="button is-danger is-small has-tooltip-left"
-                      data-tooltip={i18n`delete selected transfer from the 
database`}
+                      data-tooltip={i18n.str`delete selected transfer from the 
database`}
                       onClick={() => onDelete(i)}
                     >
                       Delete
@@ -196,11 +199,11 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n`load more transfer after the last one`}
+          data-tooltip={i18n.str`load more transfer after the last one`}
           disabled={!hasMoreAfter}
           onClick={onLoadMoreAfter}
         >
-          <Translate>load older transfers</Translate>
+          <i18n.Translate>load older transfers</i18n.Translate>
         </button>
       )}
     </div>
@@ -208,6 +211,7 @@ function Table({
 }
 
 function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <div class="content has-text-grey has-text-centered">
       <p>
@@ -216,9 +220,9 @@ function EmptyTable(): VNode {
         </span>
       </p>
       <p>
-        <Translate>
+        <i18n.Translate>
           There is no transfer yet, add more pressing the + sign
-        </Translate>
+        </i18n.Translate>
       </p>
     </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 d7acdf023..1365a59da 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
@@ -19,21 +19,20 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import * as yup from "yup";
 import { AsyncButton } from "../../../components/exception/AsyncButton.js";
 import {
-  FormProvider,
   FormErrors,
+  FormProvider,
 } from "../../../components/form/FormProvider.js";
+import { DefaultInstanceFormFields } from 
"../../../components/instance/DefaultInstanceFormFields.js";
 import { UpdateTokenModal } from "../../../components/modal/index.js";
 import { useInstanceContext } from "../../../context/instance.js";
 import { MerchantBackend } from "../../../declaration.js";
-import { Translate, useTranslator } from "../../../i18n/index.js";
-import { DefaultInstanceFormFields } from 
"../../../components/instance/DefaultInstanceFormFields.js";
 import { PAYTO_REGEX } from "../../../utils/constants.js";
-import { Amounts } from "@gnu-taler/taler-util";
 import { undefinedIfEmpty } from "../../../utils/table.js";
 
 type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & {
@@ -95,50 +94,52 @@ export function UpdatePage({
 
   const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
 
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   const errors: FormErrors<Entity> = {
-    name: !value.name ? i18n`required` : undefined,
+    name: !value.name ? i18n.str`required` : undefined,
     payto_uris:
       !value.payto_uris || !value.payto_uris.length
-        ? i18n`required`
+        ? i18n.str`required`
         : undefinedIfEmpty(
             value.payto_uris.map((p) => {
-              return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined;
+              return !PAYTO_REGEX.test(p) ? i18n.str`is not valid` : undefined;
             }),
           ),
     default_max_deposit_fee: !value.default_max_deposit_fee
-      ? i18n`required`
+      ? i18n.str`required`
       : !Amounts.parse(value.default_max_deposit_fee)
-      ? i18n`invalid format`
+      ? i18n.str`invalid format`
       : undefined,
     default_max_wire_fee: !value.default_max_wire_fee
-      ? i18n`required`
+      ? i18n.str`required`
       : !Amounts.parse(value.default_max_wire_fee)
-      ? i18n`invalid format`
+      ? i18n.str`invalid format`
       : undefined,
     default_wire_fee_amortization:
       value.default_wire_fee_amortization === undefined
-        ? i18n`required`
+        ? i18n.str`required`
         : isNaN(value.default_wire_fee_amortization)
-        ? i18n`is not a number`
+        ? i18n.str`is not a number`
         : value.default_wire_fee_amortization < 1
-        ? i18n`must be 1 or greater`
+        ? i18n.str`must be 1 or greater`
         : undefined,
-    default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined,
+    default_pay_delay: !value.default_pay_delay
+      ? i18n.str`required`
+      : undefined,
     default_wire_transfer_delay: !value.default_wire_transfer_delay
-      ? i18n`required`
+      ? i18n.str`required`
       : undefined,
     address: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n`max 7 lines`
+          ? i18n.str`max 7 lines`
           : undefined,
     }),
     jurisdiction: undefinedIfEmpty({
       address_lines:
         value.address?.address_lines && value.address?.address_lines.length > 7
-          ? i18n`max 7 lines`
+          ? i18n.str`max 7 lines`
           : undefined,
     }),
   };
@@ -160,7 +161,7 @@ export function UpdatePage({
               <div class="level-left">
                 <div class="level-item">
                   <span class="is-size-4">
-                    <Translate>Instance id</Translate>: <b>{id}</b>
+                    <i18n.Translate>Instance id</i18n.Translate>: <b>{id}</b>
                   </span>
                 </div>
               </div>
@@ -169,7 +170,7 @@ export function UpdatePage({
                   <h1 class="title">
                     <button
                       class="button is-danger"
-                      data-tooltip={i18n`Change the authorization method use 
for this instance.`}
+                      data-tooltip={i18n.str`Change the authorization method 
use for this instance.`}
                       onClick={(): void => {
                         setActive(!active);
                       }}
@@ -178,7 +179,7 @@ export function UpdatePage({
                         <i class="mdi mdi-lock-reset" />
                       </div>
                       <span>
-                        <Translate>Manage access token</Translate>
+                        <i18n.Translate>Manage access token</i18n.Translate>
                       </span>
                     </button>
                   </h1>
@@ -229,19 +230,19 @@ export function UpdatePage({
                 onClick={onBack}
                 data-tooltip="cancel operation"
               >
-                <Translate>Cancel</Translate>
+                <i18n.Translate>Cancel</i18n.Translate>
               </button>
 
               <AsyncButton
                 onClick={submit}
                 data-tooltip={
                   hasErrors
-                    ? i18n`Need to complete marked fields`
+                    ? i18n.str`Need to complete marked fields`
                     : "confirm operation"
                 }
                 disabled={hasErrors}
               >
-                <Translate>Confirm</Translate>
+                <i18n.Translate>Confirm</i18n.Translate>
               </AsyncButton>
             </div>
           </div>
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 480274e66..668fe9a8d 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
@@ -13,6 +13,7 @@
  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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../components/exception/loading.js";
@@ -26,7 +27,6 @@ import {
   useManagedInstanceDetails,
   useManagementAPI,
 } from "../../../hooks/instance.js";
-import { useTranslator } from "../../../i18n/index.js";
 import { Notification } from "../../../utils/types.js";
 import { UpdatePage } from "./UpdatePage.js";
 
@@ -70,7 +70,7 @@ function CommonUpdate(
 ): VNode {
   const { changeToken } = useInstanceContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const i18n = useTranslator();
+  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
@@ -91,7 +91,7 @@ function CommonUpdate(
             .then(onConfirm)
             .catch((error: Error) =>
               setNotif({
-                message: i18n`Failed to create instance`,
+                message: i18n.str`Failed to create instance`,
                 type: "ERROR",
                 description: error.message,
               }),
diff --git a/packages/merchant-backoffice-ui/src/stories.test.ts 
b/packages/merchant-backoffice-ui/src/stories.test.ts
index 6a401dd25..e85be31b7 100644
--- a/packages/merchant-backoffice-ui/src/stories.test.ts
+++ b/packages/merchant-backoffice-ui/src/stories.test.ts
@@ -20,15 +20,13 @@
  */
 import { setupI18n } from "@gnu-taler/taler-util";
 import { parseGroupImport } from "@gnu-taler/web-util/lib/index.browser";
-
+import { h as create } from "preact";
+import { render as renderToString } from "preact-render-to-string";
 import * as admin from "./paths/admin/index.stories.js";
 import * as instance from "./paths/instance/index.stories.js";
 
 setupI18n("en", { en: {} });
 
-import { h as create } from "preact"
-import { render as renderToString } from "preact-render-to-string";
-
 describe("All the examples:", () => {
   const cms = parseGroupImport({ admin, instance });
   cms.forEach((group) => {
@@ -37,8 +35,11 @@ describe("All the examples:", () => {
         describe(`Component: ${component.name}`, () => {
           component.examples.forEach((example) => {
             it(`should render example: ${example.name}`, () => {
-              const vdom = create(example.render.component, 
example.render.props)
-              const html = renderToString(vdom)
+              const vdom = create(
+                example.render.component,
+                example.render.props,
+              );
+              const html = renderToString(vdom);
               // console.log(html)
             });
           });
diff --git a/packages/merchant-backoffice-ui/tests/functions/regex.test.ts 
b/packages/merchant-backoffice-ui/tests/functions/regex.test.ts
index 12a318c3d..d866a13a0 100644
--- a/packages/merchant-backoffice-ui/tests/functions/regex.test.ts
+++ b/packages/merchant-backoffice-ui/tests/functions/regex.test.ts
@@ -14,10 +14,10 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
- /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
 
 import { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants.js";
 
@@ -29,11 +29,11 @@ describe('payto uri format', () => {
     'payto://void/?amount=EUR:10.5',
     'payto://ilp/g.acme.bob'
   ]
-  
-  test('should be valid', () => {
+
+  it('should be valid', () => {
     valids.forEach(v => expect(v).toMatch(PAYTO_REGEX))
   });
-  
+
   const invalids = [
     // has two question marks
     'payto://iban/DE75?512108001245126199?amount=EUR:200.0&message=hello',
@@ -46,10 +46,10 @@ describe('payto uri format', () => {
     // payto:// is incomplete
     'payto: //ilp/g.acme.bob'
   ]
-  
-  test('should not be valid', () => {
+
+  it('should not be valid', () => {
     invalids.forEach(v => expect(v).not.toMatch(PAYTO_REGEX))
-  });  
+  });
 })
 
 describe('amount format', () => {
@@ -62,11 +62,11 @@ describe('amount format', () => {
     'ARSCOL:10',
     'THISISTHEMOTHERCOIN:1,000,000.123,123',
   ]
-  
-  test('should be valid', () => {
+
+  it('should be valid', () => {
     valids.forEach(v => expect(v).toMatch(AMOUNT_REGEX))
   });
-  
+
   const invalids = [
     //no currency name
     ':10',
@@ -79,9 +79,9 @@ describe('amount format', () => {
     //missing value
     'USD:',
   ]
-  
-  test('should not be valid', () => {
+
+  it('should not be valid', () => {
     invalids.forEach(v => expect(v).not.toMatch(AMOUNT_REGEX))
-  });  
+  });
 
 })
\ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/tests/header.test.tsx 
b/packages/merchant-backoffice-ui/tests/header.test.tsx
index c917366e7..1cf2b7e6c 100644
--- a/packages/merchant-backoffice-ui/tests/header.test.tsx
+++ b/packages/merchant-backoffice-ui/tests/header.test.tsx
@@ -23,23 +23,23 @@ import { h } from "preact";
 import { ProductList } from "../src/components/product/ProductList.js";
 // See: https://github.com/preactjs/enzyme-adapter-preact-pure
 // import { shallow } from 'enzyme';
+import { render } from "@testing-library/preact";
 import * as backend from "../src/context/config.js";
-import { render, findAllByText } from "@testing-library/preact";
-import * as i18n from "../src/context/translation.js";
+// import * as i18n from "../src/context/translation.js";
 
-import * as jedLib from "jed";
-const handler = new jedLib.Jed("en");
+// import * as jedLib from "jed";
+// const handler = new jedLib.Jed("en");
 
 describe("Initial Test of the Sidebar", () => {
   beforeEach(() => {
     jest
       .spyOn(backend, "useConfigContext")
       .mockImplementation(() => ({ version: "", currency: "" }));
-    jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({
-      changeLanguage: () => null,
-      handler,
-      lang: "en",
-    }));
+    // jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({
+    //   changeLanguage: () => null,
+    //   handler,
+    //   lang: "en",
+    // }));
   });
   test("Product list renders a table", () => {
     const context = render(
diff --git a/packages/web-util/src/tests/hook.ts 
b/packages/web-util/src/tests/hook.ts
index 0ec47f252..ddc96eb0e 100644
--- a/packages/web-util/src/tests/hook.ts
+++ b/packages/web-util/src/tests/hook.ts
@@ -120,6 +120,7 @@ interface Mounted<T> {
  * 
  * @returns testing API
  */
+// eslint-disable-next-line @typescript-eslint/ban-types
 export function mountHook<T extends object>(
   hookToBeTested: () => RecursiveState<T>,
   Context?: ({ children }: { children: any }) => VNode | null,
@@ -242,6 +243,7 @@ interface HookTestResultError {
  * 
  * @returns testing result, should also be checked to be "ok"
  */
+// eslint-disable-next-line @typescript-eslint/ban-types
 export async function hookBehaveLikeThis<T extends object, PropsType>(
   hookFunction: (p: PropsType) => RecursiveState<T>,
   props: PropsType,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cec9f0f64..9460653d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -374,7 +374,7 @@ importers:
       jest: ^26.6.3
       jest-preset-preact: ^4.0.2
       mocha: ^9.2.0
-      preact: 10.6.5
+      preact: 10.11.3
       preact-render-to-string: ^5.2.6
       preact-router: 3.2.1
       qrcode-generator: 1.4.4
@@ -391,16 +391,16 @@ importers:
       date-fns: 2.29.3
       history: 4.10.1
       jed: 1.1.1
-      preact: 10.6.5
-      preact-router: 3.2.1_preact@10.6.5
+      preact: 10.11.3
+      preact-router: 3.2.1_preact@10.11.3
       qrcode-generator: 1.4.4
       swr: 1.3.0
       yup: 0.32.11
     devDependencies:
       '@creativebulma/bulma-tooltip': 1.2.0
       '@gnu-taler/pogen': link:../pogen
-      '@testing-library/preact': 2.0.1_preact@10.6.5
-      '@testing-library/preact-hooks': 1.1.0_vfcmu6iy7nffpurikpgxo6gwxi
+      '@testing-library/preact': 2.0.1_preact@10.11.3
+      '@testing-library/preact-hooks': 1.1.0_eng4adldpgibddgycwaukopxga
       '@types/history': 4.7.11
       '@types/jest': 26.0.24
       '@types/mocha': 8.2.3
@@ -424,9 +424,9 @@ importers:
       html-webpack-skip-assets-plugin: 1.0.3
       inline-chunk-html-plugin: 1.1.1
       jest: 26.6.3
-      jest-preset-preact: 4.0.5_moqeqtbsr7edkxzj3jgnhqkxsm
+      jest-preset-preact: 4.0.5_w5bq6jgm3cbfmbu2zwqko4iate
       mocha: 9.2.2
-      preact-render-to-string: 5.2.6_preact@10.6.5
+      preact-render-to-string: 5.2.6_preact@10.11.3
       rimraf: 3.0.2
       sass: 1.56.1
       typedoc: 0.20.37_typescript@4.8.4
@@ -5795,14 +5795,14 @@ packages:
       preact: 10.11.2
     dev: true
 
-  /@testing-library/preact-hooks/1.1.0_vfcmu6iy7nffpurikpgxo6gwxi:
+  /@testing-library/preact-hooks/1.1.0_eng4adldpgibddgycwaukopxga:
     resolution: {integrity: 
sha512-+JIor+NsOHkK3oIrwMDGKGHXTN0JJi462dBJlj4FNbGaDPTlctE6eu2ranWQirh7/FJMkWfzQCP+tk7jmY8ZrQ==}
     peerDependencies:
       '@testing-library/preact': ^2.0.0
       preact: ^10.4.8
     dependencies:
-      '@testing-library/preact': 2.0.1_preact@10.6.5
-      preact: 10.6.5
+      '@testing-library/preact': 2.0.1_preact@10.11.3
+      preact: 10.11.3
     dev: true
 
   /@testing-library/preact/2.0.1_preact@10.11.2:
@@ -5815,14 +5815,14 @@ packages:
       preact: 10.11.2
     dev: true
 
-  /@testing-library/preact/2.0.1_preact@10.6.5:
+  /@testing-library/preact/2.0.1_preact@10.11.3:
     resolution: {integrity: 
sha512-79kwVOY+3caoLgaPbiPzikjgY0Aya7Fc7TvGtR1upCnz2wrtmPDnN2t9vO7I7vDP2zoA+feSwOH5Q0BFErhaaQ==}
     engines: {node: '>= 10'}
     peerDependencies:
       preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0'
     dependencies:
       '@testing-library/dom': 7.31.2
-      preact: 10.6.5
+      preact: 10.11.3
     dev: true
 
   /@tootallnate/once/1.1.2:
@@ -14567,7 +14567,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-preset-preact/4.0.5_moqeqtbsr7edkxzj3jgnhqkxsm:
+  /jest-preset-preact/4.0.5_w5bq6jgm3cbfmbu2zwqko4iate:
     resolution: {integrity: 
sha512-MnU7mfpnwopJkdx0WoEyRmrNDIvRN+w6sOur0zEhaRYYMo0gJM7UdZHWTV8k6uo0+ypY+m0kQW6kMukUx4v8JQ==}
     peerDependencies:
       jest: 26.x || 27.x
@@ -14584,8 +14584,8 @@ packages:
       isomorphic-unfetch: 3.1.0
       jest: 26.6.3
       jest-watch-typeahead: 0.6.5_jest@26.6.3
-      preact: 10.6.5
-      preact-render-to-string: 5.2.6_preact@10.6.5
+      preact: 10.11.3
+      preact-render-to-string: 5.2.6_preact@10.11.3
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -17955,15 +17955,6 @@ packages:
       preact: 10.11.3
       pretty-format: 3.8.0
 
-  /preact-render-to-string/5.2.6_preact@10.6.5:
-    resolution: {integrity: 
sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
-    peerDependencies:
-      preact: '>=10'
-    dependencies:
-      preact: 10.6.5
-      pretty-format: 3.8.0
-    dev: true
-
   /preact-router/3.2.1_preact@10.11.2:
     resolution: {integrity: 
sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==}
     peerDependencies:
@@ -17980,23 +17971,12 @@ packages:
       preact: 10.11.3
     dev: false
 
-  /preact-router/3.2.1_preact@10.6.5:
-    resolution: {integrity: 
sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==}
-    peerDependencies:
-      preact: '>=10'
-    dependencies:
-      preact: 10.6.5
-    dev: false
-
   /preact/10.11.2:
     resolution: {integrity: 
sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw==}
 
   /preact/10.11.3:
     resolution: {integrity: 
sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
 
-  /preact/10.6.5:
-    resolution: {integrity: 
sha512-i+LXM6JiVjQXSt2jG2vZZFapGpCuk1fl8o6ii3G84MA3xgj686FKjs4JFDkmUVhtxyq21+4ay74zqPykz9hU6w==}
-
   /prelude-ls/1.1.2:
     resolution: {integrity: 
sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
     engines: {node: '>= 0.8.0'}

-- 
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]