Django: mixed translations issue - django

I configured my application to support 2 languages: english and italian.
In my settings.py I specified i18n-related stuff as following:
USE_I18N = True
USE_L10N = True
USE_TZ = True
LANGUAGES = (
('en', 'ENGLISH'),
('it', 'ITALIAN'),
)
then I created a "locale" folder for each application and used the following commands to generate .po/.mo files:
django-admin.py makemessages -l en --no-location --no-obsolete
django-admin.py makemessages -l it --no-location --no-obsolete
django-admin.py compilemessages
All works fine, but I have a template where the two translations get mixed up (ie: part of the texts are in italian and other in english). I think that the problem is related to how my browser (Chrome) sends language headers (currently: Accept-Language: it,en-US;q=0.8,en;q=0.6).
Other browser on my machine like Firefox are sending: Accept-Language: en-US,en;q=0.5
Ok, this may be a personal problem related to a "strange browser configuration", but is not acceptable to have such result... how can I avoid this issue and have a coherent translation?

Is it possible that there is some content with origin from DB and thus not provided with translations?
On the other hand it may be the problem in rendering multiline fields in your .po files or .html files (make sure everything is {% trans ... %} wrapped.
Lastly if in Chrome you ask for translation in English when you are in the Italian site, does it provide you with all English content and vice verso?

Related

French translation raises "ValueError('invalid token in plural form: %s' % value)"

I want to handle a french version of my website.
I use Django 2.2 with i18n and I already set locale variables in settings.py.
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGES = (
('en', _('English')),
('fr', _('French')),
('it', _('Italian')),
('es', _('Spanish')),
)
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
TIME_ZONE = 'Europe/Paris'
USE_I18N = True
USE_L10N = True
USE_TZ = True
When I use ./manage.py makemessages -l fr, I correctly have a django.po french file but after ./manage.py compilemessages -l fr the server crashes with the following error (trimed) :
File "/usr/lib/python3.7/gettext.py", line 93, in _tokenize
raise ValueError('invalid token in plural form: %s' % value)
ValueError: invalid token in plural form: EXPRESSION
English, Italian and Spanish translations work well
EDIT : Well, the issue has been resolved, but I'm not really sure how. I deleted my venv, recreated it and french translation suddenly worked. Upgrading from Django 2.2.1 to 2.2.2 may be what caused the resolution.
For other language facing this error:
There exists a line that tells Django evaluating this expression, decide which form of the word it should use, and for some languages, this expression is not written, e.g. Farsi.
For these languages, a default line is written in your main .po file(not the specific ones):
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
Here the EXPRESSION part should be changed to your language.
HERE you can read the exact format of EXPRESSION, but for short if your language has just two forms for singular and plural form change the line to this:
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
And recompile your messages.
I had the same issue.
The reason was that i accidentally wrote a word in messages.po file in configuration lines (that are on top of .po file, containing "Project-Id-Version", "Plural-Forms", ...)
So i checked what changes i made with my VSC (git) and it was fixed.
Don't forget to recompile your .mo files

django-admin makemessages --no-obsolete doesn't seem to be working

First of all, I am expecting --no-obsolete would comment out msgid and msgstr if gettext is deleted, right?
How I am testing is:
I wrote gettext("some string here") in view
I ran makemessages command
It wrote a .po file as expected
Then I deleted gettext() line from view and saved file, verified runserver working.
I ran makemessages --no-obsolete and it has not made any changes to .po file.
.po file content extract .
#. Translators: This message is a test of wrap line
#: servers/views.py:31
msgid "Do let me know if it works."
msgstr ""
dev environment
Django = 1.11
OS = Mac/Ubuntu 14.04
settings.py
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE = (
os.path.join(os.path.dirname(__file__), "locale"),
)
Now with the help of Julien and Tarun, I found following observations.
python manage.py makemessages -l <locale>
If there is no gettext in the file being processed, the above command won't write/update .po file. That means if the corresponding .po file earlier had entries for msgstr and msgid, then it won't remove those entries unless file being processed had at least one gettext.
Note: Above behavior is irrespective of --no-obsolete
Now to make the --no-obsolete work as expected we need to follow the steps below.
First thing run python manage.py makemessages -l <locale>, this would write .po file with msgid and msgstr.
Now set msgstr and run python manage.py compilemessages -l <locale>. This command writes .mo file in the same directory as .po file.
Now next time when you run makemessages again (without --no-obsolete), .po and .mo files are compared and missing/deleted gettext are commented in .po file.
And when you run makemessages --no-obsolete, commented entries are removed from the .po file.
E.g
if you have 3 gettext entries, and you run makemessages first time, it would write 3 msgid and 3 msgstr in .po file. Now if you remove all gettext entries, .po file won't be updated after you run makemessages again, but if your keep at least 1 gettext entry in same file and run makemessages again, it would delete all msgid and msgstr for deleted gettext entries.
But if you run compilemessages after makemessages, .mo file is created and then for subsequent makemessages commands .po and .mo files are compared and then msgid and msgstr is commented in .po file for deleted gettext entries.
Then finally when you run makemessages with --no-obsolete option the commented messages from .po files are deleted permanently.
What the --no-obsolete does is to run a command called msgattrib with the --no-obsolete option on the content the po file. A typical case would be you generate your po file with makemessages, you get this:
#: servers/views.py:31
msgid "Do let me know if it works."
msgstr ""
Then you translate:
#: servers/views.py:31
msgid "Do let me know if it works."
msgstr "translation"
Then you remove the gettext entry, it'll still by default keep the translation, but mark it as obsolete.
#: servers/views.py:31
#~msgid "Do let me know if it works."
#~msgstr "translation"
If you set --no-obsolete option, then once your po file is done, it'll run msgattr with no-obsolete option. This will remove lines tagged with #~. See https://linux.die.net/man/1/msgattrib
But, the way makemessages is built, is that this will be called once the po file is written. But if there are no gettext in the files being processed, then it won't write to the po file. It'll just stop before getting to this msgattrib command. The po file you see is the one generated by the previous makemessages command. So the no-obsolete won't do anything.
There's no real solution to this. the no-obsolete option doesn't deal with the cases where you don't have any gettext to process.
So I think #JulienGrégoire was right about the fact that if there is no translation processed then the --no-obsolete won't work. There needs to be at least one translation captured for --no-obsolete to work.
But the solution to this quite simple. You can update your settings.py to define LANGUAGES like below
from django.utils.translation import ugettext_lazy as _
LANGUAGES = (
('en', _('English')),
('fr', _('French')),
)
Now your settings will always generate a translation. So it will make sure that you get --no-obsolete working every time you use it

django-admin.py makemessages creates no files

I'm trying to enable translations for a django project and django-admin.py makemessages -l de doesn't seem to create any .po files, although there is a couple of {% trans ... %} in templates and a couple of gettext(...) in models for tests.
Accorgin to the documentation https://docs.djangoproject.com/en/1.7/ref/django-admin/#django-admin-makemessages, the command should search for translations in the whole project tree and create corresponding files in e.g. conf/locale directory if no setting is specified.
The only output I get is processing locale ru.
Any way to debug it or maybe well known pitfalls that I didn't find in google?
Let me answer my own question :)
The problem was that I:
Didn't import gettext as _ but did import gettext as t, thus makemessage didn't recognize translated strings in .py files
Tried to translate non-existing variables in templates instead of strings. {% trans some_var %} instead of {% trans "some_string" %}
Make sure you have the following settings available in your settings.py file:
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
SITE_ROOT = dirname(DJANGO_ROOT)
USE_I18N = True
LOCALE_PATHS = (
SITE_ROOT + '/locale',
)
If the LOCALE_PATHS value is not set it does not know where it should create the locale directories and translation files, also you don't have to have the DJANGO_ROOT and SITE_ROOT values that's just for ease of use.

