/* 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 */ /** * Translation helpers for React components and template literals. */ /** * Imports */ import React from "react"; import * as i18nCore from "@gnu-taler/taler-wallet-core"; /** * Convert template strings to a msgid */ function toI18nString(stringSeq: ReadonlyArray): 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 = React.Children.map(children, (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; } 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 * in a another non-text element. * * Example: * ``` * * Hello. Your score is * * ``` */ export class Translate extends React.Component { render(): JSX.Element { const s = stringifyChildren(this.props.children); const translation: string = i18nCore.jed.ngettext(s, s, 1); const result = getTranslatedChildren(translation, this.props.children); if (!this.props.wrap) { return
{result}
; } return React.createElement(this.props.wrap, this.props.wrapProps, result); } } /** * Switch translation based on singular or plural based on the target prop. * Should only contain TranslateSingular and TransplatePlural as children. * * Example: * ``` * * I have {n} apple. * I have {n} apples. * * ``` */ export class TranslateSwitch extends React.Component< TranslateSwitchProps, void > { render(): JSX.Element { let singular: React.ReactElement | undefined; let plural: React.ReactElement | undefined; const children = this.props.children; if (children) { React.Children.forEach(children, (child: any) => { if (child.type === TranslatePlural) { plural = child; } if (child.type === TranslateSingular) { singular = child; } }); } if (!singular || !plural) { console.error("translation not found"); return React.createElement("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 React.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
{result}
; } } /** * See [[TranslateSwitch]]. */ export class TranslateSingular extends React.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
{result}
; } }