Skip to content

Commit

Permalink
Merge pull request #156 from farridav/farridav/language_menu
Browse files Browse the repository at this point in the history
Adds optional language menu, fixes locale settings in demo
  • Loading branch information
TysonRV authored Sep 30, 2020
2 parents c339d08 + 7d6b706 commit e090d29
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 37 deletions.
20 changes: 20 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ JAZZMIN_SETTINGS = {
"changeform_format": "horizontal_tabs",
# override change forms on a per modeladmin basis
"changeform_format_overrides": {"auth.user": "collapsible", "auth.group": "vertical_tabs",},
# Add a language dropdown into the admin
"language_chooser": True,
}
```

Expand Down Expand Up @@ -226,6 +228,24 @@ Puts fieldsets and inlines into a bootstrap carousel, and allows paginaton with

![Carousel](./img/changeform_carousel.png)

## Language Chooser
You can enable a language chooser dropdown using `"language_chooser": True` in your `JAZZMIN_SETTINGS`, we mainly use this for
assisting with translations, but it could be of use to some people in their admin site.

To make proper use of this, please ensure you have internationalisation setup properly, See https://docs.djangoproject.com/en/3.1/topics/i18n/translation/

Namely:

- i18n urls for your admin
- `LocaleMiddleware` is used, and in the right place
- `LOCALE_DIRS` is setup
- `LANGUAGES` have been defined

See our [test app settings](https://github.com/farridav/django-jazzmin/tree/master/tests/test_app/settings.py)
for a practical example.

![Language chooser](./img/language_chooser.png)

## UI Tweaks

### UI Customiser
Expand Down
Binary file added docs/img/language_chooser.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions jazzmin/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@
"changeform_format": "horizontal_tabs",
# override change forms on a per modeladmin basis
"changeform_format_overrides": {},
# Add a language dropdown into the admin
"language_chooser": False,
}

#######################################
Expand Down
18 changes: 18 additions & 0 deletions jazzmin/templates/admin/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@
</li>
{% endif %}

{% if jazzmin_settings.language_chooser %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}

<li class="nav-item dropdown">
<a class="nav-link btn" data-toggle="dropdown" href="#" title="Choose language">
<i class="fas fa-globe pr-2" aria-hidden="true"></i>
</a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-left" id="jazzy-languagemenu">
{% for language in languages %}
<a href="{{ request|change_lang:language.code }}" class="dropdown-item{% if language.code == LANGUAGE_CODE %} active{% endif %}" lang="{{ language.code }}">
{{ language.name_local }}
</a>
{% endfor %}
</div>
</li>
{% endif %}

<li class="nav-item dropdown">
<a class="nav-link btn" data-toggle="dropdown" href="#" title="{{ request.user }}">
<i class="far fa-user pr-2" aria-hidden="true"></i>
Expand Down
14 changes: 11 additions & 3 deletions jazzmin/templatetags/jazzmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from django.template import Library, Context
from django.template.loader import get_template
from django.templatetags.static import static
from django.utils import translation
from django.utils.html import format_html
from django.utils.safestring import mark_safe, SafeText
from django.utils.text import get_text_list, slugify
Expand Down Expand Up @@ -242,6 +243,15 @@ def has_fieldsets(adminform: AdminForm) -> bool:
return has_fieldsets_check(adminform)


@register.filter
def change_lang(request: HttpRequest, language_code: str) -> str:
"""
Change the url to use the given language
"""
current_language = translation.get_language()
return request.get_full_path().replace(current_language, language_code)


@register.filter
def debug(value: Any) -> Any:
"""
Expand Down Expand Up @@ -387,9 +397,7 @@ def deleted(x: str) -> Dict:
)
if "name" in sub_message["changed"]:
sub_message["changed"]["name"] = gettext(sub_message["changed"]["name"])
messages.append(
changed(gettext("Changed {fields}.").format(sub_message["changed"]["fields"]))
)
messages.append(changed(gettext("Changed {fields}.").format(**sub_message["changed"])))
else:
messages.append(changed(gettext("Changed {fields}.").format(**sub_message["changed"])))

Expand Down
11 changes: 7 additions & 4 deletions tests/test_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.admindocs.middleware.XViewMiddleware",
Expand Down Expand Up @@ -75,7 +76,7 @@
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
]

LANGUAGE_CODE = "en-gb"
LANGUAGE_CODE = "en"
TIME_ZONE = "Europe/London"
USE_I18N = True
LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),)
Expand All @@ -88,8 +89,8 @@
("en", gettext("English")),
("de", gettext("German")),
("es", gettext("Spanish")),
("zh_Hans", gettext("Chinese (Simplified)")),
("zh_Hant", gettext("Chinese (Traditional)")),
("zh-hans", gettext("Simplified Chinese")),
("zh-hant", gettext("Traditional Chinese")),
)

STATIC_URL = "/static/"
Expand Down Expand Up @@ -207,7 +208,9 @@
# - carousel
"changeform_format": "horizontal_tabs",
# override change forms on a per modeladmin basis
"changeform_format_overrides": {"auth.user": "collapsible", "auth.group": "vertical_tabs",},
"changeform_format_overrides": {"auth.user": "collapsible", "auth.group": "vertical_tabs"},
# Add a language dropdown into the admin
"language_chooser": True,
}

if not DEBUG and not TEST:
Expand Down
7 changes: 5 additions & 2 deletions tests/test_app/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from django.conf import settings
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin, messages
from django.http import HttpResponseRedirect
from django.urls import re_path, reverse
from django.urls import re_path, reverse, path
from django.views.generic import RedirectView
from django.views.static import serve

Expand All @@ -23,10 +24,12 @@ def make_messages(request):
urlpatterns = [
url(r"^$", RedirectView.as_view(pattern_name="admin:index", permanent=False)),
url(r"admin/doc/", include("django.contrib.admindocs.urls")),
url(r"admin/", admin.site.urls),
url(r"make_messages/", make_messages, name="make_messages"),
path("i18n/", include("django.conf.urls.i18n")),
]

urlpatterns += i18n_patterns(path("admin/", admin.site.urls))

if settings.DEBUG:
urlpatterns.append(re_path(r"^static/(?P<path>.*)$", serve, kwargs={"document_root": settings.STATIC_ROOT}))

Expand Down
50 changes: 25 additions & 25 deletions tests/test_jazzmin_menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,34 @@ def test_side_menu(admin_client, settings):
response = admin_client.get(url)

assert parse_sidemenu(response) == {
"Global": ["/admin/"],
"Global": ["/en/admin/"],
"Polls": [
"/admin/polls/campaign/",
"/admin/polls/cheese/",
"/admin/polls/choice/",
"/admin/polls/poll/",
"/admin/polls/vote/",
"/en/admin/polls/campaign/",
"/en/admin/polls/cheese/",
"/en/admin/polls/choice/",
"/en/admin/polls/poll/",
"/en/admin/polls/vote/",
"/make_messages/",
],
"Administration": ["/admin/admin/logentry/"],
"Authentication and Authorization": ["/admin/auth/group/", "/admin/auth/user/"],
"Administration": ["/en/admin/admin/logentry/"],
"Authentication and Authorization": ["/en/admin/auth/group/", "/en/admin/auth/user/"],
}

settings.JAZZMIN_SETTINGS = override_jazzmin_settings(hide_models=["auth.user"])
response = admin_client.get(url)

assert parse_sidemenu(response) == {
"Global": ["/admin/"],
"Global": ["/en/admin/"],
"Polls": [
"/admin/polls/campaign/",
"/admin/polls/cheese/",
"/admin/polls/choice/",
"/admin/polls/poll/",
"/admin/polls/vote/",
"/en/admin/polls/campaign/",
"/en/admin/polls/cheese/",
"/en/admin/polls/choice/",
"/en/admin/polls/poll/",
"/en/admin/polls/vote/",
"/make_messages/",
],
"Administration": ["/admin/admin/logentry/"],
"Authentication and Authorization": ["/admin/auth/group/"],
"Administration": ["/en/admin/admin/logentry/"],
"Authentication and Authorization": ["/en/admin/auth/group/"],
}


Expand Down Expand Up @@ -70,11 +70,11 @@ def test_permissions_on_custom_links(client, settings):

client.force_login(user)
response = client.get(url)
assert parse_sidemenu(response) == {"Global": ["/admin/"]}
assert parse_sidemenu(response) == {"Global": ["/en/admin/"]}

client.force_login(user2)
response = client.get(url)
assert parse_sidemenu(response) == {"Global": ["/admin/"], "Polls": ["/admin/polls/poll/", "/make_messages/"]}
assert parse_sidemenu(response) == {"Global": ["/en/admin/"], "Polls": ["/en/admin/polls/poll/", "/make_messages/"]}


@pytest.mark.django_db
Expand All @@ -96,9 +96,9 @@ def test_top_menu(admin_client, settings):
response = admin_client.get(url)

assert parse_topmenu(response) == [
{"name": "Home", "link": "/admin/"},
{"name": "Home", "link": "/en/admin/"},
{"name": "Support", "link": "https://github.com/farridav/django-jazzmin/issues"},
{"name": "Users", "link": "/admin/auth/user/"},
{"name": "Users", "link": "/en/admin/auth/user/"},
{
"name": "Polls",
"link": "#",
Expand Down Expand Up @@ -132,10 +132,10 @@ def test_user_menu(admin_user, client, settings):
response = client.get(url)

assert parse_usermenu(response) == [
{"link": "/admin/password_change/", "name": "Change password"},
{"link": "/admin/logout/", "name": "Log out"},
{"link": "/admin/", "name": "Home"},
{"link": "/en/admin/password_change/", "name": "Change password"},
{"link": "/en/admin/logout/", "name": "Log out"},
{"link": "/en/admin/", "name": "Home"},
{"link": "https://github.com/farridav/django-jazzmin/issues", "name": "Support"},
{"link": "/admin/auth/user/", "name": "Users"},
{"link": "/admin/auth/user/{}/change/".format(admin_user.pk), "name": "See Profile"},
{"link": "/en/admin/auth/user/", "name": "Users"},
{"link": "/en/admin/auth/user/{}/change/".format(admin_user.pk), "name": "See Profile"},
]
4 changes: 2 additions & 2 deletions tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_delete_but_no_view_permission(client):
client.force_login(user)

response = client.get(url)
assert parse_sidemenu(response) == {"Global": ["/admin/"], "Polls": [None]}
assert parse_sidemenu(response) == {"Global": ["/en/admin/"], "Polls": [None]}


@pytest.mark.django_db
Expand All @@ -65,4 +65,4 @@ def test_no_permission(client):
client.force_login(user)

response = client.get(url)
assert parse_sidemenu(response) == {"Global": ["/admin/"]}
assert parse_sidemenu(response) == {"Global": ["/en/admin/"]}
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_get_custom_url():
"""
assert get_custom_url("http://somedomain.com") == "http://somedomain.com"
assert get_custom_url("/relative/path") == "/relative/path"
assert get_custom_url("admin:polls_poll_changelist") == "/admin/polls/poll/"
assert get_custom_url("admin:polls_poll_changelist") == "/en/admin/polls/poll/"


@pytest.mark.django_db
Expand Down

0 comments on commit e090d29

Please sign in to comment.