Skip to content

Contributing translations ​

OpenSecDash welcomes UI translations. The app currently uses simple Python dictionaries for translations, so adding or improving a language does not require a separate translation platform.

Where translations live ​

Core app translations live in:

text
backend/app/locales/en.py
backend/app/locales/de.py
backend/app/locales/__init__.py

Each locale file exports one dictionary:

python
EN = {
    "nav.dashboard": "Dashboard",
    "settings.title": "Settings",
}
python
DE = {
    "nav.dashboard": "Dashboard",
    "settings.title": "Einstellungen",
}

Plugin translations live inside the plugin file itself, in the plugin's locales dictionary. Example:

python
locales = {
    "en": {
        "my_plugin.settings.enabled": "My plugin enabled",
        "my_plugin.settings.enabled.help": "Enables My plugin.",
    },
    "de": {
        "my_plugin.settings.enabled": "My Plugin aktiviert",
        "my_plugin.settings.enabled.help": "Aktiviert My Plugin.",
    },
}

Adding a new app language ​

To add a new language, for example French (fr):

  1. Copy the English locale:
bash
cp backend/app/locales/en.py backend/app/locales/fr.py
  1. Rename the exported dictionary:
python
FR = {
    "app.name": "OpenSecDash",
    ...
}
  1. Add it to backend/app/locales/__init__.py:
python
from app.locales.de import DE
from app.locales.en import EN
from app.locales.fr import FR

LOCALES = {
    "de": DE,
    "en": EN,
    "fr": FR,
}
  1. Add the language to the Settings page selector in backend/app/templates/settings.html:
html
<option value="fr" {% if language_setting == 'fr' %}selected{% endif %}>Français</option>
  1. Allow the language code in backend/app/api/pages.py when settings are saved.

The current save handler validates language codes before storing them. Add the new code to that allow-list.

Translation keys ​

Templates call translations with:

jinja
{{ t("settings.title") }}

Python code can use:

python
from app.core.i18n import translate

translate("settings.title", "en")

If a key is missing, OpenSecDash returns the key itself. This makes missing translations visible in the UI, for example:

text
settings.title

Naming conventions ​

Use namespaced keys:

text
nav.*
common.*
settings.*
dashboard.*
events.*
access.*
assets.*
asset.*
diagnostics.*
insight.*
plugin_id.settings.*

Guidelines:

  • keep keys stable once used in templates/code
  • prefer clear, descriptive keys
  • avoid duplicate keys
  • keep technical event identifiers untranslated, such as access.denied
  • translate user-facing labels, help text, button labels, and descriptions

What should not be translated ​

Usually keep these unchanged:

  • technical event types: access.error, security.ban
  • plugin IDs: traefik_log, json_assets
  • setting keys: plugin.traefik_log.enabled
  • file paths and commands
  • API and product names such as LAPI, MQTT, GeoIP, Proxmox

Tone and wording ​

OpenSecDash is aimed at homelab users and OSS contributors. Prefer wording that is:

  • clear
  • practical
  • not overly formal
  • consistent with existing UI labels

For German translations, use informal but concise product UI language. Avoid overly long labels where they appear on buttons, table headers, or badges.

Plugin translations ​

When adding a plugin, include translations for all plugin settings:

python
PluginSetting(
    "poll_interval",
    "my_plugin.settings.poll_interval",
    "my_plugin.settings.poll_interval.help",
    "number",
    "5",
)

Needs matching locale entries:

python
"my_plugin.settings.poll_interval": "Poll interval seconds",
"my_plugin.settings.poll_interval.help": "How often the source is checked for new data.",

If you add a new app language, plugin locale dictionaries can also include that language code. If a plugin does not provide a translation for a language, OpenSecDash falls back to English for plugin settings.

Checking completeness ​

There is currently no dedicated translation completeness command. Recommended checks:

  1. Compare the new locale file with backend/app/locales/en.py.
  2. Search for the raw language key in the UI after running the app.
  3. Run tests and type checks:
bash
cd backend
uv lock --check
uv sync --python "$(cat .python-version)" --frozen --group dev
.venv/bin/python -m pytest tests/ -q
.venv/bin/pyright --pythonversion "$(cut -d. -f1,2 .python-version)" app tests ../plugins
  1. Build the website if docs were changed:
bash
cd website
npm run docs:build

Good pull requests ​

A good translation PR should include:

  • the new or updated locale file
  • Settings page language selector update, if adding a new language
  • any plugin locale additions, if relevant
  • screenshots are welcome for new languages, especially Settings and Dashboard
  • a note if some strings intentionally stay in English because they are technical terms

Released under the GNU Affero General Public License v3.0. Third-party licenses.