aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-07-26 11:27:56 -0300
committerSebastian <sebasjm@gmail.com>2021-07-26 11:33:49 -0300
commit44551245dab36570d937affdb89735c937b4ae55 (patch)
treefca0e201ce8157122af15abb9aebd1061a773b92 /packages
parente70e664da982baf1b74e6210adaa870c298281d4 (diff)
i18n
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-util/src/i18n.ts6
-rw-r--r--packages/taler-wallet-webextension/.storybook/preview.js20
-rwxr-xr-xpackages/taler-wallet-webextension/clean_and_build.sh1
-rw-r--r--packages/taler-wallet-webextension/src/components/SelectList.tsx40
-rw-r--r--packages/taler-wallet-webextension/src/context/devContext.ts (renamed from packages/taler-wallet-webextension/src/context/useDevContext.ts)0
-rw-r--r--packages/taler-wallet-webextension/src/context/translation.ts68
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useLang.ts7
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts21
-rw-r--r--packages/taler-wallet-webextension/src/i18n/strings.ts70
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx34
-rw-r--r--packages/taler-wallet-webextension/src/popup/Settings.tsx42
-rw-r--r--packages/taler-wallet-webextension/src/popup/popup.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popupEntryPoint.tsx2
13 files changed, 272 insertions, 41 deletions
diff --git a/packages/taler-util/src/i18n.ts b/packages/taler-util/src/i18n.ts
index 0324d8e07..e452ffa9f 100644
--- a/packages/taler-util/src/i18n.ts
+++ b/packages/taler-util/src/i18n.ts
@@ -45,7 +45,7 @@ function toI18nString(stringSeq: ReadonlyArray<string>): string {
/**
* Internationalize a string template with arbitrary serialized values.
*/
-export function str(stringSeq: TemplateStringsArray, ...values: any[]): string {
+export function singular(stringSeq: TemplateStringsArray, ...values: any[]): string {
const s = toI18nString(stringSeq);
const tr = jed
.translate(s)
@@ -141,7 +141,9 @@ function stringifyArray(children: Array<any>): string {
}
export const i18n = {
- str,
+ str: singular,
+ singular,
Translate,
translate,
};
+
diff --git a/packages/taler-wallet-webextension/.storybook/preview.js b/packages/taler-wallet-webextension/.storybook/preview.js
index af768dde8..169b726f9 100644
--- a/packages/taler-wallet-webextension/.storybook/preview.js
+++ b/packages/taler-wallet-webextension/.storybook/preview.js
@@ -14,15 +14,9 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { setupI18n } from "@gnu-taler/taler-util"
import { Fragment } from "preact"
-import { strings } from '../src/i18n/strings.ts'
import { NavBar } from '../src/popup/popup'
-
-const mockConfig = {
- backendURL: 'http://demo.taler.net',
- currency: 'KUDOS'
-}
+import { TranslationProvider } from '../src/context/translation'
export const parameters = {
controls: { expanded: true },
@@ -38,7 +32,7 @@ export const globalTypes = {
icon: 'globe',
items: [
{ value: 'en', right: '🇺🇸', title: 'English' },
- { value: 'es', right: '🇪🇸', title: 'Spanish' },
+ { value: 'de', right: '🇪🇸', title: 'German' },
],
},
},
@@ -58,7 +52,7 @@ export const decorators = [
<Story />
</div>
} else {
- const path = !isTestingHeader ? /popup(\/.*)\/.*/.exec(kind)[1] : ''
+ const path = !isTestingHeader ? /popup(\/.*).*/.exec(kind)[1] : ''
// add a fake header so it looks similar
return <Fragment>
<NavBar path={path} devMode={path === '/dev'} />
@@ -113,9 +107,7 @@ export const decorators = [
<Story />
</div>
},
- (Story, { globals }) => {
- setupI18n(globals.locale, strings);
- return <Story />
- },
- // (Story) => <ConfigContextProvider value={mockConfig}> <Story /> </ConfigContextProvider>
+ (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}>
+ <Story />
+ </TranslationProvider>,
];
diff --git a/packages/taler-wallet-webextension/clean_and_build.sh b/packages/taler-wallet-webextension/clean_and_build.sh
index e862be37e..fb8b31c7e 100755
--- a/packages/taler-wallet-webextension/clean_and_build.sh
+++ b/packages/taler-wallet-webextension/clean_and_build.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# This file is in the public domain.
[ "also-wallet" == "$1" ] && { pnpm -C ../taler-wallet-core/ compile || exit 1; }
+[ "also-util" == "$1" ] && { pnpm -C ../taler-util/ prepare || exit 1; }
pnpm clean && pnpm compile && rm -rf extension/ && ./pack.sh && (cd extension/ && unzip taler*.zip)
diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx b/packages/taler-wallet-webextension/src/components/SelectList.tsx
new file mode 100644
index 000000000..2c4a106ee
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019 Taler Systems SA
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { VNode } from "preact";
+import { useRef, useState } from "preact/hooks";
+import { JSX } from "preact/jsx-runtime";
+
+interface Props {
+ value: string;
+ onChange: (s: string) => void;
+ label: string;
+ list: {
+ [label: string]: string
+ }
+ name: string;
+ description?: string;
+}
+
+export function SelectList({ name, value, list, onChange, label, description }: Props): JSX.Element {
+ return <select name={name} id="slct">
+ <option selected disabled>Choose an option</option>
+ {Object.keys(list)
+ .filter((l) => l !== value)
+ .map(key => <option value={key} key={key}>{list[key]}</option> )
+ }
+ </select>
+}
diff --git a/packages/taler-wallet-webextension/src/context/useDevContext.ts b/packages/taler-wallet-webextension/src/context/devContext.ts
index ea2ba4ceb..ea2ba4ceb 100644
--- a/packages/taler-wallet-webextension/src/context/useDevContext.ts
+++ b/packages/taler-wallet-webextension/src/context/devContext.ts
diff --git a/packages/taler-wallet-webextension/src/context/translation.ts b/packages/taler-wallet-webextension/src/context/translation.ts
new file mode 100644
index 000000000..5f57958de
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/context/translation.ts
@@ -0,0 +1,68 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/useLang'
+//@ts-ignore: type declaration
+import * as jedLib from "jed";
+import { strings } from "../i18n/strings";
+import { setupI18n } from '@gnu-taler/taler-util';
+
+interface Type {
+ lang: string;
+ changeLanguage: (l: string) => void;
+}
+const initial = {
+ lang: 'en',
+ changeLanguage: () => {
+ // do not change anything
+ }
+}
+const Context = createContext<Type>(initial)
+
+interface Props {
+ initial?: string,
+ children: any,
+ forceLang?: string
+}
+
+//we use forceLang when we don't want to use the saved state, but sone forced
+//runtime lang predefined lang
+export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => {
+ const [lang, changeLanguage] = useLang(initial)
+ useEffect(() => {
+ if (forceLang) {
+ changeLanguage(forceLang)
+ }
+ })
+ useEffect(()=> {
+ setupI18n(lang, strings)
+ },[lang])
+ if (forceLang) {
+ setupI18n(forceLang, strings)
+ } else {
+ setupI18n(lang, strings)
+ }
+ return h(Context.Provider, { value: { lang, changeLanguage }, children });
+}
+
+export const useTranslationContext = (): Type => useContext(Context);
diff --git a/packages/taler-wallet-webextension/src/hooks/useLang.ts b/packages/taler-wallet-webextension/src/hooks/useLang.ts
new file mode 100644
index 000000000..d9ad7cd55
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/hooks/useLang.ts
@@ -0,0 +1,7 @@
+import { useNotNullLocalStorage } from './useLocalStorage';
+
+export function useLang(initial?: string): [string, (s:string) => void] {
+ const browserLang: string | undefined = typeof window !== "undefined" ? navigator.language || (navigator as any).userLanguage : undefined;
+ const defaultLang = (browserLang || initial || 'en').substring(0, 2)
+ return useNotNullLocalStorage('lang-preference', defaultLang)
+}
diff --git a/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts b/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts
index 30f681940..78a8b65d5 100644
--- a/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts
@@ -42,3 +42,24 @@ export function useLocalStorage(key: string, initialValue?: string): [string | u
return [storedValue, setValue];
}
+
+//TODO: merge with the above function
+export function useNotNullLocalStorage(key: string, initialValue: string): [string, StateUpdater<string>] {
+ const [storedValue, setStoredValue] = useState<string>((): string => {
+ return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue;
+ });
+
+ const setValue = (value: string | ((val: string) => string)) => {
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
+ setStoredValue(valueToStore);
+ if (typeof window !== "undefined") {
+ if (!valueToStore) {
+ window.localStorage.removeItem(key)
+ } else {
+ window.localStorage.setItem(key, valueToStore);
+ }
+ }
+ };
+
+ return [storedValue, setValue];
+}
diff --git a/packages/taler-wallet-webextension/src/i18n/strings.ts b/packages/taler-wallet-webextension/src/i18n/strings.ts
index 748b9656a..5b1257830 100644
--- a/packages/taler-wallet-webextension/src/i18n/strings.ts
+++ b/packages/taler-wallet-webextension/src/i18n/strings.ts
@@ -159,6 +159,76 @@ strings["en-US"] = {
},
};
+strings["es"] = {
+ domain: "messages",
+ locale_data: {
+ messages: {
+ "": {
+ domain: "messages",
+ plural_forms: "nplurals=2; plural=(n != 1);",
+ lang: "",
+ },
+ "Invalid Wire": [""],
+ "Invalid Test Wire Detail": [""],
+ "Test Wire Acct #%1$s on %2$s": [""],
+ "Unknown Wire Detail": [""],
+ Operation: [""],
+ "time (ms/op)": [""],
+ "The merchant %1$s offers you to purchase:": [""],
+ "The total price is %1$s (plus %2$s fees).": [""],
+ "The total price is %1$s.": [""],
+ Retry: [""],
+ "Confirm payment": [""],
+ Balance: [""],
+ History: ["Historial"],
+ Debug: [""],
+ "You have no balance to show. Need some %1$s getting started?": [""],
+ "%1$s incoming": [""],
+ "%1$s being spent": [""],
+ "Error: could not retrieve balance information.": [""],
+ "Invalid ": [""],
+ "Fees ": [""],
+ "Refresh sessions has completed": [""],
+ "Order Refused": [""],
+ "Order redirected": [""],
+ "Payment aborted": [""],
+ "Payment Sent": [""],
+ "Backup": ["Resguardo"],
+ "Order accepted": [""],
+ "Reserve balance updated": [""],
+ "Payment refund": [""],
+ Withdrawn: [""],
+ "Tip Accepted": [""],
+ "Tip Declined": [""],
+ "%1$s": [""],
+ "Your wallet has no events recorded.": [""],
+ "Wire to bank account": [""],
+ Confirm: ["Confirmar"],
+ Cancel: ["Cancelar"],
+ "Could not get details for withdraw operation:": [""],
+ "Chose different exchange provider": [""],
+ "Please select an exchange. You can review the details before after your selection.": [
+ "",
+ ],
+ "Select %1$s": [""],
+ "Select custom exchange": [""],
+ "You are about to withdraw %1$s from your bank account into your wallet.": [
+ "",
+ ],
+ "Accept fees and withdraw": [""],
+ "Cancel withdraw operation": [""],
+ "Withdrawal fees:": [""],
+ "Rounding loss:": [""],
+ "Earliest expiration (for deposit): %1$s": [""],
+ "# Coins": [""],
+ Value: [""],
+ "Withdraw Fee": [""],
+ "Refresh Fee": [""],
+ "Deposit Fee": [""],
+ },
+ },
+};
+
strings["fr"] = {
domain: "messages",
locale_data: {
diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx
index 0b76d7560..30512d227 100644
--- a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx
@@ -31,7 +31,7 @@ interface Props {
export function ProviderDetailPage({ pid, onBack }: Props): VNode {
const status = useProviderStatus(pid)
if (!status) {
- return <div>Loading...</div>
+ return <div><i18n.Translate>Loading...</i18n.Translate></div>
}
if (!status.info) {
onBack()
@@ -67,26 +67,26 @@ export function ProviderView({ info, onDelete, onSync, onBack, onExtend }: ViewP
<p>{daysSince(info?.lastSuccessfulBackupTimestamp)} </p>
<p>{descriptionByStatus(info.paymentStatus)}</p>
{info.paymentStatus.type === ProviderPaymentType.TermsChanged && <div>
- <p>terms has changed, extending the service will imply accepting the new terms of service</p>
+ <p><i18n.Translate>terms has changed, extending the service will imply accepting the new terms of service</i18n.Translate></p>
<table>
<thead>
<tr>
<td></td>
- <td>old</td>
+ <td><i18n.Translate>old</i18n.Translate></td>
<td> -&gt;</td>
- <td>new</td>
+ <td><i18n.Translate>new</i18n.Translate></td>
</tr>
</thead>
<tbody>
<tr>
- <td>fee</td>
+ <td><i18n.Translate>fee</i18n.Translate></td>
<td>{info.paymentStatus.oldTerms.annualFee}</td>
<td>-&gt;</td>
<td>{info.paymentStatus.newTerms.annualFee}</td>
</tr>
<tr>
- <td>storage</td>
+ <td><i18n.Translate>storage</i18n.Translate></td>
<td>{info.paymentStatus.oldTerms.storageLimitInMegabytes}</td>
<td>-&gt;</td>
<td>{info.paymentStatus.newTerms.storageLimitInMegabytes}</td>
@@ -117,11 +117,11 @@ function daysSince(d?: Timestamp) {
const str = formatDuration(duration, {
delimiter: ', ',
format: [
- duration?.years ? 'years' : (
- duration?.months ? 'months' : (
- duration?.days ? 'days' : (
- duration?.hours ? 'hours' : (
- duration?.minutes ? 'minutes' : 'seconds'
+ duration?.years ? i18n.str`years` : (
+ duration?.months ? i18n.str`months` : (
+ duration?.days ? i18n.str`days` : (
+ duration?.hours ? i18n.str`hours` : (
+ duration?.minutes ? i18n.str`minutes` : i18n.str`seconds`
)
)
)
@@ -139,13 +139,13 @@ function Error({ info }: { info: ProviderInfo }) {
switch (info.backupProblem.type) {
case "backup-conflicting-device":
return <ErrorMessage title={<Fragment>
- There is conflict with another backup from <b>{info.backupProblem.otherDeviceId}</b>
+ <i18n.Translate>There is conflict with another backup from <b>{info.backupProblem.otherDeviceId}</b></i18n.Translate>
</Fragment>} />
case "backup-unreadable":
return <ErrorMessage title="Backup is not readable" />
default:
return <ErrorMessage title={<Fragment>
- Unknown backup problem: {JSON.stringify(info.backupProblem)}
+ <i18n.Translate>Unknown backup problem: {JSON.stringify(info.backupProblem)}</i18n.Translate>
</Fragment>} />
}
}
@@ -172,15 +172,15 @@ function colorByStatus(status: ProviderPaymentType) {
function descriptionByStatus(status: ProviderPaymentStatus) {
switch (status.type) {
case ProviderPaymentType.InsufficientBalance:
- return 'no enough balance to make the payment'
+ return i18n.str`no enough balance to make the payment`
case ProviderPaymentType.Unpaid:
- return 'not paid yet'
+ return i18n.str`not paid yet`
case ProviderPaymentType.Paid:
case ProviderPaymentType.TermsChanged:
if (status.paidUntil.t_ms === 'never') {
- return 'service paid.'
+ return i18n.str`service paid`
} else {
- return `service paid until ${format(status.paidUntil.t_ms, 'yyyy/MM/dd HH:mm:ss')}`
+ return i18n.str`service paid until ${format(status.paidUntil.t_ms, 'yyyy/MM/dd HH:mm:ss')}`
}
case ProviderPaymentType.Pending:
return ''
diff --git a/packages/taler-wallet-webextension/src/popup/Settings.tsx b/packages/taler-wallet-webextension/src/popup/Settings.tsx
index d8cd04380..9bb10a4e3 100644
--- a/packages/taler-wallet-webextension/src/popup/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Settings.tsx
@@ -15,18 +15,23 @@
*/
+import { i18n } from "@gnu-taler/taler-util";
import { VNode } from "preact";
import { Checkbox } from "../components/Checkbox";
import { EditableText } from "../components/EditableText";
-import { useDevContext } from "../context/useDevContext";
+import { SelectList } from "../components/SelectList";
+import { useDevContext } from "../context/devContext";
import { useBackupDeviceName } from "../hooks/useBackupDeviceName";
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
+import { useLang } from "../hooks/useLang";
export function SettingsPage(): VNode {
const [permissionsEnabled, togglePermissions] = useExtendedPermissions();
const { devMode, toggleDevMode } = useDevContext()
const { name, update } = useBackupDeviceName()
+ const [lang, changeLang] = useLang()
return <SettingsView
+ lang={lang} changeLang={changeLang}
deviceName={name} setDeviceName={update}
permissionsEnabled={permissionsEnabled} togglePermissions={togglePermissions}
developerMode={devMode} toggleDeveloperMode={toggleDevMode}
@@ -34,6 +39,8 @@ export function SettingsPage(): VNode {
}
export interface ViewProps {
+ lang: string;
+ changeLang: (s: string) => void;
deviceName: string;
setDeviceName: (s: string) => Promise<void>;
permissionsEnabled: boolean;
@@ -42,20 +49,43 @@ export interface ViewProps {
toggleDeveloperMode: () => void;
}
-export function SettingsView({ deviceName, setDeviceName, permissionsEnabled, togglePermissions, developerMode, toggleDeveloperMode }: ViewProps): VNode {
+import { strings as messages } from '../i18n/strings'
+
+type LangsNames = {
+ [P in keyof typeof messages]: string
+}
+
+const names: LangsNames = {
+ es: 'Español [es]',
+ en: 'English [en]',
+ fr: 'Français [fr]',
+ de: 'Deutsch [de]',
+ sv: 'Svenska [sv]',
+ it: 'Italiano [it]',
+}
+
+
+export function SettingsView({ lang, changeLang, deviceName, setDeviceName, permissionsEnabled, togglePermissions, developerMode, toggleDeveloperMode }: ViewProps): VNode {
return (
<div>
<section style={{ height: 'calc(320px - 34px - 16px)', overflow: 'auto' }}>
-
- <h2>Wallet</h2>
+ <h2><i18n.Translate>Wallet</i18n.Translate></h2>
+ <SelectList
+ value={lang}
+ onChange={changeLang}
+ name="lang"
+ list={names}
+ label={i18n.str`Lang`}
+ description="(Choose your preferred lang)"
+ />
<EditableText
value={deviceName}
onChange={setDeviceName}
name="device-id"
- label="Device name"
+ label={i18n.str`Device name`}
description="(This is how you will recognize the wallet in the backup provider)"
/>
- <h2>Permissions</h2>
+ <h2><i18n.Translate>Permissions</i18n.Translate></h2>
<Checkbox label="Automatically open wallet based on page content"
name="perm"
description="(Enabling this option below will make using the wallet faster, but requires more permissions from your browser.)"
diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/packages/taler-wallet-webextension/src/popup/popup.tsx
index 32ff10a85..a6be4d192 100644
--- a/packages/taler-wallet-webextension/src/popup/popup.tsx
+++ b/packages/taler-wallet-webextension/src/popup/popup.tsx
@@ -27,7 +27,7 @@
import { i18n } from "@gnu-taler/taler-util";
import { ComponentChildren, JSX } from "preact";
import Match from "preact-router/match";
-import { useDevContext } from "../context/useDevContext";
+import { useDevContext } from "../context/devContext";
import { PopupNavigation } from '../components/styled'
export enum Pages {
diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
index 613218b80..39c25d508 100644
--- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
@@ -25,7 +25,7 @@ import { createHashHistory } from "history";
import { render } from "preact";
import Router, { route, Route } from "preact-router";
import { useEffect } from "preact/hooks";
-import { DevContextProvider } from "./context/useDevContext";
+import { DevContextProvider } from "./context/devContext";
import { useTalerActionURL } from "./hooks/useTalerActionURL";
import { strings } from "./i18n/strings";
import { BackupPage } from "./popup/BackupPage";