Skip to content

Commit

Permalink
Merge pull request #698 from openSUSE/gettext_wrapper
Browse files Browse the repository at this point in the history
Added Cockpit gettext wrapper
  • Loading branch information
lslezak authored Aug 11, 2023
2 parents f75bcf4 + 604bdae commit 459221b
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 3 deletions.
1 change: 1 addition & 0 deletions web/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"mgmt",
"mmcblk",
"multipath",
"ngettext",
"onboot",
"partitioner",
"patternfly",
Expand Down
4 changes: 1 addition & 3 deletions web/src/components/core/FileViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ import React, { useState, useEffect } from "react";
import { Popup } from "~/components/core";
import { Alert } from "@patternfly/react-core";
import { Loading } from "~/components/layout";
import { _ } from "~/i18n";

import cockpit from "../../lib/cockpit";

// FIXME: replace by a wrapper, this is just for testing
const _ = cockpit.gettext;

export default function FileViewer({ file, title, onCloseCallback }) {
// the popup is visible
const [isOpen, setIsOpen] = useState(true);
Expand Down
103 changes: 103 additions & 0 deletions web/src/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) [2023] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program 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 this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

/**
* This is a wrapper module for i18n functions. Currently it uses the cockpit
* implementation but the wrapper allows easy transition to another backend if
* needed.
*/

import cockpit from "./lib/cockpit";

/**
* Returns a translated text in the current locale or the original text if the
* translation is not found.
*
* @param {string} str the input string to translate
* @return {string} translated or original text
*/
const _ = (str) => cockpit.gettext(str);

/**
* Similar to the _() function. This variant returns singular or plural form
* depending on an additional "num" argument.
*
* @see {@link _} for further information
* @param {string} str1 the input string in the singular form
* @param {string} strN the input string in the plural form
* @param {number} n the actual number which decides whether to use the
* singular or plural form
* @return {string} translated or original text
*/
const n_ = (str1, strN, n) => cockpit.ngettext(str1, strN, n);

/**
* This is a no-op function, it can be used only for marking the text for
* translation so it is extracted to the POT file but the text itself is not
* translated. It needs to be translated by the _() function later.
*
* @example <caption>Error messages</caption>
* try {
* ...
* // the exception contains untranslated string
* throw(N_("Download failed"));
* } catch (error) {
* // log the untranslated error
* console.log(error);
* // for users display the translated error
* return <div>Error: {_(error)}</div>;
* }
*
* @example <caption>Constants</caption>
* // ERROR_MSG will not be translated, but the string will be found
* // by gettext when creating the POT file
* const ERROR_MSG = N_("Download failed");
* const OK_MSG = N_("Success");
*
* // assume that "result" contains one of the constants above
* const result = ...;
* // here the string will be translated using the current locale
* return <div>Result: {_(result)}</div>;
*
* @param {string} str the input string
* @return {string} the input string
*/
const N_ = (str) => str;

/**
* Similar to the N_() function, but for the singular and plural form.
*
* @see {@link N_} for further information
* @param {string} str1 the input string in the singular form
* @param {string} strN the input string in the plural form
* @param {number} n the actual number which decides whether to use the
* singular or plural form
* @return {string} the original text, either "string1" or "stringN" depending
* on the value "num"
*/
const Nn_ = (str1, strN, n) => (n === 1) ? str1 : strN;

export {
_,
n_,
N_,
Nn_
};
78 changes: 78 additions & 0 deletions web/src/i18n.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) [2023] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program 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 this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { _, n_, N_, Nn_ } from "~/i18n";
import cockpit from "./lib/cockpit";

// mock the cockpit gettext functions
jest.mock("./lib/cockpit");
const gettextFn = jest.fn();
cockpit.gettext.mockImplementation(gettextFn);
const ngettextFn = jest.fn();
cockpit.ngettext.mockImplementation(ngettextFn);

// some testing texts
const text = "text to translate";
const singularText = "singular text to translate";
const pluralText = "plural text to translate";

describe("i18n", () => {
describe("_", () => {
it("calls the cockpit.gettext() implementation", () => {
_(text);

expect(gettextFn).toHaveBeenCalledWith(text);
});
});

describe("n_", () => {
it("calls the cockpit.ngettext() implementation", () => {
n_(singularText, pluralText, 1);

expect(ngettextFn).toHaveBeenCalledWith(singularText, pluralText, 1);
});
});

describe("N_", () => {
it("returns the original text and does not translate it", () => {
const val = N_(text);

// test the object identity
expect(Object.is(val, text)).toBe(true);
});
});

describe("Nn_", () => {
it("returns the singular form for value 1 and does not translate it", () => {
const val = Nn_(singularText, pluralText, 1);

// test the object identity
expect(Object.is(val, singularText)).toBe(true);
});

it("returns the plural form for value 42 and does not translate it", () => {
const val = Nn_(singularText, pluralText, 42);

// test the object identity
expect(Object.is(val, pluralText)).toBe(true);
});
});
});

0 comments on commit 459221b

Please sign in to comment.