Skip to main content

django-unfold-farsi

Drop-in RTL / Persian support and sane defaults for the django-unfold admin.

django-unfold-farsi turns the Unfold admin into a polished right-to-left, Persian-first experience with a single settings helper — no template forks, no manual CSS patching. It bundles the Vazirmatn font, a set of surgical dir="rtl" layout overrides, a translated login/logout page, and language-forcing middleware. Everything is i18n-driven: RTL styling activates only while the active language is bidirectional (fa, ar, he, …). Switch the admin to English and you get Unfold's stock LTR look untouched.

PyPI version Python versions License: MIT


Table of contents


Features

  • One-call setupapply_unfold_farsi_defaults() merges STYLES, SCRIPTS and a default color palette into your existing UNFOLD dict. Your keys always win.
  • True RTL layout — every override is scoped to html[dir="rtl"], covering the sidebar, dashboard, changelist, forms, inlines, buttons, breadcrumbs, tabs, date/select/select2 widgets and the bulk-actions bar.
  • Self-hosted Vazirmatn font — no external CDN; ships subsetted woff2 files (arabic / latin / latin-ext) and applies them in RTL only.
  • Persian-first, still switchable — middleware makes Persian the default regardless of the browser's Accept-Language, while an explicit language choice from the switcher is always honoured.
  • Translated auth screens — Persianized login and logout templates plus a bundled fa message catalog for Unfold's own UI strings.
  • Guardrails — Django system checks warn you when the app or middleware is wired in the wrong order.

Requirements

Dependency Supported versions
Python 3.10 – 3.14
Django 4.2 LTS – 6.1
django-unfold 0.20+

Installation

pip install django-unfold-farsi

Using uv:

uv add django-unfold-farsi

From a local checkout (editable):

uv add --editable ../django-unfold-farsi

django and django-unfold are pulled in automatically as dependencies.


Quick start

The minimum to go from a stock Unfold admin to RTL/Persian:

# settings.py
from django.templatetags.static import static
from unfold_farsi.settings import apply_unfold_farsi_defaults

INSTALLED_APPS = [
    "unfold_farsi",   # before unfold
    "unfold",
    "django.contrib.admin",
    # ...
]

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "unfold_farsi.middleware.PersianDefaultLanguageMiddleware",  # before LocaleMiddleware
    "django.middleware.locale.LocaleMiddleware",
    # ...
]

LANGUAGE_CODE = "fa"
USE_I18N = True

UNFOLD = apply_unfold_farsi_defaults(
    {"SITE_TITLE": "پنل مدیریت", "SITE_HEADER": "سایت من"},
    static=static,
)
python manage.py collectstatic --noinput
python manage.py check      # confirms the wiring (see System checks)

That's it — open /admin/ and you're in a Persian, right-to-left Unfold.


Configuration

1. INSTALLED_APPS — order matters

Place unfold_farsi before unfold so its templates and static assets take precedence over Unfold's originals.

INSTALLED_APPS = [
    "unfold_farsi",   # must precede unfold so its template/static overrides win
    "unfold",
    "unfold.contrib.filters",
    "unfold.contrib.forms",
    "unfold.contrib.inlines",
    "django.contrib.admin",
    # ...your apps
]

2. MIDDLEWARE — bracket LocaleMiddleware

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "unfold_farsi.middleware.PersianDefaultLanguageMiddleware",  # before LocaleMiddleware
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
]

PersianDefaultLanguageMiddleware strips the incoming Accept-Language header so Django's language detection falls through to LANGUAGE_CODE (fa) instead of following the browser. An explicit choice from the language switcher (the django_language cookie / session) still wins, so users can switch to English at will. It must run before LocaleMiddleware.

3. UNFOLD — apply the defaults

Wrap your existing UNFOLD dict with the helper:

from django.templatetags.static import static
from unfold_farsi.settings import apply_unfold_farsi_defaults

UNFOLD = apply_unfold_farsi_defaults(
    {
        "SITE_TITLE": "پنل مدیریت",
        "SITE_HEADER": "سایت من",
        # ...your project-specific SIDEBAR, SITE_ICON, COLORS, etc.
    },
    static=static,
)

The helper is non-destructive:

  • Appends this package's STYLES and SCRIPTS after any you already declared.
  • Fills in a purple COLORS palette only if you didn't set one.
  • Sets SHOW_LANGUAGES = True (the language switcher) unless you override it.

Your own keys always take priority. static must be django.templatetags.static.static — Unfold requires each asset entry to be a lambda request: ... callable, which the helper builds for you.

NoteSIDEBAR, SITE_TITLE, SITE_ICON and friends are intentionally not shipped; they're project-specific. Set them yourself in the dict you pass in.

4. Language switching (optional)

To let users switch languages, declare them and wire Django's set_language view:

# settings.py
LANGUAGE_CODE = "fa"
LANGUAGES = [("fa", "فارسی"), ("en", "English")]
USE_I18N = True
# urls.py
from django.urls import include, path

urlpatterns = [
    path("i18n/", include("django.conf.urls.i18n")),
    # ...
]

Switch the admin to English and the RTL layout, Vazirmatn font and Persian copy all deactivate automatically — you get a clean LTR English admin on Unfold's default font.

5. Collect static

python manage.py collectstatic --noinput

How it works

i18n-driven, not settings-driven. Nothing keys off a "Persian mode" flag. The overrides target html[dir="rtl"], and Django sets dir="rtl" on the <html> element whenever the active language is bidirectional. So the same build serves a right-to-left Persian admin and a left-to-right English one depending only on the active language.

