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,