django-i18n-noprefix


Namedjango-i18n-noprefix JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
SummaryDjango internationalization without URL prefixes
upload_time2025-08-08 05:54:19
maintainerNone
docs_urlNone
authorjinto
requires_python>=3.8
licenseMIT
keywords django i18n internationalization l10n localization no-prefix translation url
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # django-i18n-noprefix

[![Tests](https://github.com/jinto/django-i18n-noprefix/workflows/Tests/badge.svg)](https://github.com/jinto/django-i18n-noprefix/actions/workflows/test.yml)
[![Code Quality](https://github.com/jinto/django-i18n-noprefix/workflows/Code%20Quality/badge.svg)](https://github.com/jinto/django-i18n-noprefix/actions/workflows/quality.yml)
[![Release](https://github.com/jinto/django-i18n-noprefix/workflows/Release/badge.svg)](https://github.com/jinto/django-i18n-noprefix/actions/workflows/release.yml)
[![codecov](https://codecov.io/gh/jinto/django-i18n-noprefix/branch/main/graph/badge.svg)](https://codecov.io/gh/jinto/django-i18n-noprefix)
[![PyPI Version](https://img.shields.io/pypi/v/django-i18n-noprefix.svg)](https://pypi.org/project/django-i18n-noprefix/)
[![Python Support](https://img.shields.io/pypi/pyversions/django-i18n-noprefix.svg)](https://pypi.org/project/django-i18n-noprefix/)
[![Django Support](https://img.shields.io/badge/Django-4.2%20%7C%205.0%20%7C%205.1-green.svg)](https://www.djangoproject.com/)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jinto/django-i18n-noprefix/blob/main/LICENSE)

**Clean URLs for multilingual Django sites without language prefixes.**

Seamlessly integrate Django's i18n framework while keeping your URLs clean. No more `/en/about/` or `/ko/about/` — just `/about/` that works in any language.

## 🎯 Why django-i18n-noprefix?

Django's built-in i18n system automatically adds language codes to your URLs (`/en/`, `/ko/`, `/ja/`). While this works, it has drawbacks:

### The Problem with URL Prefixes

| Issue | With Prefixes | With django-i18n-noprefix |
|-------|--------------|---------------------------|
| **URLs** | `/en/products/`<br>`/ko/products/` | `/products/` |
| **SEO** | Duplicate content issues | Single URL per content |
| **Sharing** | Language-specific links | Universal links |
| **User Experience** | Exposes technical details | Clean, professional URLs |

### Real-World Example

```python
# Before: Django default i18n
https://mysite.com/en/products/laptop/    # English
https://mysite.com/ko/products/laptop/    # Korean
https://mysite.com/ja/products/laptop/    # Japanese

# After: With django-i18n-noprefix
https://mysite.com/products/laptop/        # Any language!
```

## ✨ Features

- 🚫 **No URL prefixes** — Keep URLs clean and professional
- 🔄 **Full Django i18n compatibility** — Use all Django's i18n features
- 🍪 **Smart language detection** — Session, cookie, and Accept-Language header support
- 🎨 **3 language selector styles** — Dropdown, list, or inline
- 🎯 **Framework agnostic** — Bootstrap, Tailwind, or vanilla CSS
- ⚡ **Zero configuration** — Works out of the box
- 🔒 **Production ready** — 93% test coverage
- 📦 **Lightweight** — No external dependencies except Django

## 🚀 Quick Start (5 minutes)

### 1. Install

```bash
pip install django-i18n-noprefix
```

### 2. Update Settings

```python
# settings.py

INSTALLED_APPS = [
    # ...
    'django_i18n_noprefix',  # Add this
]

MIDDLEWARE = [
    # ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    # 'django.middleware.locale.LocaleMiddleware',  # Remove this
    'django_i18n_noprefix.middleware.NoPrefixLocaleMiddleware',  # Add this
    'django.middleware.common.CommonMiddleware',
    # ...
]

# Configure languages
LANGUAGES = [
    ('en', 'English'),
    ('ko', '한국어'),
    ('ja', '日本語'),
]
LANGUAGE_CODE = 'en'
```

### 3. Add URL Pattern

```python
# urls.py
from django.urls import path, include

urlpatterns = [
    # ...
    path('i18n/', include('django_i18n_noprefix.urls')),
]
```

### 4. Add Language Selector

```django
<!-- base.html -->
{% load i18n_noprefix %}

<!DOCTYPE html>
<html>
<body>
    <!-- Add anywhere in your template -->
    {% language_selector %}

    <!-- Your content -->
    {% block content %}{% endblock %}
</body>
</html>
```

**That's it!** Your site now supports multiple languages without URL prefixes.

## 📋 Detailed Installation

### Using pip

```bash
pip install django-i18n-noprefix
```

### Using uv

```bash
uv pip install django-i18n-noprefix
```

### Development Installation

```bash
git clone https://github.com/jinto/django-i18n-noprefix.git
cd django-i18n-noprefix
pip install -e ".[dev]"
```

## 🔧 Configuration

### Basic Configuration

The minimal configuration shown in Quick Start is often sufficient. Here are all available options:

```python
# settings.py

# Required: Enable Django i18n
USE_I18N = True
USE_L10N = True  # Django < 5.0
USE_TZ = True

# Language Configuration
LANGUAGES = [
    ('en', 'English'),
    ('ko', '한국어'),
    ('ja', '日本語'),
    ('zh', '中文'),
    ('es', 'Español'),
]

LANGUAGE_CODE = 'en'  # Default language

# Optional: Customize cookie name (default: 'django_language')
LANGUAGE_COOKIE_NAME = 'django_language'
LANGUAGE_COOKIE_AGE = 365 * 24 * 60 * 60  # 1 year
LANGUAGE_COOKIE_PATH = '/'
LANGUAGE_COOKIE_DOMAIN = None
LANGUAGE_COOKIE_SECURE = False
LANGUAGE_COOKIE_HTTPONLY = False
LANGUAGE_COOKIE_SAMESITE = 'Lax'
```

### Language Detection Priority

Languages are detected in this order:

1. **URL parameter** (when switching languages)
2. **Session** (for logged-in users)
3. **Cookie** (for anonymous users)
4. **Accept-Language header** (browser preference)
5. **LANGUAGE_CODE setting** (fallback)

## 📖 Usage Examples

### Basic Language Selector

```django
{% load i18n_noprefix %}

<!-- Dropdown style (default) -->
{% language_selector %}

<!-- List style -->
{% language_selector style='list' %}

<!-- Inline style -->
{% language_selector style='inline' %}
```

### Custom Language Selector

```django
{% load i18n i18n_noprefix %}
{% get_current_language as CURRENT_LANGUAGE %}
{% get_available_languages as LANGUAGES %}

<!-- Custom dropdown -->
<select onchange="location.href=this.value">
    {% for lang_code, lang_name in LANGUAGES %}
        <option value="{% switch_language_url lang_code %}"
                {% if lang_code == CURRENT_LANGUAGE %}selected{% endif %}>
            {{ lang_name }}
        </option>
    {% endfor %}
</select>

<!-- Custom buttons -->
<div class="language-buttons">
    {% for lang_code, lang_name in LANGUAGES %}
        <a href="{% switch_language_url lang_code %}"
           class="btn {% if lang_code|is_current_language %}active{% endif %}">
            {{ lang_code|upper }}
        </a>
    {% endfor %}
</div>
```

### Programmatic Language Change

```python
# views.py
from django.shortcuts import redirect
from django_i18n_noprefix.utils import activate_language

def my_view(request):
    # Change language programmatically
    activate_language(request, 'ko')
    return redirect('home')

# Or in class-based views
from django.views import View

class LanguagePreferenceView(View):
    def post(self, request):
        language = request.POST.get('language')
        if language:
            activate_language(request, language)
        return redirect(request.META.get('HTTP_REFERER', '/'))
```

### AJAX Language Switching

```javascript
// JavaScript
function changeLanguage(langCode) {
    fetch('/i18n/set-language-ajax/', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': getCookie('csrftoken')
        },
        body: JSON.stringify({language: langCode})
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            location.reload();
        }
    });
}
```

### Using with Django Forms

```python
# forms.py
from django import forms
from django.conf import settings

class LanguageForm(forms.Form):
    language = forms.ChoiceField(
        choices=settings.LANGUAGES,
        widget=forms.Select(attrs={'class': 'form-control'})
    )

# views.py
from django.shortcuts import redirect
from django_i18n_noprefix.utils import activate_language

def change_language_view(request):
    if request.method == 'POST':
        form = LanguageForm(request.POST)
        if form.is_valid():
            activate_language(request, form.cleaned_data['language'])
            return redirect('home')
    else:
        form = LanguageForm()
    return render(request, 'change_language.html', {'form': form})
```

## 🔄 Integration with Django i18n

This package **complements** Django's i18n system. You can use all Django i18n features:

### Using Django's Translation Features

```django
{% load i18n %}

<!-- Translation tags work normally -->
{% trans "Welcome" %}
{% blocktrans %}Hello {{ name }}{% endblocktrans %}

<!-- Get current language -->
{% get_current_language as LANGUAGE_CODE %}
Current language: {{ LANGUAGE_CODE }}

<!-- Get available languages -->
{% get_available_languages as LANGUAGES %}
```

### In Python Code

```python
from django.utils.translation import gettext as _
from django.utils.translation import get_language

def my_view(request):
    # Django's translation functions work normally
    message = _("Welcome to our site")
    current_language = get_language()

    return render(request, 'template.html', {
        'message': message,
        'language': current_language
    })
```

### Migration from Standard Django i18n

Migrating from Django's default i18n is straightforward:

1. **Remove URL prefixes**:
   ```python
   # Old: urls.py
   from django.conf.urls.i18n import i18n_patterns

   urlpatterns = i18n_patterns(
       path('about/', views.about),
       # ...
   )

   # New: urls.py
   urlpatterns = [
       path('about/', views.about),
       # ...
   ]
   ```

2. **Replace middleware** (as shown in Quick Start)

3. **Update language switcher** (use our template tags)

That's it! All your translations and language files remain unchanged.

## 🎨 Styling Options

### Bootstrap 5

```django
{% load static %}
<link href="{% static 'i18n_noprefix/css/bootstrap5.css' %}" rel="stylesheet">

{% language_selector style='dropdown' %}
```

### Tailwind CSS

```django
{% load static %}
<link href="{% static 'i18n_noprefix/css/tailwind.css' %}" rel="stylesheet">

{% language_selector style='inline' %}
```

### Vanilla CSS

```django
{% load static %}
<link href="{% static 'i18n_noprefix/css/vanilla.css' %}" rel="stylesheet">

{% language_selector style='list' %}
```

### Custom Styling

```css
/* Override CSS variables */
:root {
    --i18n-primary: #007bff;
    --i18n-primary-hover: #0056b3;
    --i18n-text: #333;
    --i18n-bg: #fff;
    --i18n-border: #ddd;
    --i18n-radius: 4px;
}

/* Or write custom CSS */
.language-selector-dropdown {
    /* Your styles */
}
```

## 📊 Django/Python Compatibility Matrix

| Django Version | Python 3.8 | Python 3.9 | Python 3.10 | Python 3.11 | Python 3.12 |
|---------------|------------|------------|-------------|-------------|-------------|
| 4.2 LTS       | ✅         | ✅         | ✅          | ✅          | ✅          |
| 5.0           | ❌         | ❌         | ✅          | ✅          | ✅          |
| 5.1           | ❌         | ❌         | ✅          | ✅          | ✅          |
| 5.2           | ❌         | ❌         | ✅          | ✅          | ✅          |

## 🎮 Example Project

See the package in action with our complete example project:

```bash
# Clone the repository
git clone https://github.com/jinto/django-i18n-noprefix.git
cd django-i18n-noprefix/example_project

# Install dependencies
pip install django

# Run the demo
./run_demo.sh

# Visit http://localhost:8000
```

The example project includes:
- Multi-language support (English, Korean, Japanese)
- All three language selector styles
- Bootstrap, Tailwind, and Vanilla CSS examples
- Complete translation files
- Production-ready configuration

## 🔍 API Reference

### Middleware

```python
class NoPrefixLocaleMiddleware:
    """
    Replacement for Django's LocaleMiddleware that doesn't use URL prefixes.

    Methods:
        get_language(request) -> str: Detect language from request
        save_language(request, response, language) -> None: Persist language choice
    """
```

### Template Tags

```django
{% load i18n_noprefix %}

<!-- Render language selector -->
{% language_selector [style='dropdown|list|inline'] [next_url='/custom/'] %}

<!-- Get URL for language switch -->
{% switch_language_url 'ko' [next_url='/custom/'] %}

<!-- Check if language is current -->
{{ 'ko'|is_current_language }} → True/False
```

### Views

```python
# URL: /i18n/set-language/<lang_code>/
def change_language(request, lang_code):
    """Change language and redirect."""

# URL: /i18n/set-language-ajax/
def set_language_ajax(request):
    """AJAX endpoint for language change."""
```

### Utility Functions

```python
from django_i18n_noprefix.utils import (
    activate_language,       # Activate language for request
    get_supported_languages, # Get list of language codes
    get_language_choices,    # Get language choices for forms
    is_valid_language,      # Validate language code
)
```

## 🐛 Troubleshooting

### Common Issues and Solutions

**Language not persisting across requests**
- Ensure sessions are enabled in `INSTALLED_APPS` and `MIDDLEWARE`
- Check that cookies are not blocked in the browser
- Verify `NoPrefixLocaleMiddleware` is after `SessionMiddleware`

**Translations not working**
```python
# Check these settings
USE_I18N = True
LOCALE_PATHS = [BASE_DIR / 'locale']

# Run these commands
python manage.py makemessages -l ko
python manage.py compilemessages
```

**Static files not loading**
```bash
python manage.py collectstatic
```

**Middleware order issues**
```python
# Correct order
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  # ← First
    'django_i18n_noprefix.middleware.NoPrefixLocaleMiddleware',  # ← Second
    'django.middleware.common.CommonMiddleware',
    # ...
]
```

### Debug Mode

```python
# Enable debug logging
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django_i18n_noprefix': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}
```

## 🚀 Performance

- **Zero overhead**: Middleware adds < 0.1ms per request
- **Smart caching**: Language preference cached in session/cookie
- **No database queries**: Pure middleware solution
- **CDN friendly**: No URL prefixes mean better cache utilization

## 🔒 Security Considerations

- CSRF protection on language change endpoints
- XSS safe: All outputs are properly escaped
- Cookie security: Supports Secure, HttpOnly, and SameSite flags
- No user input in URL paths

## 🤝 Contributing

We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

```bash
# Setup development environment
git clone https://github.com/jinto/django-i18n-noprefix.git
cd django-i18n-noprefix
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=django_i18n_noprefix

# Code formatting
black .
ruff check .

# Type checking
mypy django_i18n_noprefix
```

## 📝 Changelog

See [CHANGELOG.md](CHANGELOG.md) for release history.

## 📄 License

MIT License - see [LICENSE](LICENSE) for details.

## 🙏 Credits

Built with ❤️ by [jinto](https://github.com/jinto) and contributors.

Inspired by Django's excellent i18n framework and the needs of real-world multilingual applications.

## 📚 Resources

- [Documentation](https://github.com/jinto/django-i18n-noprefix#readme)
- [PyPI Package](https://pypi.org/project/django-i18n-noprefix/)
- [Issue Tracker](https://github.com/jinto/django-i18n-noprefix/issues)
- [Example Project](https://github.com/jinto/django-i18n-noprefix/tree/main/example_project)
- [Django i18n Documentation](https://docs.djangoproject.com/en/stable/topics/i18n/)

---

**Need help?** [Open an issue](https://github.com/jinto/django-i18n-noprefix/issues) or check our [example project](https://github.com/jinto/django-i18n-noprefix/tree/main/example_project).

**Like this project?** Give it a ⭐ on [GitHub](https://github.com/jinto/django-i18n-noprefix)!

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "django-i18n-noprefix",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "django, i18n, internationalization, l10n, localization, no-prefix, translation, url",
    "author": "jinto",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/54/61/bf6825d32be833902f56c1b4c613e991d90d8b95ae4bd164985303118cfe/django_i18n_noprefix-0.1.1.tar.gz",
    "platform": null,
    "description": "# django-i18n-noprefix\n\n[![Tests](https://github.com/jinto/django-i18n-noprefix/workflows/Tests/badge.svg)](https://github.com/jinto/django-i18n-noprefix/actions/workflows/test.yml)\n[![Code Quality](https://github.com/jinto/django-i18n-noprefix/workflows/Code%20Quality/badge.svg)](https://github.com/jinto/django-i18n-noprefix/actions/workflows/quality.yml)\n[![Release](https://github.com/jinto/django-i18n-noprefix/workflows/Release/badge.svg)](https://github.com/jinto/django-i18n-noprefix/actions/workflows/release.yml)\n[![codecov](https://codecov.io/gh/jinto/django-i18n-noprefix/branch/main/graph/badge.svg)](https://codecov.io/gh/jinto/django-i18n-noprefix)\n[![PyPI Version](https://img.shields.io/pypi/v/django-i18n-noprefix.svg)](https://pypi.org/project/django-i18n-noprefix/)\n[![Python Support](https://img.shields.io/pypi/pyversions/django-i18n-noprefix.svg)](https://pypi.org/project/django-i18n-noprefix/)\n[![Django Support](https://img.shields.io/badge/Django-4.2%20%7C%205.0%20%7C%205.1-green.svg)](https://www.djangoproject.com/)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jinto/django-i18n-noprefix/blob/main/LICENSE)\n\n**Clean URLs for multilingual Django sites without language prefixes.**\n\nSeamlessly integrate Django's i18n framework while keeping your URLs clean. No more `/en/about/` or `/ko/about/` \u2014 just `/about/` that works in any language.\n\n## \ud83c\udfaf Why django-i18n-noprefix?\n\nDjango's built-in i18n system automatically adds language codes to your URLs (`/en/`, `/ko/`, `/ja/`). While this works, it has drawbacks:\n\n### The Problem with URL Prefixes\n\n| Issue | With Prefixes | With django-i18n-noprefix |\n|-------|--------------|---------------------------|\n| **URLs** | `/en/products/`<br>`/ko/products/` | `/products/` |\n| **SEO** | Duplicate content issues | Single URL per content |\n| **Sharing** | Language-specific links | Universal links |\n| **User Experience** | Exposes technical details | Clean, professional URLs |\n\n### Real-World Example\n\n```python\n# Before: Django default i18n\nhttps://mysite.com/en/products/laptop/    # English\nhttps://mysite.com/ko/products/laptop/    # Korean\nhttps://mysite.com/ja/products/laptop/    # Japanese\n\n# After: With django-i18n-noprefix\nhttps://mysite.com/products/laptop/        # Any language!\n```\n\n## \u2728 Features\n\n- \ud83d\udeab **No URL prefixes** \u2014 Keep URLs clean and professional\n- \ud83d\udd04 **Full Django i18n compatibility** \u2014 Use all Django's i18n features\n- \ud83c\udf6a **Smart language detection** \u2014 Session, cookie, and Accept-Language header support\n- \ud83c\udfa8 **3 language selector styles** \u2014 Dropdown, list, or inline\n- \ud83c\udfaf **Framework agnostic** \u2014 Bootstrap, Tailwind, or vanilla CSS\n- \u26a1 **Zero configuration** \u2014 Works out of the box\n- \ud83d\udd12 **Production ready** \u2014 93% test coverage\n- \ud83d\udce6 **Lightweight** \u2014 No external dependencies except Django\n\n## \ud83d\ude80 Quick Start (5 minutes)\n\n### 1. Install\n\n```bash\npip install django-i18n-noprefix\n```\n\n### 2. Update Settings\n\n```python\n# settings.py\n\nINSTALLED_APPS = [\n    # ...\n    'django_i18n_noprefix',  # Add this\n]\n\nMIDDLEWARE = [\n    # ...\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    # 'django.middleware.locale.LocaleMiddleware',  # Remove this\n    'django_i18n_noprefix.middleware.NoPrefixLocaleMiddleware',  # Add this\n    'django.middleware.common.CommonMiddleware',\n    # ...\n]\n\n# Configure languages\nLANGUAGES = [\n    ('en', 'English'),\n    ('ko', '\ud55c\uad6d\uc5b4'),\n    ('ja', '\u65e5\u672c\u8a9e'),\n]\nLANGUAGE_CODE = 'en'\n```\n\n### 3. Add URL Pattern\n\n```python\n# urls.py\nfrom django.urls import path, include\n\nurlpatterns = [\n    # ...\n    path('i18n/', include('django_i18n_noprefix.urls')),\n]\n```\n\n### 4. Add Language Selector\n\n```django\n<!-- base.html -->\n{% load i18n_noprefix %}\n\n<!DOCTYPE html>\n<html>\n<body>\n    <!-- Add anywhere in your template -->\n    {% language_selector %}\n\n    <!-- Your content -->\n    {% block content %}{% endblock %}\n</body>\n</html>\n```\n\n**That's it!** Your site now supports multiple languages without URL prefixes.\n\n## \ud83d\udccb Detailed Installation\n\n### Using pip\n\n```bash\npip install django-i18n-noprefix\n```\n\n### Using uv\n\n```bash\nuv pip install django-i18n-noprefix\n```\n\n### Development Installation\n\n```bash\ngit clone https://github.com/jinto/django-i18n-noprefix.git\ncd django-i18n-noprefix\npip install -e \".[dev]\"\n```\n\n## \ud83d\udd27 Configuration\n\n### Basic Configuration\n\nThe minimal configuration shown in Quick Start is often sufficient. Here are all available options:\n\n```python\n# settings.py\n\n# Required: Enable Django i18n\nUSE_I18N = True\nUSE_L10N = True  # Django < 5.0\nUSE_TZ = True\n\n# Language Configuration\nLANGUAGES = [\n    ('en', 'English'),\n    ('ko', '\ud55c\uad6d\uc5b4'),\n    ('ja', '\u65e5\u672c\u8a9e'),\n    ('zh', '\u4e2d\u6587'),\n    ('es', 'Espa\u00f1ol'),\n]\n\nLANGUAGE_CODE = 'en'  # Default language\n\n# Optional: Customize cookie name (default: 'django_language')\nLANGUAGE_COOKIE_NAME = 'django_language'\nLANGUAGE_COOKIE_AGE = 365 * 24 * 60 * 60  # 1 year\nLANGUAGE_COOKIE_PATH = '/'\nLANGUAGE_COOKIE_DOMAIN = None\nLANGUAGE_COOKIE_SECURE = False\nLANGUAGE_COOKIE_HTTPONLY = False\nLANGUAGE_COOKIE_SAMESITE = 'Lax'\n```\n\n### Language Detection Priority\n\nLanguages are detected in this order:\n\n1. **URL parameter** (when switching languages)\n2. **Session** (for logged-in users)\n3. **Cookie** (for anonymous users)\n4. **Accept-Language header** (browser preference)\n5. **LANGUAGE_CODE setting** (fallback)\n\n## \ud83d\udcd6 Usage Examples\n\n### Basic Language Selector\n\n```django\n{% load i18n_noprefix %}\n\n<!-- Dropdown style (default) -->\n{% language_selector %}\n\n<!-- List style -->\n{% language_selector style='list' %}\n\n<!-- Inline style -->\n{% language_selector style='inline' %}\n```\n\n### Custom Language Selector\n\n```django\n{% load i18n i18n_noprefix %}\n{% get_current_language as CURRENT_LANGUAGE %}\n{% get_available_languages as LANGUAGES %}\n\n<!-- Custom dropdown -->\n<select onchange=\"location.href=this.value\">\n    {% for lang_code, lang_name in LANGUAGES %}\n        <option value=\"{% switch_language_url lang_code %}\"\n                {% if lang_code == CURRENT_LANGUAGE %}selected{% endif %}>\n            {{ lang_name }}\n        </option>\n    {% endfor %}\n</select>\n\n<!-- Custom buttons -->\n<div class=\"language-buttons\">\n    {% for lang_code, lang_name in LANGUAGES %}\n        <a href=\"{% switch_language_url lang_code %}\"\n           class=\"btn {% if lang_code|is_current_language %}active{% endif %}\">\n            {{ lang_code|upper }}\n        </a>\n    {% endfor %}\n</div>\n```\n\n### Programmatic Language Change\n\n```python\n# views.py\nfrom django.shortcuts import redirect\nfrom django_i18n_noprefix.utils import activate_language\n\ndef my_view(request):\n    # Change language programmatically\n    activate_language(request, 'ko')\n    return redirect('home')\n\n# Or in class-based views\nfrom django.views import View\n\nclass LanguagePreferenceView(View):\n    def post(self, request):\n        language = request.POST.get('language')\n        if language:\n            activate_language(request, language)\n        return redirect(request.META.get('HTTP_REFERER', '/'))\n```\n\n### AJAX Language Switching\n\n```javascript\n// JavaScript\nfunction changeLanguage(langCode) {\n    fetch('/i18n/set-language-ajax/', {\n        method: 'POST',\n        headers: {\n            'Content-Type': 'application/json',\n            'X-CSRFToken': getCookie('csrftoken')\n        },\n        body: JSON.stringify({language: langCode})\n    })\n    .then(response => response.json())\n    .then(data => {\n        if (data.success) {\n            location.reload();\n        }\n    });\n}\n```\n\n### Using with Django Forms\n\n```python\n# forms.py\nfrom django import forms\nfrom django.conf import settings\n\nclass LanguageForm(forms.Form):\n    language = forms.ChoiceField(\n        choices=settings.LANGUAGES,\n        widget=forms.Select(attrs={'class': 'form-control'})\n    )\n\n# views.py\nfrom django.shortcuts import redirect\nfrom django_i18n_noprefix.utils import activate_language\n\ndef change_language_view(request):\n    if request.method == 'POST':\n        form = LanguageForm(request.POST)\n        if form.is_valid():\n            activate_language(request, form.cleaned_data['language'])\n            return redirect('home')\n    else:\n        form = LanguageForm()\n    return render(request, 'change_language.html', {'form': form})\n```\n\n## \ud83d\udd04 Integration with Django i18n\n\nThis package **complements** Django's i18n system. You can use all Django i18n features:\n\n### Using Django's Translation Features\n\n```django\n{% load i18n %}\n\n<!-- Translation tags work normally -->\n{% trans \"Welcome\" %}\n{% blocktrans %}Hello {{ name }}{% endblocktrans %}\n\n<!-- Get current language -->\n{% get_current_language as LANGUAGE_CODE %}\nCurrent language: {{ LANGUAGE_CODE }}\n\n<!-- Get available languages -->\n{% get_available_languages as LANGUAGES %}\n```\n\n### In Python Code\n\n```python\nfrom django.utils.translation import gettext as _\nfrom django.utils.translation import get_language\n\ndef my_view(request):\n    # Django's translation functions work normally\n    message = _(\"Welcome to our site\")\n    current_language = get_language()\n\n    return render(request, 'template.html', {\n        'message': message,\n        'language': current_language\n    })\n```\n\n### Migration from Standard Django i18n\n\nMigrating from Django's default i18n is straightforward:\n\n1. **Remove URL prefixes**:\n   ```python\n   # Old: urls.py\n   from django.conf.urls.i18n import i18n_patterns\n\n   urlpatterns = i18n_patterns(\n       path('about/', views.about),\n       # ...\n   )\n\n   # New: urls.py\n   urlpatterns = [\n       path('about/', views.about),\n       # ...\n   ]\n   ```\n\n2. **Replace middleware** (as shown in Quick Start)\n\n3. **Update language switcher** (use our template tags)\n\nThat's it! All your translations and language files remain unchanged.\n\n## \ud83c\udfa8 Styling Options\n\n### Bootstrap 5\n\n```django\n{% load static %}\n<link href=\"{% static 'i18n_noprefix/css/bootstrap5.css' %}\" rel=\"stylesheet\">\n\n{% language_selector style='dropdown' %}\n```\n\n### Tailwind CSS\n\n```django\n{% load static %}\n<link href=\"{% static 'i18n_noprefix/css/tailwind.css' %}\" rel=\"stylesheet\">\n\n{% language_selector style='inline' %}\n```\n\n### Vanilla CSS\n\n```django\n{% load static %}\n<link href=\"{% static 'i18n_noprefix/css/vanilla.css' %}\" rel=\"stylesheet\">\n\n{% language_selector style='list' %}\n```\n\n### Custom Styling\n\n```css\n/* Override CSS variables */\n:root {\n    --i18n-primary: #007bff;\n    --i18n-primary-hover: #0056b3;\n    --i18n-text: #333;\n    --i18n-bg: #fff;\n    --i18n-border: #ddd;\n    --i18n-radius: 4px;\n}\n\n/* Or write custom CSS */\n.language-selector-dropdown {\n    /* Your styles */\n}\n```\n\n## \ud83d\udcca Django/Python Compatibility Matrix\n\n| Django Version | Python 3.8 | Python 3.9 | Python 3.10 | Python 3.11 | Python 3.12 |\n|---------------|------------|------------|-------------|-------------|-------------|\n| 4.2 LTS       | \u2705         | \u2705         | \u2705          | \u2705          | \u2705          |\n| 5.0           | \u274c         | \u274c         | \u2705          | \u2705          | \u2705          |\n| 5.1           | \u274c         | \u274c         | \u2705          | \u2705          | \u2705          |\n| 5.2           | \u274c         | \u274c         | \u2705          | \u2705          | \u2705          |\n\n## \ud83c\udfae Example Project\n\nSee the package in action with our complete example project:\n\n```bash\n# Clone the repository\ngit clone https://github.com/jinto/django-i18n-noprefix.git\ncd django-i18n-noprefix/example_project\n\n# Install dependencies\npip install django\n\n# Run the demo\n./run_demo.sh\n\n# Visit http://localhost:8000\n```\n\nThe example project includes:\n- Multi-language support (English, Korean, Japanese)\n- All three language selector styles\n- Bootstrap, Tailwind, and Vanilla CSS examples\n- Complete translation files\n- Production-ready configuration\n\n## \ud83d\udd0d API Reference\n\n### Middleware\n\n```python\nclass NoPrefixLocaleMiddleware:\n    \"\"\"\n    Replacement for Django's LocaleMiddleware that doesn't use URL prefixes.\n\n    Methods:\n        get_language(request) -> str: Detect language from request\n        save_language(request, response, language) -> None: Persist language choice\n    \"\"\"\n```\n\n### Template Tags\n\n```django\n{% load i18n_noprefix %}\n\n<!-- Render language selector -->\n{% language_selector [style='dropdown|list|inline'] [next_url='/custom/'] %}\n\n<!-- Get URL for language switch -->\n{% switch_language_url 'ko' [next_url='/custom/'] %}\n\n<!-- Check if language is current -->\n{{ 'ko'|is_current_language }} \u2192 True/False\n```\n\n### Views\n\n```python\n# URL: /i18n/set-language/<lang_code>/\ndef change_language(request, lang_code):\n    \"\"\"Change language and redirect.\"\"\"\n\n# URL: /i18n/set-language-ajax/\ndef set_language_ajax(request):\n    \"\"\"AJAX endpoint for language change.\"\"\"\n```\n\n### Utility Functions\n\n```python\nfrom django_i18n_noprefix.utils import (\n    activate_language,       # Activate language for request\n    get_supported_languages, # Get list of language codes\n    get_language_choices,    # Get language choices for forms\n    is_valid_language,      # Validate language code\n)\n```\n\n## \ud83d\udc1b Troubleshooting\n\n### Common Issues and Solutions\n\n**Language not persisting across requests**\n- Ensure sessions are enabled in `INSTALLED_APPS` and `MIDDLEWARE`\n- Check that cookies are not blocked in the browser\n- Verify `NoPrefixLocaleMiddleware` is after `SessionMiddleware`\n\n**Translations not working**\n```python\n# Check these settings\nUSE_I18N = True\nLOCALE_PATHS = [BASE_DIR / 'locale']\n\n# Run these commands\npython manage.py makemessages -l ko\npython manage.py compilemessages\n```\n\n**Static files not loading**\n```bash\npython manage.py collectstatic\n```\n\n**Middleware order issues**\n```python\n# Correct order\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',  # \u2190 First\n    'django_i18n_noprefix.middleware.NoPrefixLocaleMiddleware',  # \u2190 Second\n    'django.middleware.common.CommonMiddleware',\n    # ...\n]\n```\n\n### Debug Mode\n\n```python\n# Enable debug logging\nLOGGING = {\n    'version': 1,\n    'disable_existing_loggers': False,\n    'handlers': {\n        'console': {\n            'class': 'logging.StreamHandler',\n        },\n    },\n    'loggers': {\n        'django_i18n_noprefix': {\n            'handlers': ['console'],\n            'level': 'DEBUG',\n        },\n    },\n}\n```\n\n## \ud83d\ude80 Performance\n\n- **Zero overhead**: Middleware adds < 0.1ms per request\n- **Smart caching**: Language preference cached in session/cookie\n- **No database queries**: Pure middleware solution\n- **CDN friendly**: No URL prefixes mean better cache utilization\n\n## \ud83d\udd12 Security Considerations\n\n- CSRF protection on language change endpoints\n- XSS safe: All outputs are properly escaped\n- Cookie security: Supports Secure, HttpOnly, and SameSite flags\n- No user input in URL paths\n\n## \ud83e\udd1d Contributing\n\nWe welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n```bash\n# Setup development environment\ngit clone https://github.com/jinto/django-i18n-noprefix.git\ncd django-i18n-noprefix\npip install -e \".[dev]\"\n\n# Run tests\npytest\n\n# Run with coverage\npytest --cov=django_i18n_noprefix\n\n# Code formatting\nblack .\nruff check .\n\n# Type checking\nmypy django_i18n_noprefix\n```\n\n## \ud83d\udcdd Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for release history.\n\n## \ud83d\udcc4 License\n\nMIT License - see [LICENSE](LICENSE) for details.\n\n## \ud83d\ude4f Credits\n\nBuilt with \u2764\ufe0f by [jinto](https://github.com/jinto) and contributors.\n\nInspired by Django's excellent i18n framework and the needs of real-world multilingual applications.\n\n## \ud83d\udcda Resources\n\n- [Documentation](https://github.com/jinto/django-i18n-noprefix#readme)\n- [PyPI Package](https://pypi.org/project/django-i18n-noprefix/)\n- [Issue Tracker](https://github.com/jinto/django-i18n-noprefix/issues)\n- [Example Project](https://github.com/jinto/django-i18n-noprefix/tree/main/example_project)\n- [Django i18n Documentation](https://docs.djangoproject.com/en/stable/topics/i18n/)\n\n---\n\n**Need help?** [Open an issue](https://github.com/jinto/django-i18n-noprefix/issues) or check our [example project](https://github.com/jinto/django-i18n-noprefix/tree/main/example_project).\n\n**Like this project?** Give it a \u2b50 on [GitHub](https://github.com/jinto/django-i18n-noprefix)!\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Django internationalization without URL prefixes",
    "version": "0.1.1",
    "project_urls": {
        "Changelog": "https://github.com/jinto/django-i18n-noprefix/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/jinto/django-i18n-noprefix#readme",
        "Homepage": "https://github.com/jinto/django-i18n-noprefix",
        "Issues": "https://github.com/jinto/django-i18n-noprefix/issues",
        "Repository": "https://github.com/jinto/django-i18n-noprefix.git"
    },
    "split_keywords": [
        "django",
        " i18n",
        " internationalization",
        " l10n",
        " localization",
        " no-prefix",
        " translation",
        " url"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "dddd7a39a2d61be9209c675587c210d384afbfbcfeefb6f01707f1c260ddef52",
                "md5": "99873fd86abaf90580c79bacf55110eb",
                "sha256": "92a4d9bc448dfd0dff16a5927324901690fd0bd997540d92cb0233cbfa932b59"
            },
            "downloads": -1,
            "filename": "django_i18n_noprefix-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "99873fd86abaf90580c79bacf55110eb",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 38354,
            "upload_time": "2025-08-08T05:54:18",
            "upload_time_iso_8601": "2025-08-08T05:54:18.242342Z",
            "url": "https://files.pythonhosted.org/packages/dd/dd/7a39a2d61be9209c675587c210d384afbfbcfeefb6f01707f1c260ddef52/django_i18n_noprefix-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "5461bf6825d32be833902f56c1b4c613e991d90d8b95ae4bd164985303118cfe",
                "md5": "d92e397b35353d1bd1ed5a7c9792ebaf",
                "sha256": "a982afa7ac0621690b4f51964b6c90b5f9256f25e917af2c0eb5f9ed2cc5a951"
            },
            "downloads": -1,
            "filename": "django_i18n_noprefix-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "d92e397b35353d1bd1ed5a7c9792ebaf",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 31237,
            "upload_time": "2025-08-08T05:54:19",
            "upload_time_iso_8601": "2025-08-08T05:54:19.565588Z",
            "url": "https://files.pythonhosted.org/packages/54/61/bf6825d32be833902f56c1b4c613e991d90d8b95ae4bd164985303118cfe/django_i18n_noprefix-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-08 05:54:19",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jinto",
    "github_project": "django-i18n-noprefix",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "django-i18n-noprefix"
}
        
Elapsed time: 1.82961s