在 Django 中检测 views.py 之外的语言时出现问题
Problem detecting a language outside views.py in Django
我有一个 Django 应用程序,它从 json 文件加载国家和州列表,并使用我为此目的创建的自定义翻译词典小工具翻译它们。我没有将 gettext 用于此特定任务,因为 gettext 不适用于来自数据库或文件的数据。
首先是处理 json 文件的文件
from django.utils.translation import gettext_lazy as _, get_language
import importlib
current_lng = get_language()
# importing the appropriate translation file based on current_lng
imp = importlib.import_module("countries.translations.countries_%s" % current_lng)
import json
def readJson(filename):
with open(filename, 'r', encoding="utf8") as fp:
return json.load(fp)
def get_country():
filepath = 'myproj/static/data/countries_states_cities.json'
all_data = readJson(filepath)
all_countries = [('----', _("--- Select a Country ---"))]
for x in all_data:
y = (x['name'], imp.t_countries(x['name']))
all_countries.append(y)
return all_countries
# the files continues with other function but they are not relevant
arr_country = get_country()
我在forms.py
中使用countries_hangler.py
from django import forms
from .models import Address
from django.conf import settings
from .countries_handler import arr_country
from django.utils.translation import gettext_lazy as _
class AddressForm(forms.ModelForm):
# data = []
#def __init__(self, data):
# self.data = data
country = forms.ChoiceField(
choices = arr_country,
required = False,
label=_('Company Country Location'),
widget=forms.Select(attrs={'class':'form-control', 'id': 'id_country'}),
)
def get_state_by_country(self, country):
return return_state_by_country(country)
def get_city_by_state(self, state):
return return_city_by_state(state)
class Meta:
model = Address
fields = ['country']
在 views.py 我有这些行
from django.shortcuts import render
from django.http import HttpResponseRedirect, JsonResponse
from .forms import AddressForm
import json
from django.utils import translation
from django.utils.translation import gettext_lazy as _, get_language
def load_form(request):
form = AddressForm
# the lang variable is only passed for testing
return render(request, 'countries/country_form.html', {'form':form, 'lang':get_language()})
settings.py配置正确,这里是相关部分
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# ...
LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LANGUAGES = [
('en', 'English'),
('fr', 'French'),
]
我的 urls.py 定义也正确
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls),
path('countries/', include('countries.urls')),
prefix_default_language=False
)
现在,当我这样更改语言时:
http://127.0.0.1:8000/fr/countries/
countries_handler.py 中的 get_language 函数仍然是 returns 'en' 而不是 'fr'。结果是从未加载法语的翻译文件:
# countries/translations/countries_fr.py
# here's my little custom translation tool
def t_countries(c):
country_dict = {
"Albania" : "Albanie",
"Algeria" : "Algérie",
"Antigua and Barbuda" : "Antigua-et-Barbuda",
"Argentina" : "Argentine",
"Armenia" : "Arménie",
# ...
}
if c in country_dict:
return country_dict[c]
return c
顺便说一下,如果我在 forms.py 或 countries_handler.py 中添加以下行:
from django.utils import translation
然后
translation.activate('fr')
那么语言变化会被很好地检测到。
现在我真正知道了问题的原因,唯一无法解决的问题。问题的原因是:
在 forms.py 中(通过 languages_handler.py)get_language() 未检测到从 url 更改的语言,因为 forms.py 构建了表单当开发服务器启动时,而不是在请求 url 之后。
结果是 get_language 只有 returns views.py 中的正确语言,而不是 forms.py 中的正确语言。当然,我尝试通过以下方式在视图中而不是在表单中加载表单数据
# views.py
from .countries_handler import arr_country
# .....
def load_form(request):
form = AddressForm(arr_country)
return render(request, 'countries/country_form.html', {'form':form, 'lang':get_language()})
然后在表格中:
class AddressForm(forms.ModelForm):
data = []
def __init__(self, data):
self.data = data
country = forms.ChoiceField(
choices = data,
required = False,
label=_('Company Country Location'),
widget=forms.Select(attrs={'class':'form-control', 'id': 'id_country'}),
)
但这不起作用。在这种情况下,表单甚至不会显示!!!
那么,我该怎么做才能解决这个问题呢?有没有办法从 url 检测用户的当前语言或形成 cookie,然后将数据发送到表单并在运行时动态构建它?
我需要在 url 中更改语言时正确加载国家列表。
终于自己找到了解决办法,分享一下。首先注意:
Forms.py和countries_handler.py在服务器启动时是运行,而不是在发生http请求时。因此,来自浏览器的任何更改,例如用户在 url 中更改语言,都必须在 views.py 中处理,在 urls.py 中的路由命中它的确切位置。这里是正确的 views.py:
# ...
from .countries_handler import get_country, return_state_by_country
from django.utils.translation import gettext_lazy as _, get_language
# ...
def load_form(request):
# load_form is called by urls.py, therefore it respond to a change made in the url, at run time.
# get_language must be called here to get a correct result.
arr_country = get_country(get_language())
form = AddressForm(arr_country)
return render(request, 'countries/country_form.html', {'form':form, 'lang':get_language()})
这里调用了countries_handler.py中的get_country。 get_language 正确提供当前选择的语言。
这是forms.py:
class AddressForm(forms.ModelForm):
""" Using an empty list as a default argument is a common error.
It may lead to unwanted behavior. The correct way to do it is to
initialize the list to None """
def __init__(self, data=None, *args, **kwargs):
super(AddressForm, self).__init__(*args, **kwargs)
if data is not None:
self.fields['country'].choices = data
country = forms.ChoiceField(
choices = (),
required = False,
label=_('Company Country Location'),
widget=forms.Select(attrs={'class':'form-control', 'id': 'id_country'}),
)
def get_state_by_country(self, country):
return return_state_by_country(country)
def get_city_by_state(self, state):
return return_city_by_state(state)
class Meta:
model = Address
fields = ['country']
请注意我是如何直接从构造函数中填充选择字段的。数据被声明为可选的,因为当我使用相同的 class 调用 return_state_by_country(country) 时,例如,我没有任何数据要传递。
也就是。我没有 post 项目的所有文件以避免冗长。但最重要的是在这个答案中。我在那里学习了如何在 运行 时间将数据动态传递给表单构造函数。其余不变
我有一个 Django 应用程序,它从 json 文件加载国家和州列表,并使用我为此目的创建的自定义翻译词典小工具翻译它们。我没有将 gettext 用于此特定任务,因为 gettext 不适用于来自数据库或文件的数据。
首先是处理 json 文件的文件
from django.utils.translation import gettext_lazy as _, get_language
import importlib
current_lng = get_language()
# importing the appropriate translation file based on current_lng
imp = importlib.import_module("countries.translations.countries_%s" % current_lng)
import json
def readJson(filename):
with open(filename, 'r', encoding="utf8") as fp:
return json.load(fp)
def get_country():
filepath = 'myproj/static/data/countries_states_cities.json'
all_data = readJson(filepath)
all_countries = [('----', _("--- Select a Country ---"))]
for x in all_data:
y = (x['name'], imp.t_countries(x['name']))
all_countries.append(y)
return all_countries
# the files continues with other function but they are not relevant
arr_country = get_country()
我在forms.py
中使用countries_hangler.pyfrom django import forms
from .models import Address
from django.conf import settings
from .countries_handler import arr_country
from django.utils.translation import gettext_lazy as _
class AddressForm(forms.ModelForm):
# data = []
#def __init__(self, data):
# self.data = data
country = forms.ChoiceField(
choices = arr_country,
required = False,
label=_('Company Country Location'),
widget=forms.Select(attrs={'class':'form-control', 'id': 'id_country'}),
)
def get_state_by_country(self, country):
return return_state_by_country(country)
def get_city_by_state(self, state):
return return_city_by_state(state)
class Meta:
model = Address
fields = ['country']
在 views.py 我有这些行
from django.shortcuts import render
from django.http import HttpResponseRedirect, JsonResponse
from .forms import AddressForm
import json
from django.utils import translation
from django.utils.translation import gettext_lazy as _, get_language
def load_form(request):
form = AddressForm
# the lang variable is only passed for testing
return render(request, 'countries/country_form.html', {'form':form, 'lang':get_language()})
settings.py配置正确,这里是相关部分
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# ...
LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LANGUAGES = [
('en', 'English'),
('fr', 'French'),
]
我的 urls.py 定义也正确
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls),
path('countries/', include('countries.urls')),
prefix_default_language=False
)
现在,当我这样更改语言时:
http://127.0.0.1:8000/fr/countries/
countries_handler.py 中的 get_language 函数仍然是 returns 'en' 而不是 'fr'。结果是从未加载法语的翻译文件:
# countries/translations/countries_fr.py
# here's my little custom translation tool
def t_countries(c):
country_dict = {
"Albania" : "Albanie",
"Algeria" : "Algérie",
"Antigua and Barbuda" : "Antigua-et-Barbuda",
"Argentina" : "Argentine",
"Armenia" : "Arménie",
# ...
}
if c in country_dict:
return country_dict[c]
return c
顺便说一下,如果我在 forms.py 或 countries_handler.py 中添加以下行:
from django.utils import translation
然后
translation.activate('fr')
那么语言变化会被很好地检测到。
现在我真正知道了问题的原因,唯一无法解决的问题。问题的原因是:
在 forms.py 中(通过 languages_handler.py)get_language() 未检测到从 url 更改的语言,因为 forms.py 构建了表单当开发服务器启动时,而不是在请求 url 之后。
结果是 get_language 只有 returns views.py 中的正确语言,而不是 forms.py 中的正确语言。当然,我尝试通过以下方式在视图中而不是在表单中加载表单数据
# views.py
from .countries_handler import arr_country
# .....
def load_form(request):
form = AddressForm(arr_country)
return render(request, 'countries/country_form.html', {'form':form, 'lang':get_language()})
然后在表格中:
class AddressForm(forms.ModelForm):
data = []
def __init__(self, data):
self.data = data
country = forms.ChoiceField(
choices = data,
required = False,
label=_('Company Country Location'),
widget=forms.Select(attrs={'class':'form-control', 'id': 'id_country'}),
)
但这不起作用。在这种情况下,表单甚至不会显示!!!
那么,我该怎么做才能解决这个问题呢?有没有办法从 url 检测用户的当前语言或形成 cookie,然后将数据发送到表单并在运行时动态构建它?
我需要在 url 中更改语言时正确加载国家列表。
终于自己找到了解决办法,分享一下。首先注意:
Forms.py和countries_handler.py在服务器启动时是运行,而不是在发生http请求时。因此,来自浏览器的任何更改,例如用户在 url 中更改语言,都必须在 views.py 中处理,在 urls.py 中的路由命中它的确切位置。这里是正确的 views.py:
# ...
from .countries_handler import get_country, return_state_by_country
from django.utils.translation import gettext_lazy as _, get_language
# ...
def load_form(request):
# load_form is called by urls.py, therefore it respond to a change made in the url, at run time.
# get_language must be called here to get a correct result.
arr_country = get_country(get_language())
form = AddressForm(arr_country)
return render(request, 'countries/country_form.html', {'form':form, 'lang':get_language()})
这里调用了countries_handler.py中的get_country。 get_language 正确提供当前选择的语言。
这是forms.py:
class AddressForm(forms.ModelForm):
""" Using an empty list as a default argument is a common error.
It may lead to unwanted behavior. The correct way to do it is to
initialize the list to None """
def __init__(self, data=None, *args, **kwargs):
super(AddressForm, self).__init__(*args, **kwargs)
if data is not None:
self.fields['country'].choices = data
country = forms.ChoiceField(
choices = (),
required = False,
label=_('Company Country Location'),
widget=forms.Select(attrs={'class':'form-control', 'id': 'id_country'}),
)
def get_state_by_country(self, country):
return return_state_by_country(country)
def get_city_by_state(self, state):
return return_city_by_state(state)
class Meta:
model = Address
fields = ['country']
请注意我是如何直接从构造函数中填充选择字段的。数据被声明为可选的,因为当我使用相同的 class 调用 return_state_by_country(country) 时,例如,我没有任何数据要传递。
也就是。我没有 post 项目的所有文件以避免冗长。但最重要的是在这个答案中。我在那里学习了如何在 运行 时间将数据动态传递给表单构造函数。其余不变