diff --git a/.github/workflows/weblate-update-pot.yml b/.github/workflows/weblate-update-pot.yml
new file mode 100644
index 0000000000..6d4db70d63
--- /dev/null
+++ b/.github/workflows/weblate-update-pot.yml
@@ -0,0 +1,88 @@
+name: Weblate Update POT
+
+on:
+ schedule:
+ # run every working day (Monday-Friday) at 1:42AM UTC
+ - cron: "42 1 * * 0-4"
+
+ # allow running manually
+ workflow_dispatch:
+
+jobs:
+ update-pot:
+ # do not run in forks
+ if: github.repository == 'openSUSE/agama'
+
+ runs-on: ubuntu-latest
+
+ container:
+ image: registry.opensuse.org/opensuse/tumbleweed:latest
+
+ steps:
+ - name: Configure and refresh repositories
+ # disable unused repositories to have a faster refresh
+ run: zypper modifyrepo -d repo-non-oss repo-openh264 repo-update && zypper ref
+
+ - name: Install tools
+ run: zypper --non-interactive install --no-recommends diffutils git gettext-tools
+
+ - name: Checkout Agama sources
+ uses: actions/checkout@v3
+ with:
+ path: agama
+
+ - name: Generate POT file
+ # TODO: use a shared script for this
+ run: |
+ cd agama/web
+ xgettext --default-domain=agama --output=- --language=C --keyword= \
+ --keyword=_:1,1t --keyword=_:1c,2,2t --keyword=C_:1c,2 \
+ --keyword=N_ --keyword=NC_:1c,2 --foreign-user \
+ --copyright-holder="SuSE Linux Products GmbH, Nuernberg" \
+ --from-code=UTF-8 --add-comments=TRANSLATORS --sort-by-file \
+ $(find . ! -name cockpit.js -name '*.js' -o ! -name '*.test.jsx' -name '*.jsx') | \
+ sed '/^#/ s/, c-format//' > agama.pot
+ msgfmt --statistics agama.pot
+
+ - name: Validate the generated POT file
+ run: msgfmt --check-format agama/web/agama.pot
+
+ - name: Checkout Weblate sources
+ uses: actions/checkout@v3
+ with:
+ path: agama-weblate
+ repository: openSUSE/agama-weblate
+ token: ${{ secrets.GH_TOKEN }}
+
+ - name: Configure Git
+ run: |
+ git config --global user.name "YaST Bot"
+ git config --global user.email "yast-devel@opensuse.org"
+
+ - name: Update POT file
+ run: |
+ mkdir -p agama-weblate/web
+ cp agama/web/agama.pot agama-weblate/web/agama.pot
+
+ # any change besides the timestamp in the POT file?
+ - name: Check changes
+ id: check_changes
+ run: |
+ git -C agama-weblate diff --ignore-matching-lines="POT-Creation-Date:" web/agama.pot > pot.diff
+
+ if [ -s pot.diff ]; then
+ echo "POT file updated"
+ echo "pot_updated=true" >> $GITHUB_OUTPUT
+ else
+ echo "POT file unchanged"
+ echo "pot_updated=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Push updated POT file
+ # run only when the POT file has been updated
+ if: steps.check_changes.outputs.pot_updated == 'true'
+ run: |
+ cd agama-weblate
+ git add web/agama.pot
+ git commit -m "Update POT file"$'\n\n'"Agama commit: $GITHUB_SHA"
+ git push
diff --git a/web/cspell.json b/web/cspell.json
index 51faca87f9..940b7fae66 100644
--- a/web/cspell.json
+++ b/web/cspell.json
@@ -32,6 +32,7 @@
"filename",
"fullname",
"freedesktop",
+ "gettext",
"ibft",
"ifaces",
"ipaddr",
diff --git a/web/src/components/core/FileViewer.jsx b/web/src/components/core/FileViewer.jsx
index 09eb9474f1..f23aab4f00 100644
--- a/web/src/components/core/FileViewer.jsx
+++ b/web/src/components/core/FileViewer.jsx
@@ -26,6 +26,9 @@ import { Loading } from "~/components/layout";
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);
@@ -61,13 +64,13 @@ export default function FileViewer({ file, title, onCloseCallback }) {
title={title || file}
className="large"
>
- {state === "loading" && }
+ {state === "loading" && }
{(content === null || error) &&
{error}
}
@@ -76,7 +79,7 @@ export default function FileViewer({ file, title, onCloseCallback }) {
- Close
+ {_("Close")}
);
diff --git a/web/src/components/core/FileViewer.test.jsx b/web/src/components/core/FileViewer.test.jsx
index f64110b886..49955e8182 100644
--- a/web/src/components/core/FileViewer.test.jsx
+++ b/web/src/components/core/FileViewer.test.jsx
@@ -22,7 +22,7 @@
import React from "react";
import { screen, waitFor, within } from "@testing-library/react";
-import { plainRender } from "~/test-utils";
+import { mockGettext, plainRender } from "~/test-utils";
import { FileViewer } from "~/components/core";
import cockpit from "../../lib/cockpit";
@@ -44,6 +44,8 @@ const file_name = "/testfile";
const content = "Read file content";
const title = "YaST Logs";
+mockGettext();
+
describe("FileViewer", () => {
beforeEach(() => {
readFn.mockResolvedValue(content);
diff --git a/web/src/test-utils.js b/web/src/test-utils.js
index 436efee575..51fc16ff0d 100644
--- a/web/src/test-utils.js
+++ b/web/src/test-utils.js
@@ -33,6 +33,7 @@ import { render } from "@testing-library/react";
import { createClient } from "~/client/index";
import { InstallerClientProvider } from "~/context/installer";
import { NotificationProvider } from "~/context/notification";
+import cockpit from "./lib/cockpit";
/**
* Internal mock for manipulating routes, using ["/"] by default
@@ -165,11 +166,25 @@ const withNotificationProvider = (content) => {
);
};
+/**
+ * Mocks the cockpit.gettext() method with an identity function (returns
+ * the original untranslated text)
+ */
+const mockGettext = () => {
+ const gettextFn = jest.fn();
+ gettextFn.mockImplementation((text) => {
+ return text;
+ });
+
+ cockpit.gettext.mockImplementation(gettextFn);
+};
+
export {
plainRender,
installerRender,
createCallbackMock,
mockComponent,
+ mockGettext,
mockLayout,
mockNavigateFn,
mockRoutes,