How to use django-compressor with apache?

I'm been using Django Compressor to manage my coffee/less files and its great for development, but I've had some issues to make it work for my production deployment.
My idea is to have apache to host the static files, possibly in another server. I'm setting COMPRESS_OFFLINE = True on the settings.py file.
Then I do the following
python manage.py compress - This populates the CACHE directory in my static directory, where all static files will be collected.
python manage.py collectstatic - This collects static files from all the apps on my project (some of which don't use compressor) into my static directory.
Copy the static directory somewhere to be hosted with apache. And setup apache to serve the files.
Modify the static_url variable in the settings.py file to point to the static server.
If I open any page, I get the following error on my server, this only seems to happen when I have DEBUG = False and COMPRESS_OFFLINE = True on my settings.py file:
TemplateSyntaxError: Caught OfflineGenerationError while rendering:
You have offline compression enabled but key
"777ba26736d046ab043dc151e7e9a060" is missing from offline manifest.
You may need to run "python manage.py compress".
When I check the static/CACHE directory, I confirm what the error says, this is my manifest.json file:
{
"6189b8598993d1cbdbd35d4dfd1a6711": "<script type=\"text/javascript\" src=\"http://192.168.1.123/CACHE/js/2f6ca6616bd6.js\"></script>",
"5c66dbed0e5b766c6e32773cd8585f3c": "<link rel=\"stylesheet\" href=\"http://192.168.1.123/CACHE/css/154d95903951.css\" type=\"text/css\" />"
}
If I delete the CACHE directory and rerun python manage.py compress, I get a new set of ID's both on the error message and the manifest file, but the ID on the error is still missing on the manifest.
So, I guess there are two questions here. Why is it not working? What is the proper way to achieve this?
Thanks.
If you've run compress, and you still get the message
OfflineGenerationError: You have offline compression enabled but key "4971a40e3b459a8cda8287a7f7caa96d" is missing from offline manifest. You may need to run "python manage.py compress"
then it's likely you have dynamic content inside compress tags. Make sure that compress is always the innermost block, and that there are no tags inside the compress block.
I guess you're using django-compressor 1.1.2 which doesn't support static template tag {% static "..." %}.
Try installing the dev version of django-compressor with:
pip install django_compressor==dev
It should solve the problem.
David Wolfe is absolutely right: had to dig throught all the code of mine to get rid of {% trans... etc.
I make it like this:
<script>
window.__enter_email = "{% trans "Enter correct email" %}"
window.__url = "{% url "shop:go" %}"
</script>
{% compress js %}
<script>
$("#bla")..... window.__enter_email ...
</script>
{% endcompress %}
Hope, helps someone!

Empty catalog when internationalizing JavaScript code

I'm trying to set up Internationalization of JavaScript code in my Django application.
My Django app has a locale subdirectory with a properly generated djangojs.po file. The package definition is as follows:
# urls.py
js_info_dict = {
'packages': ('my_project',),
}
./manage.py makemessages worked well as the .po file contains all the to-be-translated strings but no JavaScript string ever gets translated on the website and the catalog is always empty.
I also had some problems with. This is how it works for me:
Add this to yr root urls.py:
js_info_dict = { 'domain': 'djangojs',
'packages': ('YOUR_PROJECT_NAME',), }
urlpatterns = patterns('',
#enable using translation strings in javascript
#source: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#module-django.views.i18n
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
)
In JS files use:
var somevar = gettext('Text to translate');
To compile django translation files: In a shell/terminal run from the project root (where 'apps', 'settings', etc lie):
#for "normal django files" (.py, .html):
django-admin.py makemessages --locale=de
#for javascript files. source: http://stackoverflow.com/a/3571954/268125
django-admin.py makemessages -a -d djangojs --locale=de
#to compile the translation files to machine code
django-admin.py compilemessages --locale=de
i added my_project to INSTALLED APPS in settings.py and that seemed to do the trick