feat: add i18n framework with Hebrew (he) localization#1208
Open
yosef-chai wants to merge 1 commit into
Open
Conversation
Introduces a lightweight gettext-style internationalization layer for the
Web UI without changing English behavior for existing users.
Infrastructure
--------------
* backup/i18n.py — translation loader exposing _() for Python and
install_jinja_globals() to register _, lang, dir and is_rtl as Jinja2
globals. Missing translations transparently fall through to the English
source.
* backup/locales/{en,he}.json — flat JSON locale files. en.json is the
canonical key reference (English source maps to itself); he.json ships
302 Hebrew translations.
* backup/static/js/i18n.js — browser-side _() / _f() helpers reading the
window.I18N map injected by the bootstrap endpoint.
* backup/static/css/rtl.css — RTL overrides for Materialize widgets that
need explicit direction-aware tweaks (labels, prefixes, switches,
sidenav, toast container, etc.). Loaded conditionally only when the
active language is RTL.
Wire-up
-------
* config.json + backup/config/settings.py: new "language" option
(default "en", validator restricts to known codes).
* backup/ui/uiserver.py: applies the configured language before any
template rendering, registers Jinja2 globals on both ingress and
extra-port apps, and ships the active locale's translation map plus
language/dir hints with every bootstrap response so JS code can call
_() uniformly. The FOLDERS list and mount labels are translated at
serialization time.
* Templates: base.jinja2 + base-server.jinja2 set <html lang/dir> from
the active locale and conditionally include rtl.css. navbar.jinja2 and
footer.jinja2 use {{ _("...") }} as the demonstrative pattern.
* Python user-facing strings wrapped with _(): backup status badges
(Backed Up / Drive Only / HA Only / Deleted / Pending / Created /
Failed! / Loading X% / Uploading X%), Time.formatDelta units and
"X ago" / "right now", sensor friendly_name, notification title/body,
Disabled/Never status text, and every KnownError.message() return
in backup/exceptions/exceptions.py.
Docs
----
* DOCS.md: documents the new "language" option.
* CONTRIBUTING.md: adds a "Localization" section explaining the
framework, the locale file layout, and the recipe for adding new
languages.
Notes for reviewers
-------------------
* Backwards compatible: the default language is "en" and the locale file
is empty, so the addon's behavior is unchanged for existing users.
* English remains canonical in source. Translators only edit JSON files;
no need to touch templates or Python.
* Some JS substring checks (e.g. status.includes("Backed Up") in
scripts.js) still match against the displayed status text. They will
fall back to the generic "Help unavailable" tooltip in non-English
locales until those checks are refactored to use a separate status
code field; the rest of the UI works regardless. Happy to follow up on
that in a separate PR if you'd like to merge the framework first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a lightweight gettext-style internationalization layer to the Web UI plus a complete Hebrew (
he) locale, opt-in via a newlanguageconfig option. English behavior is unchanged for existing users.Why
There has been recurring community interest in non-English UIs. This PR introduces the smallest viable framework so translations can ship as data (JSON locale files) without further code changes — adding a new language only means dropping a new JSON file and registering its code in two places. RTL layout is wired up so Hebrew/Arabic/Persian translations are visually correct out of the box.
What's in the change
New files
backup/i18n.py— translation loader exposing_()for Python andinstall_jinja_globals()for Jinja2.backup/locales/en.json— canonical key reference (English source → English; missing keys fall through to source).backup/locales/he.json— 302 Hebrew translations covering navbar, footer, settings, errors, modals, status badges, notifications, sensor friendly names, exception messages, andformatDeltaunits.backup/static/js/i18n.js— browser-side_()and_f()helpers fed bywindow.I18Ninjected at bootstrap.backup/static/css/rtl.css— Materialize widget overrides for RTL (input labels, prefixes, switches, sidenav, toast container, etc.). Loaded conditionally only when the active language is RTL.Existing files
config.json+backup/config/settings.py: newlanguageoption (defaulten, validator restricts to known codes).backup/ui/uiserver.py: applies the configured language before any rendering, installs Jinja2 globals on both ingress and extra-port apps, and ships the translation map pluslang/dirin the bootstrap response.FOLDERSand mount labels are translated at serialization time.base.jinja2/base-server.jinja2: set<html lang/dir>dynamically and conditionally includertl.css.navbar.jinja2/footer.jinja2: demonstrative{{ _("...") }}wrapping._(): status badges inbackups.py(Backed Up/Drive Only/HA Only/Deleted),hasource.py(Pending/Created/Failed!/Loading {0}%),drivesource.py(Uploading {0}%),Time.formatDeltaunits and{0} ago/right now, sensorfriendly_names inhaupdater.py/harequests.py, notification title/body,Disabled/Neverstatus inuiserver.py, and everyKnownError.message()return inexceptions/exceptions.py.Docs
DOCS.md: documents the newlanguageoption.CONTRIBUTING.md: new “Localization” section explaining the framework, the locale file layout, and the recipe for adding new languages.Design notes
/bootstrapendpoint additionally emitswindow.I18N,window.I18N_LANG, andwindow.I18N_DIR, so the browser-side_()is available on every page without touching individual templates.i18n.pykeeps a single module-level cache and resets the active language whenever the server starts, so reloading the addon picks up config changes.enanden.jsonis empty, so nothing visible changes for current users. The new option doesn't break the supervisor schema.Known follow-up (intentionally out of scope)
scripts.jsstill does a fewbackup.status.includes("Backed Up")-style substring checks against the displayed status text. Oncestatusis translated, those checks miss and the tooltip falls back to the generic “Help unavailable.” The cleanest fix is to add a parallelstatus_codefield togetBackupDetails()and have JS switch on the canonical key. I left that out of this PR so the framework can land first; happy to follow up.Test plan
python3 -m py_compileon every edited file.json.loadonconfig.json+ both locale JSONs._,lang,dir,is_rtlglobals registered.set_language("he")+gettext("Settings")round-trip.Screenshots
Happy to add if useful — let me know what views you'd like to see in
envshe.