Skip to content

Commit c3ed066

Browse files
committed
feat: Enhance LanguageMiddleware with improved language detection and validation
- Added support for multiple language detection sources in LanguageMiddleware: query parameters (`?lang=xx`), user profile preference, cookies, and Accept-Language header. - Introduced `LISAN_ALLOWED_LANGUAGES` setting to restrict accepted languages to a configurable list. - Fallback to `LISAN_DEFAULT_LANGUAGE` when no valid language is detected or if the requested language is not supported. - Improved parsing of the Accept-Language header to handle complex cases (e.g., "en-US,en;q=0.9"). - Ensured safe retrieval of user profile language preferences to avoid potential errors. - Added validation to ensure selected language is within supported languages. - Updated default behavior to gracefully handle missing or malformed headers and attributes.
1 parent 14a248b commit c3ed066

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

CHANGELOG.md

+28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
## [v0.1.2] - 2024-10-05
2+
3+
This release focuses on refining the multilingual handling capabilities by introducing additional flexibility in language detection and ensuring compatibility with supported languages.
4+
5+
### Added
6+
- **Enhanced Language Detection in Middleware**:
7+
- **Improved `LanguageMiddleware`**: The `LanguageMiddleware` now includes more robust language detection mechanisms:
8+
- It checks for the `lang` query parameter, user profile language preference, cookies, and the `Accept-Language` header in a prioritized manner.
9+
- Added support for configurable fallback languages via the `LISAN_DEFAULT_LANGUAGE` setting.
10+
- Introduced validation to ensure the selected language is within the list of supported languages, using the new `LISAN_ALLOWED_LANGUAGES` setting.
11+
- Refined the `Accept-Language` header parsing to handle complex header values and gracefully fallback when necessary.
12+
13+
- **Support for Configurable Allowed Languages**: A new setting, `LISAN_ALLOWED_LANGUAGES`, has been added to specify the languages supported by the application. If a language from the request is not in this list, the middleware falls back to the default language.
14+
15+
### Configuration Changes
16+
- **New Settings**:
17+
- `LISAN_ALLOWED_LANGUAGES`: A list of language codes that the application supports. The `LanguageMiddleware` ensures that only these languages are applied to the request. Defaults to `[LISAN_DEFAULT_LANGUAGE]`.
18+
19+
### Fixed
20+
- Improved robustness in the `LanguageMiddleware` by ensuring safe access to user profile attributes and handling of missing or malformed `Accept-Language` headers.
21+
- Prevented invalid language codes from being set on requests by validating against the new `LISAN_ALLOWED_LANGUAGES` setting.
22+
23+
### Migration Notes
24+
No database migrations are required for this release. However, to take advantage of the new language validation features, developers should define `LISAN_ALLOWED_LANGUAGES` in `settings.py`.
25+
26+
---
27+
28+
129
## [v0.1.1] - 2024-09-14
230

331
This version brings enhanced flexibility, making the `lisan` package highly adaptable for multilingual projects by enabling external services for automated translations while maintaining a customizable architecture.

lisan/middleware.py

+42-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.utils.deprecation import MiddlewareMixin
2+
from django.conf import settings
23

34

45
class LanguageMiddleware(MiddlewareMixin):
@@ -24,11 +25,49 @@ def process_request(self, request):
2425
Args:
2526
request: The HTTP request object.
2627
"""
28+
# Fetch the default language from settings, fallback to 'en'
29+
default_language = getattr(settings, 'LISAN_DEFAULT_LANGUAGE', 'en')
30+
supported_language = getattr(settings, 'LISAN_ALLOWED_LANGUAGES', [default_language])
31+
32+
# Safe retrieval of user profile language preference
33+
language_preference = None
34+
if hasattr(request, 'user') and hasattr(request.user, 'profile') and hasattr(request.user.profile, 'language_preference'):
35+
language_preference = request.user.profile.language_preference
36+
37+
# Extract the language code in order of precedence
2738
language_code = (
2839
request.GET.get('lang') or
29-
getattr(request.user, 'profile.language_preference', None) or
40+
language_preference or
3041
request.COOKIES.get('language') or
31-
request.headers.get('Accept-Language') or
32-
'en'
42+
self.parse_accept_language(request.headers.get('Accept-Language')) or
43+
default_language
3344
)
45+
46+
# Validate against supported languages
47+
if language_code not in supported_language:
48+
language_code = default_language
49+
50+
# Set the language code on the request object
3451
request.language_code = language_code
52+
53+
def parse_accept_language(self, accept_language_header):
54+
"""
55+
Parse the 'Accept-Language' header and extract the first language code.
56+
If the header is malformed or empty, return None.
57+
58+
Args:
59+
accept_language_header: The value of the 'Accept-Language' header.
60+
61+
Returns:
62+
A string representing the best matched language code, or None if not found.
63+
"""
64+
if not accept_language_header:
65+
return None
66+
67+
# Split on commas and take the first part before any semicolon
68+
languages = accept_language_header.split(',')
69+
if languages:
70+
primary_language = languages[0].split(';')[0].strip()
71+
return primary_language if primary_language else None
72+
73+
return None

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def read_file(filename):
1111

1212
setup(
1313
name='lisan',
14-
version='0.1.1',
14+
version='0.1.2',
1515
packages=find_packages(),
1616
include_package_data=True,
1717
license='MIT',

0 commit comments

Comments
 (0)