moving strings from wallet-core to web-extension
This commit is contained in:
parent
2e1438eb04
commit
1d55c551bb
@ -1,93 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation helpers for React components and template literals.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { strings } from "./strings";
|
||||
export { strings } from "./strings";
|
||||
|
||||
// @ts-ignore: no type decl for this library
|
||||
import * as jedLib from "jed";
|
||||
import { Logger } from "../util/logging";
|
||||
|
||||
const logger = new Logger("i18n/index.ts");
|
||||
|
||||
export let jed: any = undefined;
|
||||
|
||||
/**
|
||||
* Set up jed library for internationalization,
|
||||
* based on browser language settings.
|
||||
*/
|
||||
export function setupI18n(lang: string): any {
|
||||
lang = lang.replace("_", "-");
|
||||
|
||||
if (!strings[lang]) {
|
||||
lang = "en-US";
|
||||
logger.warn(`language ${lang} not found, defaulting to english`);
|
||||
}
|
||||
jed = new jedLib.Jed(strings[lang]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use different translations for testing. Should not be used outside
|
||||
* of test cases.
|
||||
*/
|
||||
export function internalSetStrings(langStrings: any): void {
|
||||
jed = new jedLib.Jed(langStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert template strings to a msgid
|
||||
*/
|
||||
function toI18nString(stringSeq: ReadonlyArray<string>): string {
|
||||
let s = "";
|
||||
for (let i = 0; i < stringSeq.length; i++) {
|
||||
s += stringSeq[i];
|
||||
if (i < stringSeq.length - 1) {
|
||||
s += `%${i + 1}$s`;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internationalize a string template with arbitrary serialized values.
|
||||
*/
|
||||
export function str(stringSeq: TemplateStringsArray, ...values: any[]): string {
|
||||
const s = toI18nString(stringSeq);
|
||||
const tr = jed
|
||||
.translate(s)
|
||||
.ifPlural(1, s)
|
||||
.fetch(...values);
|
||||
return tr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an internationalized string (based on the globally set, current language)
|
||||
* from a JSON object. Fall back to the default language of the JSON object
|
||||
* if no match exists.
|
||||
*/
|
||||
export function getJsonI18n<K extends string>(
|
||||
obj: Record<K, string>,
|
||||
key: K,
|
||||
): string {
|
||||
return obj[key];
|
||||
}
|
@ -14,7 +14,8 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import * as core from "@gnu-taler/taler-wallet-core";
|
||||
import { setupI18n } from "@gnu-taler/taler-util"
|
||||
import { strings } from '../src/i18n'
|
||||
|
||||
const mockConfig = {
|
||||
backendURL: 'http://demo.taler.net',
|
||||
@ -45,8 +46,8 @@ export const globalTypes = {
|
||||
|
||||
export const decorators = [
|
||||
(Story, { globals }) => {
|
||||
core.setupI18n(globals.locale);
|
||||
setupI18n(globals.locale, strings);
|
||||
return <Story />
|
||||
},
|
||||
// (Story) => <ConfigContextProvider value={mockConfig}> <Story /> </ConfigContextProvider>
|
||||
// (Story) => <ConfigContextProvider value={mockConfig}> <Story /> </ConfigContextProvider>
|
||||
];
|
||||
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation helpers for React components and template literals.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
|
||||
import * as i18nCore from "@gnu-taler/taler-wallet-core";
|
||||
import { Component, ComponentChildren, h, JSX, toChildArray, VNode } from "preact";
|
||||
/**
|
||||
* Convert template strings to a msgid
|
||||
*/
|
||||
function toI18nString(stringSeq: ReadonlyArray<string>): string {
|
||||
let s = "";
|
||||
for (let i = 0; i < stringSeq.length; i++) {
|
||||
s += stringSeq[i];
|
||||
if (i < stringSeq.length - 1) {
|
||||
s += `%${i + 1}$s`;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
export const str = i18nCore.str;
|
||||
export const internalSetStrings = i18nCore.internalSetStrings;
|
||||
export const strings = i18nCore.strings;
|
||||
|
||||
|
||||
interface TranslateSwitchProps {
|
||||
target: number;
|
||||
}
|
||||
|
||||
function stringifyChildren(children: any): string {
|
||||
let n = 1;
|
||||
const ss = toChildArray(children).map((c) => {
|
||||
if (typeof c === "string") {
|
||||
return c;
|
||||
}
|
||||
return `%${n++}$s`;
|
||||
});
|
||||
const s = ss.join("").replace(/ +/g, " ").trim();
|
||||
console.log("translation lookup", JSON.stringify(s));
|
||||
return s;
|
||||
}
|
||||
|
||||
interface TranslateProps {
|
||||
/**
|
||||
* Component that the translated element should be wrapped in.
|
||||
* Defaults to "div".
|
||||
*/
|
||||
wrap?: any;
|
||||
|
||||
/**
|
||||
* Props to give to the wrapped component.
|
||||
*/
|
||||
wrapProps?: any;
|
||||
|
||||
/**
|
||||
* Translated elements
|
||||
*/
|
||||
children: ComponentChildren;
|
||||
}
|
||||
|
||||
function getTranslatedChildren(
|
||||
translation: string,
|
||||
children: ComponentChildren,
|
||||
): ComponentChildren {
|
||||
const tr = translation.split(/%(\d+)\$s/);
|
||||
const childArray = toChildArray(children);
|
||||
// Merge consecutive string children.
|
||||
const placeholderChildren = [];
|
||||
for (let i = 0; i < childArray.length; i++) {
|
||||
const x = childArray[i];
|
||||
if (x === undefined) {
|
||||
continue;
|
||||
} else if (typeof x === "string") {
|
||||
continue;
|
||||
} else {
|
||||
placeholderChildren.push(x);
|
||||
}
|
||||
}
|
||||
const result = [];
|
||||
for (let i = 0; i < tr.length; i++) {
|
||||
if (i % 2 == 0) {
|
||||
// Text
|
||||
result.push(tr[i]);
|
||||
} else {
|
||||
const childIdx = Number.parseInt(tr[i]) - 1;
|
||||
result.push(placeholderChildren[childIdx]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate text node children of this component.
|
||||
* If a child component might produce a text node, it must be wrapped
|
||||
* in a another non-text element.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* <Translate>
|
||||
* Hello. Your score is <span><PlayerScore player={player} /></span>
|
||||
* </Translate>
|
||||
* ```
|
||||
*/
|
||||
export function Translate({children, wrap, wrapProps}: TranslateProps): VNode {
|
||||
const s = stringifyChildren(children);
|
||||
const translation: string = i18nCore.jed.ngettext(s, s, 1);
|
||||
const result = getTranslatedChildren(translation, children);
|
||||
if (!wrap) {
|
||||
return <div>{result}</div>;
|
||||
}
|
||||
return h(wrap, wrapProps, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch translation based on singular or plural based on the target prop.
|
||||
* Should only contain TranslateSingular and TransplatePlural as children.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* <TranslateSwitch target={n}>
|
||||
* <TranslateSingular>I have {n} apple.</TranslateSingular>
|
||||
* <TranslatePlural>I have {n} apples.</TranslatePlural>
|
||||
* </TranslateSwitch>
|
||||
* ```
|
||||
*/
|
||||
export class TranslateSwitch extends Component<
|
||||
TranslateSwitchProps,
|
||||
void
|
||||
> {
|
||||
render(): JSX.Element {
|
||||
let singular: VNode<TranslationPluralProps> | undefined;
|
||||
let plural: VNode<TranslationPluralProps> | undefined;
|
||||
const children = this.props.children;
|
||||
if (children) {
|
||||
toChildArray(children).forEach((child: any) => {
|
||||
if (child.type === TranslatePlural) {
|
||||
plural = child;
|
||||
}
|
||||
if (child.type === TranslateSingular) {
|
||||
singular = child;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!singular || !plural) {
|
||||
console.error("translation not found");
|
||||
return h("span", {}, ["translation not found"]);
|
||||
}
|
||||
singular.props.target = this.props.target;
|
||||
plural.props.target = this.props.target;
|
||||
// We're looking up the translation based on the
|
||||
// singular, even if we must use the plural form.
|
||||
return singular;
|
||||
}
|
||||
}
|
||||
|
||||
interface TranslationPluralProps {
|
||||
target: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[TranslateSwitch]].
|
||||
*/
|
||||
export class TranslatePlural extends Component<
|
||||
TranslationPluralProps,
|
||||
void
|
||||
> {
|
||||
render(): JSX.Element {
|
||||
const s = stringifyChildren(this.props.children);
|
||||
const translation = i18nCore.jed.ngettext(s, s, 1);
|
||||
const result = getTranslatedChildren(translation, this.props.children);
|
||||
return <div>{result}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[TranslateSwitch]].
|
||||
*/
|
||||
export class TranslateSingular extends Component<
|
||||
TranslationPluralProps,
|
||||
void
|
||||
> {
|
||||
render(): JSX.Element {
|
||||
const s = stringifyChildren(this.props.children);
|
||||
const translation = i18nCore.jed.ngettext(s, s, this.props.target);
|
||||
const result = getTranslatedChildren(translation, this.props.children);
|
||||
return <div>{result}</div>;
|
||||
}
|
||||
}
|
24
packages/taler-wallet-webextension/src/i18n/index.ts
Normal file
24
packages/taler-wallet-webextension/src/i18n/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation helpers for React components and template literals.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
export { strings } from "./strings";
|
@ -22,14 +22,14 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import * as i18n from "../i18n";
|
||||
// import * as i18n from "../i18n";
|
||||
|
||||
import { renderAmount, ProgressButton } from "../renderHtml";
|
||||
import * as wxApi from "../wxApi";
|
||||
|
||||
import { useState, useEffect } from "preact/hooks";
|
||||
|
||||
import { getJsonI18n } from "@gnu-taler/taler-wallet-core";
|
||||
import { getJsonI18n, i18n } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
PreparePayResult,
|
||||
ConfirmPayResult,
|
||||
@ -171,19 +171,19 @@ export function TalerPayDialog({ talerPayUri }: Props): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<i18n.Translate wrap="p">
|
||||
<i18n.Translate>
|
||||
The merchant <span>{merchantName}</span> offers you to purchase:
|
||||
</i18n.Translate>
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<strong>{contractTerms.summary}</strong>
|
||||
</div>
|
||||
{totalFees ? (
|
||||
<i18n.Translate wrap="p">
|
||||
<i18n.Translate>
|
||||
The total price is <span>{amount} </span>
|
||||
(plus <span>{renderAmount(totalFees)}</span> fees).
|
||||
</i18n.Translate>
|
||||
) : (
|
||||
<i18n.Translate wrap="p">
|
||||
<i18n.Translate>
|
||||
The total price is <span>{amount}</span>.
|
||||
</i18n.Translate>
|
||||
)}
|
||||
|
@ -19,8 +19,13 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { PaymentStatus, TransactionPayment, TransactionType, TransactionWithdrawal, TransactionDeposit, TransactionRefresh, TransactionTip, TransactionRefund, WithdrawalType, TransactionCommon } from '@gnu-taler/taler-util';
|
||||
import { Fragment, h } from 'preact';
|
||||
import {
|
||||
PaymentStatus,
|
||||
TransactionCommon, TransactionDeposit, TransactionPayment,
|
||||
TransactionRefresh, TransactionRefund, TransactionTip, TransactionType,
|
||||
TransactionWithdrawal,
|
||||
WithdrawalType
|
||||
} from '@gnu-taler/taler-util';
|
||||
import { WalletTransactionView as Component } from './popup';
|
||||
|
||||
export default {
|
||||
@ -174,7 +179,7 @@ export const Refund = dynamic({
|
||||
});
|
||||
|
||||
export const RefundPending = dynamic({
|
||||
transaction: { ...exampleData.refund , pending: true }
|
||||
transaction: { ...exampleData.refund, pending: true }
|
||||
});
|
||||
|
||||
export const RefundWithProducts = dynamic({
|
||||
|
@ -37,13 +37,13 @@ import {
|
||||
AmountString,
|
||||
Timestamp,
|
||||
amountFractionalBase,
|
||||
i18n,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { format } from "date-fns";
|
||||
import { Component, ComponentChildren, Fragment, JSX } from "preact";
|
||||
import { route, Route, Router } from 'preact-router';
|
||||
import { Match } from 'preact-router/match';
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import * as i18n from "../i18n";
|
||||
import { PageLink, renderAmount } from "../renderHtml";
|
||||
import * as wxApi from "../wxApi";
|
||||
import { PermissionsCheckbox, useExtendedPermissions, Diagnostics } from "./welcome";
|
||||
@ -92,10 +92,10 @@ function bigAmount(amount: AmountJson): JSX.Element {
|
||||
|
||||
function EmptyBalanceView(): JSX.Element {
|
||||
return (
|
||||
<i18n.Translate wrap="p">
|
||||
<p><i18n.Translate>
|
||||
You have no balance to show. Need some{" "}
|
||||
<PageLink pageName="/welcome">help</PageLink> getting started?
|
||||
</i18n.Translate>
|
||||
</i18n.Translate></p>
|
||||
);
|
||||
}
|
||||
|
||||
@ -166,13 +166,13 @@ class WalletBalanceView extends Component<any, any> {
|
||||
|
||||
if (!Amounts.isZero(pendingIncoming)) {
|
||||
incoming = (
|
||||
<i18n.Translate wrap="span">
|
||||
<span><i18n.Translate>
|
||||
<span style={{ color: "darkgreen" }}>
|
||||
{"+"}
|
||||
{renderAmount(entry.pendingIncoming)}
|
||||
</span>{" "}
|
||||
incoming
|
||||
</i18n.Translate>
|
||||
</i18n.Translate></span>
|
||||
);
|
||||
}
|
||||
|
||||
@ -436,14 +436,14 @@ interface WalletTransactionProps {
|
||||
|
||||
export function WalletTransactionView({ transaction, onDelete, onBack }: WalletTransactionProps) {
|
||||
if (!transaction) {
|
||||
return <div>Loading ...</div>;
|
||||
return <div><i18n.Translate>Loading ...</i18n.Translate></div>;
|
||||
}
|
||||
|
||||
function Footer() {
|
||||
return <footer style={{ marginTop: 'auto', display: 'flex' }}>
|
||||
<button onClick={onBack}>back</button>
|
||||
<button onClick={onBack}><i18n.Translate>back</i18n.Translate></button>
|
||||
<div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}>
|
||||
<button onClick={onDelete}>remove</button>
|
||||
<button onClick={onDelete}><i18n.Translate>remove</i18n.Translate></button>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -21,8 +21,7 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
import * as i18n from "../i18n";
|
||||
|
||||
import { i18n } from '@gnu-taler/taler-util'
|
||||
import { renderAmount } from "../renderHtml";
|
||||
|
||||
import { useState, useEffect } from "preact/hooks";
|
||||
@ -64,11 +63,11 @@ export function View({ talerWithdrawUri, details, cancelled, selectedExchange, a
|
||||
return (
|
||||
<div>
|
||||
<h1>Digital Cash Withdrawal</h1>
|
||||
<i18n.Translate wrap="p">
|
||||
<p><i18n.Translate>
|
||||
You are about to withdraw{" "}
|
||||
<strong>{renderAmount(details.amount)}</strong> from your bank account
|
||||
into your wallet.
|
||||
</i18n.Translate>
|
||||
</i18n.Translate></p>
|
||||
{selectedExchange ? (
|
||||
<p>
|
||||
The exchange <strong>{selectedExchange}</strong> will be used as the
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
// import * as test from "ava";
|
||||
import { internalSetStrings, str, Translate } from "../src/i18n";
|
||||
import { internalSetStrings, str, Translate } from "@gnu-taler/taler-util";
|
||||
import { render, configure } from "enzyme";
|
||||
import Adapter from 'enzyme-adapter-preact-pure';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user