diff options
author | Sebastian <sebasjm@gmail.com> | 2022-12-20 17:45:24 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2022-12-20 17:45:24 -0300 |
commit | c59f9a2556731ad95ab8bd7eefe7fa8a41629834 (patch) | |
tree | 5cb60195d66cebbee0ba99e05eafe22f369a46a8 /packages/merchant-backoffice-ui/src/components | |
parent | 382e66b179d6fda2598936196b2ae1b97bfab8ef (diff) | |
download | wallet-core-c59f9a2556731ad95ab8bd7eefe7fa8a41629834.tar.xz |
use translation context from web-utils, don't use match react-router since is broken
Diffstat (limited to 'packages/merchant-backoffice-ui/src/components')
23 files changed, 381 insertions, 355 deletions
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. + "USD:2.3". + </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>"{instance}"</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>"{element.name}"</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>"{element.name}"</b> (ID:{" "} + <b>{element.id}</b>), you will also delete all it'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'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> |