Looked again into it and saw that the mentioned script scans the whole repository.
So I created a wrapper script which provides you with the possibility to just collect the data of your plugin files.
This guide walks through every step.
Copy this bash script code and save it to extratc_pot
#!/bin/bash
# Generate gettext catalogs from a set of Python files.
#
# Usage:
# extract_pot OUTDIR LANG FILE_OR_DIR [FILE_OR_DIR ...]
#
# Produces, under OUTDIR:
# multisite.pot (template, freshly extracted)
# <LANG>/LC_MESSAGES/multisite.po (created if missing, else merged)
# <LANG>/LC_MESSAGES/multisite.mo (compiled from the .po)
#
# Dirs are searched recursively for *.py (tests excluded).
# Edit the .po between runs to add translations; re-run to merge + recompile.
set -e
set -o pipefail
if [ "$#" -lt 3 ]; then
echo "Usage: $0 OUTDIR LANG FILE_OR_DIR [FILE_OR_DIR ...]" >&2
exit 1
fi
OUTDIR="$1"
LANG_CODE="$2"
shift 2
POT_FILE="${OUTDIR}/multisite.pot"
PO_DIR="${OUTDIR}/${LANG_CODE}/LC_MESSAGES"
PO_FILE="${PO_DIR}/multisite.po"
MO_FILE="${PO_DIR}/multisite.mo"
# Expand args: keep files as-is, recurse into dirs for *.py (skip tests).
FILES=()
for arg in "$@"; do
if [ -d "$arg" ]; then
while IFS= read -r f; do
FILES+=("$f")
done < <(find -L "$arg" -type f -name "*.py" \
-not -path "*/tests/*" -not -path "*/test/*")
else
FILES+=("$arg")
fi
done
if [ "${#FILES[@]}" -eq 0 ]; then
echo "No Python files found." >&2
exit 1
fi
mkdir -p "${PO_DIR}"
# 1. Extract translatable strings into the .pot template.
xgettext -w 80 --sort-output --force-po -L python --from-code=utf-8 \
--add-comments=weblate-flags \
--keyword=_l \
--keyword=Title:1,1t \
--keyword=Help:1,1t \
--keyword=Label:1,1t \
--keyword=Message:1,1t \
-o "${POT_FILE}" \
"${FILES[@]}"
echo "Wrote ${POT_FILE}" >&2
# 2. Create the .po (first run) or merge new strings into the existing one.
if [ -f "${PO_FILE}" ]; then
msgmerge -U "${PO_FILE}" "${POT_FILE}"
echo "Merged ${PO_FILE}" >&2
else
msginit -i "${POT_FILE}" --no-translator -l "${LANG_CODE}" -o "${PO_FILE}"
echo "Created ${PO_FILE} (edit it to add translations)" >&2
fi
# 3. Compile the .po into the .mo gettext actually loads.
msgfmt "${PO_FILE}" -o "${MO_FILE}"
echo "Compiled ${MO_FILE}" >&2
Make the script executable:
chmod +x extract_pot
Before you start: run all commands as your site user (e.g. su - mysite), not as root. The translation tools (xgettext, msginit, msgmerge, msgfmt) are part of the gettext package — on most systems they’re already installed. If a command complains it’s missing, install gettext with your system package manager.
Throughout this guide I’ll use these example values — replace them with your own:
- Plugin name:
my_plugin (any name you like; just be consistent)
- Language:
de (German). Use fr for French, es for Spanish, etc.
- Plugin source folder:
/path/to/my_plugin (the folder containing the plugin’s .py files)
Step 1 — Generate the translation files
Run the script with three arguments, in this order:
./extract_pot <output-folder> <language> <plugin-source-folder>
| Argument |
What to put |
Example |
| 1. output-folder |
Where the translation files should be created |
~/local/share/check_mk/locale/packages/my_plugin |
| 2. language |
The two-letter language code |
de |
| 3. plugin-source-folder |
The folder with your plugin’s .py files |
/path/to/my_plugin |
So the full command looks like this:
./extract_pot ~/local/share/check_mk/locale/packages/my_plugin de /path/to/my_plugin
Outcome: the script scans your plugin’s code for translatable texts and creates this folder structure:
~/local/share/check_mk/locale/packages/my_plugin/
├── multisite.pot ← list of all texts found (template)
└── de/
└── LC_MESSAGES/
├── multisite.po ← the file YOU edit (Step 2)
└── multisite.mo ← the file Checkmk reads (built automatically)
At this point the translations are still empty — the script found the English texts but doesn’t know your language yet. That’s the next step.
Step 2 — Add your translations
Open the .po file in a text editor:
nano ~/local/share/check_mk/locale/packages/my_plugin/de/LC_MESSAGES/multisite.po
Inside you’ll see pairs of lines like this:
msgid "Maximum number of connections"
msgstr ""
msgid is the original English text — leave it unchanged.
msgstr is your translation — type it between the quotes.
For example, for German:
msgid "Maximum number of connections"
msgstr "Maximale Anzahl an Verbindungen"
Do this for every text you want translated. Any msgstr you leave empty will simply stay in English. Save and close the editor when you’re done (in nano: Ctrl+O, Enter, then Ctrl+X).
Step 3 — Apply the translations
Run the exact same command as in Step 1 again:
./extract_pot ~/local/share/check_mk/locale/packages/my_plugin de /path/to/my_plugin
Outcome: the script rebuilds the multisite.mo file from your edited .po. This .mo file is the one Checkmk actually reads.
You can repeat Steps 2 and 3 as often as you like. The script keeps the translations you already wrote and only adds any new texts — so it’s safe to re-run whenever the plugin is updated.
Step 4 — Reload the GUI
For Checkmk to notice the new translation, reload the web server:
omd reload apache
Then, in the Checkmk GUI, make sure your user’s language is set to the one you translated (top-right user menu → Edit profile → Language). Your plugin’s texts should now appear in your language.
Good to know
- What gets translated: only the texts shown in the configuration GUI — rule titles, help texts, and labels. The actual monitoring output of a check (the status summary text on a service) is not affected by this.
- One language at a time: to add a second language, just run Step 1 again with a different language code (e.g.
fr), then translate that language’s .po file.
- Empty translations stay English: there’s no harm in translating only some of the texts.
Hope this helps anyone who wants their custom plugins in their own language. Happy to answer questions below.