Three layers:

  1. CSS (unfold_custom.css) — logical mirroring of Unfold's physical ml-*/mr-* utilities, plus targeted fixes for widgets Unfold pins to a physical edge (date/time shortcut, select & select2 chevrons, timezone badge, collapsible/summary chevrons, accent bar, breadcrumb separator).
  2. JavaScript (unfold_custom.js) — the few fixes CSS can't express: mirroring the inline offset Unfold sets on the bulk-actions bar from JS, and seeding Alpine's changeListWidth when its ResizeObserver reports 0. Guarded by document.documentElement.dir !== 'rtl' — a no-op in LTR.
  3. Middleware + templates + locale — Persian-default language selection and translated auth screens.

API reference

apply_unfold_farsi_defaults(unfold=None, *, static)

Merge this package's RTL/Persian defaults into an UNFOLD settings dict. (Also aliased as apply_unfold_rtl_defaults for backwards compatibility).

Parameter Type Description
unfold dict | None Your existing UNFOLD dict. Copied, not mutated.
static Callable[[str], str] Must be django.templatetags.static.static.

Returns a new dict with STYLES/SCRIPTS appended, COLORS defaulted, and SHOW_LANGUAGES enabled — caller keys preserved.

PersianDefaultLanguageMiddleware

unfold_farsi.middleware.PersianDefaultLanguageMiddleware — drops the request's Accept-Language header so language detection falls back to LANGUAGE_CODE. Must be listed before django.middleware.locale.LocaleMiddleware.


System checks

Run python manage.py check. The app registers checks that surface common wiring mistakes:

ID Warning
unfold_farsi.W001 unfold_farsi is listed after unfold in INSTALLED_APPS.
unfold_farsi.W002 The middleware is not installed at all.
unfold_farsi.W003 The middleware runs after LocaleMiddleware.

What's shipped

Path Purpose
static/unfold_farsi/css/fonts.css Self-hosted Vazirmatn (@font-face), RTL only
static/unfold_farsi/css/unfold_custom.css html[dir="rtl"] layout/typography overrides
static/unfold_farsi/js/unfold_custom.js Runtime RTL fixups (RTL only)
static/unfold_farsi/fonts/vazirmatn/*.woff2 Subsetted font files
templates/admin/login.html Translatable Unfold login page
templates/registration/logged_out.html Translatable Unfold logout page
locale/fa/LC_MESSAGES/ Persian catalog for the strings above + Unfold UI
unfold_farsi.middleware.PersianDefaultLanguageMiddleware Persian-default, switchable
unfold_farsi.settings.apply_unfold_farsi_defaults The wiring helper

Customization

  • Colors — pass your own COLORS in the UNFOLD dict and the helper leaves it alone. The bundled default is a purple primary ramp.
  • Extra CSS/JS — declare your own STYLES/SCRIPTS in the dict; the package appends its assets after yours, so your rules can override.
  • A different font — override the font-family under html[dir="rtl"] in a stylesheet loaded after this package's.

Translations

The bundled fa catalog covers the auth templates plus Unfold's own UI strings (Unfold ships no locale of its own). If you edit the translatable strings in the templates, refresh the catalog:

django-admin makemessages -l fa
django-admin compilemessages   # requires GNU gettext

The package also registers its locale/ directory on LOCALE_PATHS at startup so the admin's jsi18n JavaScript catalog picks up the djangojs strings.


Compatibility

Any RTL/bidirectional language works — the overrides key off dir="rtl", which Django sets for fa, ar, he, ur, etc. Persian is only the default; the CSS itself is language-agnostic. The Vazirmatn font is optimised for Persian and Arabic script.


FAQ

Does this affect the English admin? No. Every override is scoped to html[dir="rtl"]; in LTR you get stock Unfold.

Do I need to set a Persian mode flag anywhere? No. It follows the active language via dir="rtl".

Can users still use English? Yes — wire the language switcher (step 4). The middleware only sets the default; an explicit choice always wins.

The bulk-actions "Run" button is clipped / the bar collapses. Make sure collectstatic ran and unfold_custom.js is being served; those are the exact issues it fixes.


Development

git clone <your-fork-url>
cd django-unfold-farsi
uv sync                       # or: pip install -e .

# Run the bundled demo project
cd demo
python manage.py migrate
python manage.py runserver

The demo/ project is a minimal Unfold admin wired exactly as the docs describe — use it to see changes live.


Contributing

Issues and pull requests are welcome. Please keep CSS overrides scoped to html[dir="rtl"], and run python manage.py check in the demo before submitting.


License

Released under the MIT License.


Credits

  • django-unfold — the admin theme this builds on.
  • Vazirmatn by Saber Rastikerdar — the bundled Persian font.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_unfold_farsi-0.1.0.tar.gz (128.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_unfold_farsi-0.1.0-py3-none-any.whl (133.3 kB view details)

Uploaded Python 3

File details

Details for the file django_unfold_farsi-0.1.0.tar.gz.

File metadata

  • Download URL: django_unfold_farsi-0.1.0.tar.gz
  • Upload date:
  • Size: 128.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for django_unfold_farsi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5c276479ead1e508544788a832e00ffb2018df4e7bb388941bf1f73cc904946b
MD5 6132745c977f2e7ed6bb051f7776a882
BLAKE2b-256 c8a03364a60b575385939bf907801e32c3f3025aa3dcf76ced901401832c408e

See more details on using hashes here.

File details

Details for the file django_unfold_farsi-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: django_unfold_farsi-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 133.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for django_unfold_farsi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bd193963056c95699c91c45a9e6abd31559621074c331b0658f1e4fc58ff0e11
MD5 4a843e865499e318de7517cb3490b04a
BLAKE2b-256 fbc424500be8a5dc05f66496d23394133c5748b8ad431a3a1a757a4f2bbe8316

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page