aboutsummaryrefslogtreecommitdiff
path: root/src/webex
diff options
context:
space:
mode:
Diffstat (limited to 'src/webex')
-rw-r--r--src/webex/i18n-test.tsx69
-rw-r--r--src/webex/i18n.tsx132
2 files changed, 115 insertions, 86 deletions
diff --git a/src/webex/i18n-test.tsx b/src/webex/i18n-test.tsx
new file mode 100644
index 000000000..4a1c40254
--- /dev/null
+++ b/src/webex/i18n-test.tsx
@@ -0,0 +1,69 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 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 test from "ava";
+import { internalSetStrings, str, Translate } from "./i18n";
+import { strings } from "../i18n/strings";
+import React from "react";
+import { render } from "enzyme";
+import { configure } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+
+configure({ adapter: new Adapter() });
+
+const testStrings = {
+ domain: "messages",
+ locale_data: {
+ messages: {
+ str1: ["foo1"],
+ str2: [""],
+ "str3 %1$s / %2$s": ["foo3 %2$s ; %1$s"],
+ "": {
+ domain: "messages",
+ plural_forms: "nplurals=2; plural=(n != 1);",
+ lang: "",
+ },
+ },
+ },
+};
+
+test("str translation", (t) => {
+ // Alias, so we nly use the function for lookups, not for string extranction.
+ const strAlias = str;
+ const TranslateAlias = Translate;
+ internalSetStrings(testStrings);
+ t.is(strAlias`str1`, "foo1");
+ t.is(strAlias`str2`, "str2");
+ const a = "a";
+ const b = "b";
+ t.is(strAlias`str3 ${a} / ${b}`, "foo3 b ; a");
+ const r = render(<TranslateAlias>str1</TranslateAlias>);
+ t.is(r.text(), "foo1");
+
+ const r2 = render(
+ <TranslateAlias>
+ str3 <span>{a}</span> / <span>{b}</span>
+ </TranslateAlias>,
+ );
+ t.is(r2.text(), "foo3 b ; a");
+
+ t.pass();
+});
+
+test("existing str translation", (t) => {
+ internalSetStrings(strings);
+ t.pass();
+});
diff --git a/src/webex/i18n.tsx b/src/webex/i18n.tsx
index 4c111a05a..6b5c2318d 100644
--- a/src/webex/i18n.tsx
+++ b/src/webex/i18n.tsx
@@ -28,7 +28,7 @@ import * as jedLib from "jed";
import * as React from "react";
-const jed = setupJed();
+let jed = setupJed();
const enableTracing = false;
@@ -55,6 +55,14 @@ function setupJed(): any {
}
/**
+ * 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 {
@@ -110,6 +118,37 @@ interface TranslateProps {
wrapProps?: any;
}
+function getTranslatedChildren(
+ translation: string,
+ children: React.ReactNode,
+): React.ReactNode[] {
+ const tr = translation.split(/%(\d+)\$s/);
+ const childArray = React.Children.toArray(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
@@ -125,35 +164,8 @@ interface TranslateProps {
export class Translate extends React.Component<TranslateProps, {}> {
render(): JSX.Element {
const s = stringifyChildren(this.props.children);
- const tr = jed
- .ngettext(s, s, 1)
- .split(/%(\d+)\$s/)
- .filter((e: any, i: number) => i % 2 === 0);
- const childArray = React.Children.toArray(this.props.children);
- for (let i = 0; i < childArray.length - 1; ++i) {
- if (
- typeof childArray[i] === "string" &&
- typeof childArray[i + 1] === "string"
- ) {
- childArray[i + 1] = (childArray[i] as string).concat(
- childArray[i + 1] as string,
- );
- childArray.splice(i, 1);
- }
- }
- const result = [];
- while (childArray.length > 0) {
- const x = childArray.shift();
- if (x === undefined) {
- continue;
- }
- if (typeof x === "string") {
- const t = tr.shift();
- result.push(t);
- } else {
- result.push(x);
- }
- }
+ const translation: string = jed.ngettext(s, s, 1);
+ const result = getTranslatedChildren(translation, this.props.children);
if (!this.props.wrap) {
return <div>{result}</div>;
}
@@ -216,34 +228,8 @@ export class TranslatePlural extends React.Component<
> {
render(): JSX.Element {
const s = stringifyChildren(this.props.children);
- const tr = jed
- .ngettext(s, s, 1)
- .split(/%(\d+)\$s/)
- .filter((e: any, i: number) => i % 2 === 0);
- const childArray = React.Children.toArray(this.props.children);
- for (let i = 0; i < childArray.length - 1; ++i) {
- if (
- typeof childArray[i] === "string" &&
- typeof childArray[i + 1] === "string"
- ) {
- childArray[i + i] = ((childArray[i] as string) +
- childArray[i + 1]) as string;
- childArray.splice(i, 1);
- }
- }
- const result = [];
- while (childArray.length > 0) {
- const x = childArray.shift();
- if (x === undefined) {
- continue;
- }
- if (typeof x === "string") {
- const t = tr.shift();
- result.push(t);
- } else {
- result.push(x);
- }
- }
+ const translation = jed.ngettext(s, s, 1);
+ const result = getTranslatedChildren(translation, this.props.children);
return <div>{result}</div>;
}
}
@@ -257,34 +243,8 @@ export class TranslateSingular extends React.Component<
> {
render(): JSX.Element {
const s = stringifyChildren(this.props.children);
- const tr = jed
- .ngettext(s, s, 1)
- .split(/%(\d+)\$s/)
- .filter((e: any, i: number) => i % 2 === 0);
- const childArray = React.Children.toArray(this.props.children);
- for (let i = 0; i < childArray.length - 1; ++i) {
- if (
- typeof childArray[i] === "string" &&
- typeof childArray[i + 1] === "string"
- ) {
- childArray[i + i] = ((childArray[i] as string) +
- childArray[i + 1]) as string;
- childArray.splice(i, 1);
- }
- }
- const result = [];
- while (childArray.length > 0) {
- const x = childArray.shift();
- if (x === undefined) {
- continue;
- }
- if (typeof x === "string") {
- const t = tr.shift();
- result.push(t);
- } else {
- result.push(x);
- }
- }
+ const translation = jed.ngettext(s, s, this.props.target);
+ const result = getTranslatedChildren(translation, this.props.children);
return <div>{result}</div>;
}
}