We're developing an application which will serve multiple industries. I'm hoping to use django's i18n functionality to accomplish two things:
Translation to different languages (standard).
Translation to different industries (much less standard).
Lets say the application serves vets and car mechanics, you would end up with a matrix of options:
| English | French
----------------------------------
Vets | horse | cheval
----------------------------------
Car Mechanics | car | voiture
I guess I can setup the message files for different contexts pretty easily:
python manage.py makemessages -l fr_vet
etc...
Now how would I go about activating that translation?
I know the industry in middlewhere from the request, could I subclass django.middleware.locale.LocaleMiddleware and alter it, or do I need to subclass django.utils.translation and alter the activate function? Or something completely different?
Appologies if I've missed an existing explanation of how to do this - it was a classic case of "I'm sure the answer must exist but without knowing what it's called I can't google it".
I had the same situation once and I read almost all Django documentation about translations (it's really huge)... I would like to share with you some important links that can help:
Switch language in templates:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#switching-language-in-templates
This one, I think is the most important one for you, "How django discovers language..."
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#how-django-discovers-language-preference
One of the things that I think you are missing are the languages in your settings file, for example:
LANGUAGES = (
('de', _('German')),
('en', _('English')),
)
Lara's answer was useful, but didn't explain it fully, I thought it worthwhile to add a full explanation.
All that's required is to add middleware which sets the translation code for the industry:
from django.middleware.locale import LocaleMiddleware
from django.utils import translation
class CustomLocaleMiddleware(LocaleMiddleware):
def process_request(self, request):
check_path = self.is_language_prefix_patterns_used()
language = translation.get_language_from_request(
request, check_path=check_path)
if hasattr(request, 'user_info'):
language = language[:2] + '-' + request.user_info['trans_code']
translation.activate(language)
request.LANGUAGE_CODE = translation.get_language()
(Note: user_info is added to request in middleware which is called before CustomLocaleMiddleware)
request.user_info['trans_code'] will be set to "ve" for vets and "cm" for car mechanics.
Then in settings:
MIDDLEWARE_CLASSES = (
...
'path.to.middleware.CustomAuthMiddleware', # this adds request.user_info
'path.to.middleware.CustomLocaleMiddleware',
...
)
LANGUAGE_CODE = 'en'
USE_I18N = True
USE_L10N = True
LANGUAGES = (
('en-ve', 'English Vets'),
('en-cm', 'English Car Mechanics'),
('fr-ve', 'French Vets'),
('fr-cm', 'French Car Mechanics'),
)
LOCALE_PATHS = (os.path.join(BASE_DIR,'locale'),)
You can create your .po files with
python manage.py makemessages -l en_VE -l en_CM -l fr_VE -l fr_CM
This has the advantage that it would default to the standard "de" translation if you had it but hadn't yet build "de-ve" or "de-cm".
Related
I'm Korean and using django-all-auth in my Django Project.
I checked that there is Korean .po file in django-all-auth.
But all expressions are english, not Korean.
I just followed Installation part and Configuration part in doc.
And here is my settings.py
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
USE_I18N = True
USE_L10N = True
USE_TZ = True
Did I miss something?
You need to activate the internationalization middleware:
In your settings.py set:
MIDDLEWARE = {...,"django.middleware.locale.LocaleMiddleware",...}
As said in the Doc
Make sure you’ve activated translation for your project (the fastest
way is to check if MIDDLEWARE includes
django.middleware.locale.LocaleMiddleware). If you haven’t yet, see
How Django discovers language preference.
Since now your pages are configured to route to the desired internationalization you now need to provide localization yourself. They will not automatically be translated. You need to handle it yourself.
To do so you will have to mark the elements that need to be translated by using
from django.utils.translation import ugettext as _
Now mark the text that you need to translate by using ugettext as _in the following way:-
class PollPluginPublisher(CMSPluginBase):
model = PollPluginModel # model where plugin data are saved
module = _("Polls")
name = _("Poll Plugin") # name of the plugin in the interface
Now the elements are marked to be translated in our case ("Polls") and ("Poll Plugin").
After dealing with this you can run following command in your root directory:-
django-admin makemessages -l de
Replace the last "de" with your language locale name. What this command does is that it will create the po file which will just store the elements that need to be translated. Be sure that your LOCALE_PATH is set properly.
After this done you can use the following django-packages for translations:-
1) django-rosetta :- https://django-rosetta.readthedocs.io/en/latest/
2) django-modeltranslation:- http://django-modeltranslation.readthedocs.io/en/latest/
For further reference of LOCALIZATION you can view:-
https://docs.djangoproject.com/en/1.10/topics/i18n/translation/#localization-how-to-create-language-files
I am building a Django app that uses Django's translation features to provide localization to multiple languages. But I am also using Django's translation features to translate certain terminology into different industries based on the currently logged in user's settings.
For example, for an English speaking user working in the learning assessment industry, I want the following behavior:
For a given request to a page:
Look up the user's natural language (e.g., German)
Look up the user's industry (e.g., learning assessment)
Activate the German/Learning Assessment language (e.g., translation.activate("learning-assessment-de")
The "learning-assessment-de" .po file will only translate a subset of all the strings in the project, because it's only there to translate certain industry-specific terminology.
This is the question:
When a string is missing, I want Django to fall back to German (determined in step #1 above) rather than English (the default language in my settings.py).
My default English/German .po files will assume a certain industry.
Is this possible?
I think it's possible and one of the fastest ways to do this (even if to test if it works) would be to monkey-patch Django translation module to add fallback language support like this (not tested):
from django.utils.translation import trans_real
... # Import other things
# Added `fallback_language` parameter
def do_translate(message, translation_function, fallback_language=None):
"""
Translates 'message' using the given 'translation_function' name -- which
will be either gettext or ugettext. It uses the current thread to find the
translation object to use. If no current translation is activated, the
message will be run through the default translation object.
"""
global _default
# str() is allowing a bytestring message to remain bytestring on Python 2
eol_message = message.replace(str('\r\n'), str('\n')).replace(str('\r'), str('\n'))
t = getattr(_active, "value", None)
if t is not None:
result = getattr(t, translation_function)(eol_message)
else:
# Use other language as fallback.
if fallback_language is not None:
fallback = translation(fallback_language)
result = getattr(_default, translation_function)(eol_message)
else:
if _default is None:
from django.conf import settings
_default = translation(settings.LANGUAGE_CODE)
result = getattr(_default, translation_function)(eol_message)
if isinstance(message, SafeData):
return mark_safe(result)
return result
# Added `fallback_language` parameter
def do_ntranslate(singular, plural, number, translation_function, fallback_language=None):
global _default
t = getattr(_active, "value", None)
if t is not None:
return getattr(t, translation_function)(singular, plural, number)
# Use other language as fallback.
if fallback_language is not None:
fallback = translation(fallback_language)
return getattr(fallback, translation_function)(singular, plural, number)
if _default is None:
from django.conf import settings
_default = translation(settings.LANGUAGE_CODE)
return getattr(_default, translation_function)(singular, plural, number)
# Override Django functions with custom ones.
trans_real.do_translate = do_translate
trans_real.do_ntranslate = do_ntranslate
The two above functions are taken from the django.utils.translation.trans_real module. Just added few lines to these functions. You will also need to modify the other functions (e.g. gettext, ngettext, pgettext) to accept the fallback_language parameter and pass it to the two above ones. Please try if this code works.
Note, that monkey-patching is project-wide - it affects Your whole application and third-party applications too. Alternatively You may take the source of the django.utils.translation.trans_real module as a base to create custom translation functions that will be used only in few places in your application.
In the project's settings file, if we want to restrict language choices for i18n, we are supposed to write this:
gettext = lambda s: s
LANGUAGES = (
('Fr', gettext('French')),
('en', gettext('English')),
)
but I write this:
LANGUAGES = (
('fr', 'cool dudes'),
('en', 'Anglais')
)
Whatever I put in the second item of the tuples (with "gettext = lambda s: s" or not), Django brings back "Français" and "English" in my language selector in a rendered page… I've also tried to raw language data in that selector's captions:
{'code':'fr', 'name':'French', 'bidi':False, 'name_local':u'Fran\xe7ais'}
{'code':'en', 'name':'English', 'bidi':False, 'name_local':u'English'}
It puzzles me, so what's the point to have 2-items tuples for this setting?
The Django documentation for Settings describes this second value and how to use it.
...With this arrangement, django-admin.py makemessages will still find and mark these strings for translation, but the translation won't happen at runtime -- so you'll have to remember to wrap the languages in the real gettext() in any code that uses LANGUAGES at runtime.
The second string in the two-tuple is intended to be a human-readable rendition of the language name. Wrap the second value in a call to gettext() in order to mark them as translatable strings. But the normal implementation of gettext() isn't available in the settings module, so you have to define a dummy implementation, as your code shows. If you want the language name localised, you do need to wrap the value in the actual gettext() at the point of use.
You don't show your template code for the language selector UI. Are you using get_language_info()? The example the docs give doesn't show it, but it looks to me like you'd need to wrap the name_local element in _(). (But I haven't tested this.)
from django.utils.translation import get_language_info
li = get_language_info('de')
print(li['name'], _(li['name_local']), li['bidi'])
I wanna get absolute path all installed_apps in "setting.py" .
app_list = [ app for app in Myproject.settings.INSTALLED_APPS ]
I list of application but how i can get absolute path all of them.
Thank you.
It's not really possible to do what you want. I mean you can get almost there, but there's no real functionality for this, and in particular, Python doesn't distinguish between classes, methods, and variables. They all count as attributes of the module.
First, INSTALLED_APPS is a tuple of strings. So, in order to get any useful information, you're going to have load them as modules:
for app in settings.INSTALLED_APPS:
module = __import__(app)
Then, once you have the actual module, you can call dir on it to get a list of its attributes:
for app in settings.INSTALLED_APPS:
module = __import__(app)
print dir(module)
And, that's about as far as you can go. It's going to output everything set in the module, not just classes. From here, about the only recourse I can think of to weed out everything but classes is to assume that naming conventions were followed and look for items that start with a capital letter. That's not exactly scientific, but that's all you got.
Today I solved my problem. I write this code and its just working. For limited name class I find name of class that inherent from "models.Model" . you can change it and enjoy that.also this code find files in depth 1 of modules. it can change.
app_list = [app for app in training.settings.INSTALLED_APPS if "task" in app]
for module in app_list:
module1 = __import__(module)
temp = module1.__path__
files_path = [temp[0] + os.sep + files_name for files_name in os.listdir(temp[0]) if
os.path.splitext(files_name)[1] == ".py"]
p = re.compile(r'class\s*\w*\s*\(models.Model\):')
for file in files_path:
infile = open(file)
text = infile.read()
all_class = p.findall(text)
print [class_name[6:][:-15] for class_name in all_class]
At first sorry for my English :).
LANGUAGE_CODE setting doesn't work correctly.
When I configured LANGUAGE_CODE="mn", but default language code is "en".
from django.utils.translation import get_language
print get_language()
>>> en
then I tried to configure LANGUAGES setting
LANGUAGES = (
("mn": "Mongolia"),
("en": "English"),
)
but still "en"
changed LANGUAGES setting
LANGUAGES = (
("mn": "Mongolia"),
("en-us": "English"),
)
now it is "mn"
but want above settings
LANGUAGE_CODE = "mn"
LANGUAGES = (
("mn": "Mongolia"),
("en": "English"),
)
It doesn't work correctly. Is it BUG ? or something else?
I also tried creating "mn", "en" locale.
Hope help me. Thanks.
If you haven't already, be sure to read this specific topic in the Django documentation:
How Django discovers language preference
https://docs.djangoproject.com/en/1.3/topics/i18n/deployment/#how-django-discovers-language-preference
We're not using Django 1.3 yet, but we are using Django with multiple languages.
The first item to check is to verify the Accept-Language HTTP header being sent by your browser. Use Fiddler or Charles Proxy or similar tool to verify. Sounds like your browser may be sending "en-us" as a language preference.
In your settings file, make sure USE_L10N and USE_I18N are set to True.
https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n