How do I localize js templates in Django - django

I've the problem, template for dynamic JS file is main.js located in TEMPLATE_DIRS. When I do ./manage.py makemessages -a all messages {% trans "MSG" %} are fetched but not from main.js template.
Of course I can rename main.js to main.js.html, is any pretty way?
./manage.py makemessages -d djangojs — dont help.

You can specify extensions in makemessages command (see django docs):
django-admin.py makemessages -d djangojs -l de -e js -e html,txt

Related

Django makemessages to get custom translatable strings

In my Django project I use Vue + vue-gettext for i18n.
To make translatable strings I have the following code in my my .vue files:
<translate>Hello %{name}</translate>
<a href="..." v-translate>Click here</a>
(using translate tags and v-translate attributes)
Is there a way to configure manage.py makemessages to parse this code as well? By default it does not look into .vue files or parse this i18n code format.
Basically I ended up having a Makfile - that extracts messages from vue files and then merges it with django
# On OSX the PATH variable isn't exported unless "SHELL" is also set, see: http://stackoverflow.com/a/25506676
SHELL = /bin/bash
NODE_BINDIR = ./node_modules/.bin
export PATH := $(NODE_BINDIR):$(PATH)
INPUT_DIR = static/js
# Where to write the files generated by this makefile.
OUTPUT_DIR = .
# Available locales for the app.
LOCALES = en nl fr
# Name of the generated .po files for each available locale.
LOCALE_FILES ?= $(patsubst %,$(OUTPUT_DIR)/locale/%/LC_MESSAGES/app.po,$(LOCALES))
GETTEXT_HTML_SOURCES = $(shell find $(INPUT_DIR) -name '*.vue' -o -name '*.html' 2> /dev/null)
GETTEXT_JS_SOURCES = $(shell find $(INPUT_DIR) -name '*.vue' -o -name '*.js')
# Makefile Targets
.PHONY: clean makemessages
clean:
rm -f /tmp/template.pot
makemessages: clean django_makemessages /tmp/template.pot
django_makemessages:
./manage.py makemessages $(patsubst %,-l %,$(LOCALES))
# Create a main .pot template, then generate .po files for each available language.
# Thanx to Systematic: https://github.com/Polyconseil/systematic/blob/866d5a/mk/main.mk#L167-L183
/tmp/template.pot: $(GETTEXT_HTML_SOURCES)
# `dir` is a Makefile built-in expansion function which extracts the directory-part of `$#`.
# `$#` is a Makefile automatic variable: the file name of the target of the rule.
# => `mkdir -p /tmp/`
mkdir -p $(dir $#)
which gettext-extract
# Extract gettext strings from templates files and create a POT dictionary template.
gettext-extract --attribute v-translate --quiet --output $# $(GETTEXT_HTML_SOURCES)
# Extract gettext strings from JavaScript files.
xgettext --language=JavaScript --keyword=npgettext:1c,2,3 \
--from-code=utf-8 --join-existing --no-wrap \
--package-name=$(shell node -e "console.log(require('./package.json').name);") \
--package-version=$(shell node -e "console.log(require('./package.json').version);") \
--output $# $(GETTEXT_JS_SOURCES)
# Generate .po files for each available language.
#for lang in $(LOCALES); do \
export PO_FILE=$(OUTPUT_DIR)/locale/$$lang/LC_MESSAGES/djangojs.po; \
echo "msgmerge --update $$PO_FILE $#"; \
mkdir -p $$(dirname $$PO_FILE); \
[ -f $$PO_FILE ] && msgmerge --lang=$$lang --update --backup=off $$PO_FILE $# || msginit --no-translator --locale=$$lang --input=$# --output-file=$$PO_FILE; \
msgattrib --no-wrap --no-obsolete -o $$PO_FILE $$PO_FILE; \
done;
I don't use vue-gettext since Django provides the JavaScriptCatalog view but I still faced the same issues with makemessages not picking up translation string in templates.
Using Django 4, Vue 3, and gettext 0.21 I made my own makemessages management command that overrides the Django's built-in one
from django.core.management.commands import makemessages
class Command(makemessages.Command):
"""
Overrides the default `makemessages` to add my own configuration & extend the command to recognize `interpolate` keyword in JS source files
"""
def handle(self, *args, **kwargs):
# Disable writing of the string's source file location in the generate po file
kwargs['no_location'] = True
# Ignore node_modules by default
kwargs['ignore_patterns'] = ['node_modules/*']
if kwargs['domain'] == 'djangojs':
# Set the default extensions
kwargs['extensions'] = kwargs['extensions'] or []
kwargs['extensions'].append('.js')
kwargs['extensions'].append('.vue')
self.xgettext_options.append('--keyword=interpolate')
# Set language to C so xgettext can pick up translations in Vue templates
self.xgettext_options.append('--language=C')
return super().handle(*args, **kwargs)
With default JavaScriptCatalog setup I have a js module that I import whenever I want to translate something
// Exports Django's `JavaScriptCatalog` functions dynamically
const djangoTranslationFns = [
'gettext',
'ngettext',
'get_format',
'gettext_noop',
'pgettext',
'npgettext',
'pluralidx',
];
const exports = {};
if (Object.keys(window).includes(djangoTranslationFns[0])) {
// If one of the functions are defined then it's safe to asumme they all are
djangoTranslationFns.forEach((fn) => { exports[fn] = window[fn]; });
// Override the interpolate function so it uses `gettext` by default
exports.interpolate = (fmt, obj, named) => window.interpolate(window.gettext(fmt), obj, named);
}
export default exports;
Then in the template
<script setup>
import i18n from '#/i18n';
</script>
<template>
<h1>{{ i18n.gettext("Welcome") }}</h1>
</template>

Internationalization of static JS in Django

I want to translate a part of the JS in Django.
I've try the command python manage.py makemessages -d djangojs but it take only file in TEMPLATE_DIRS in the settings.py
I've try to set a JS in a template directory, and it work perfectly.
I've the djangojs.po and i can generate the .mo when i compile.
So the question is : How make message in static file?
I've found the same problem
Here
and
Here but no one answer who keep a good architecture.
Please, save me!
My architecture:
myapp
locale
static
myapp
js
try.js
template
myapp
try.html
views.py
urls.py
[...]
PS: Sorry for my english, i'm not native ;)
My error is when i set the STATIC_ROOT in settings.py. In fact this variable say at Django where stock Static when it do a CollectStatic on the server, i used her for say where found my static (Django can find all static whitout informations, it find static on a folder static on the project folder or on the app folder)
Finally :
set this in the urls.py of the pro
js_info_dict = {
'domain': 'djangojs',
'packages': ('app.kanboard',),
}
urlpatterns = patterns('',
[...]
#Internationalization Javascript
url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
)
In the template
<script type="text/javascript" src="{% url 'django.views.i18n.javascript_catalog' %}"></script>
In the JS in static/my_app/my_file.js
document.write(gettext('Ma chaine de caractère a traduire'))
After, we rule this command-line
python manage.py makemessages -d djangojs
Here, djangojs is the domain set in urls.py at the begin (It's a pseudo-convention)
At this time, we have a djangojs.po in the folder locale what we can compile as a standard .po.
Link of the doc : Here
Lik of my ticket where you can find a sample project and explication in english : The ticket
Good luck !

django i18n not working

A python newbie here.
I wanna my website support English and Chinese. So I just follow django book, chapter 19 internationalization. But it seems doesn't work for me, string I hope to be displayed as chinese, still english there. My code and settin is as following.
[settings.py]
LANGUAGE_CODE = 'zh-cn'
USE_I18N = True
USE_L10N = True
LANGUAGES = (
('en', 'English'),
('zh-cn', 'Chinese')
)
TEMPLATE_CONTEXT_PROCESSORS = {
'django.core.context_processors.i18n',
}
MIDDLEWARE_CLASSES = (
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
In my app views.py, I forcely set language code as 'zh-cn' in index
def index( request ):
response= render_to_response( 'index.htm' )
response.set_cookie('django_language','zh-cn')
return response
then I'd hope annother page that will be loaded after index.htm, will display a chinese string.
Annother page is renderred by upload.html
{% load i18n %}
<html>
<head>
{% block head %}
{% endblock %}
</head>
<body>
{% block body %}
<h1>{% trans 'Upload Demo' %}</h1>
{% endblock %}
</body>
</html>
After then, I do
django-admin.py makemessages -l zh-cn -e htm
in my django project folder, and I got django.po at
locale/zh-cn/LC_MESSAGES/django.po
which content is like
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-05-10 18:33+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL#ADDRESS>\n"
"Language-Team: LANGUAGE <LL#li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: uploader/template/base.htm:10
msgid "Upload Demo"
msgstr "上传文件"
Thereafter, I call following command to compile message
django-admin.py compilemessages
I got django.mo file at some folder with django.po
Fistly I access the index page, then I access another page, which has 'Upload Demo' string id. Actually I still see english string there.
And tried debug via printing language code, find that language has been set correctly.
context = RequestContext(request)
print context
translation.activate('zh-cn')
Lastly, I use
gettext locale/zh-cn/LC_MESSAGES/django.mo "Upload Demo"
really got 'Upload Demo'. So I think problem is here.
But why this happen? I really confused. Can any body help me.
Deeply appreciated any comment or help.
gettext locale/zh-cn/LC_MESSAGES/django.mo "Upload Demo"
I think I made a mistake. Above command return a string that is same as string you typed as string ID rather than translated string. In above command, it is "Upload Demo", That is if your change "Upload Demo" in above command as "bla bla", you will "bla bla".
Maybe it's too late, but I bet your problem probably was due to missing LOCALE_PATHS tuple in your project's settings.
After an hour of pulling my hair out, I solved the same problem by simply adding this to the settings.py file in my project:
LOCALE_PATHS = (
'/home/myuser/Projects/Some_project_root/My_django_project_root/locale',
)
And this is how that directory looks:
locale/
├── de
│   └── LC_MESSAGES
│   ├── django.mo
│   └── django.po
├── en
│   └── LC_MESSAGES
│   ├── django.mo
│   └── django.po
└── es
└── LC_MESSAGES
├── django.mo
└── django.po
Your codeblocks are a bit messy so it is quite hard to read it all. But you might want to start with your .mo file. It contains a #, fuzzy annotation. Fuzzy means that the build script was not sure about the translation and therefore requires attention of the translator (=you). Start by checking all #, fuzzy marked translations. If the translation is correct (or after you have corrected the wrong translation) remove the #, fuzzy annotation. Next run compile messages again. This could fix your problem.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-05-10 18:33+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
See also: django fuzzy string translation not showing up
Friendly regards,
Wout
You can see LANGUAGE_CODE is not the same as django_language. I think get_language in utils.py does not handle underscore LANGUAGES correctly. get_language will return zh-cn but it should not be cut into cn. Instead, zh-cn should be converted to zh_CN.
So you should use below code in setting.py
LANGUAGES = (
('en', _('English')),
('zh-cn', _('Simplified Chinese')),
)
and run the following command from terminal
django-admin.py makemessages -l zh_CN
django-admin.py compilemessages
This is working perfectly for me
I believe the issue lies within the makemessages and compilemessages arguments that you are passing. #Vishnu's answer above is correct.
TLDR
Don't use zh-cn for the locale. Use zh_CN.
django-admin.py makemessages -l zh_CN
django-admin.py compilemessages -l zh_CN
Explanation
The "locale name" as defined in the Django Docs is:
A locale name, either a language specification of the form ll or a combined language and country specification of the form ll_CC. Examples: it, de_AT, es, pt_BR. The language part is always in lower case and the country part in upper case. The separator is an underscore.
In the makemessages example (found at the bottom of the makemessages documentation), you will notice the use of the locale name convention:
django-admin makemessages --locale=pt_BR
django-admin makemessages --locale=pt_BR --locale=fr
django-admin makemessages -l pt_BR
django-admin makemessages -l pt_BR -l fr
django-admin makemessages --exclude=pt_BR
django-admin makemessages --exclude=pt_BR --exclude=fr
django-admin makemessages -x pt_BR
django-admin makemessages -x pt_BR -x fr
You will notice the same in the compilemessages example:
django-admin compilemessages --locale=pt_BR
django-admin compilemessages --locale=pt_BR --locale=fr -f
django-admin compilemessages -l pt_BR
django-admin compilemessages -l pt_BR -l fr --use-fuzzy
django-admin compilemessages --exclude=pt_BR
django-admin compilemessages --exclude=pt_BR --exclude=fr
django-admin compilemessages -x pt_BR
django-admin compilemessages -x pt_BR -x fr
I have experienced a similar problem: compilemessagesseems to be working only on locale in the current directory. This was a remedy:
find . -name "locale" | while read VAR1; do CURR="``pwd``";cd $VAR1/..; echo "in $VAR1";django-admin.py compilemessages ; cd $CURR; done`
There is an outstanding problem in this code which is as follow:
In settings.py Make sure django.middleware.locale.LocaleMiddleware in the MIDDLEWARE setting comes after SessionMiddleware and CacheMiddleware and before CommonMiddleware if those other middlewares are used.
# settings.py
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.cache',
# ...
'django.middleware.locale.LocaleMiddleware',
# ...
'django.middleware.common.CommonMiddleware',
]
for more information see: 1 , 2

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

Load Multiple Fixtures at Once

Is there anyway to load one fixture and have it load multiple fixtures?
I'd ideally like to type:
python manage.py loaddata all_fixtures
And have that load all of the data instead of having to type everything. Is this possible?
Using $ python manage.py loaddata myfixtures/*.json would work as Bash will substitute the wildcard to a list of matching filenames.
I have multiple apps on the project directory and have each app with its 'fixtures' directory. So using some bash I can do:
python3 manage.py loaddata */fixtures/*.json
And that expands all of the json files inside of the fixtures directory on each app in my project. You can test it by simply doing:
ls */fixtures/*.json
Why not create a Makefile that pulls in all your fixtures? eg something like:
load_all_fixtures:
./manage.py loaddata path/to/fixtures/foo.json
./manage.py loaddata path/to/fixtures/bar.json
./manage.py loaddata path/to/fixtures/baz.json
And then at the shell prompt, run
make load_all_fixtures
(This kind of approach is also good for executing unit tests for certain apps only and ignoring others, if need be)
This thread shows up among the first results with a Google search "load data from all fixtures" and doesn't mention what IMO is the correct solution for this, ie the solution that allows you to load any fixtures you want without any wildcard tricks nor a single modification of the settings.py file (I also used to do it this way)
Just make your apps' fixtures directories flat (and not the usual Django scheme that e.g. goes app_name/templates/app_name/mytemplate.html), ie app_name/fixtures/myfixture.[json, yaml, xml]
Here's what the django doc says :
For example:
django-admin loaddata foo/bar/mydata.json
would search /fixtures/foo/bar/mydata.json for each installed application, /foo/bar/mydata.json for each directory in FIXTURE_DIRS, and the literal path foo/bar/mydata.json.
What that means is that if you have a fixtures/myfixture.json in all your app directories, you just have to run
./manage.py loaddata myfixture
to load all the fixtures that are located there within your project ... And that's it ! You can even restrict what apps you load fixtures from by using --app or --exclude arguments.
I'll mention that I use my fixtures only to populate my database while doing some development so I don't mind having a flat structure in my 'fixtures' directories ... But even if you use your fixtures for tests it seems like having a flat structure is the Django-esque way to go, and as
that answer suggests, you would reference the fixture from a specific app by just writing something like :
class MyTestCase(TestCase):
fixtures = ['app_name/fixtures/myfixture.json']
My command is this, simple. (django 1.6)
python manage.py loaddata a.json b.json c.json
If you want to have this work on linux and windows you simply could use this for loading all your json-Fixtures:
import os
files = os.listdir('path/to/my/fixtures')
def loaddata(file):
if os.path.splitext(file)[1] == '.json' and file != 'initial_data.json':
print file
os.system("python manage.py loaddata %s" % file)
map(loaddata, files)
Works for me with Django-admin version 3.1.4
python manage.py loaddata json_file_1 json_file_2
My folder structure is like this -
app_name_1
├──fixtures
├────json_file_1.json
├────json_file_2.json
app_name_2
├──fixtures
├────json_file_3.json
Manage.py loaddata will look automatically in certain places, so if you name your fixtures the same in each app, or put all your fixtures in the same folder it can be easy to load them all. If you have many different fixtures, and need a more complex naming schema, you can easily load all your fixtures using find with -exec
find . -name "*.json" -exec manage.py loaddata {} \;
As I state in this [question][2], I also have this in a fabfile. EDIT: use python manage.py if manage.py is not in your VE path.
If your fixtures are located into the same folder, you can simply ls and xargs: ls myfolder | xargs django-admin loaddata.
Example with this structure:
$ tree fixtures/
root_dir/fixtures/
├── 1_users.json
├── 2_articles.json
└── 3_addresses.json
$ ls -d fixtures/* | xargs django-admin loaddata
would do the same as:
$ django-admin loaddata 1_users.json
$ django-admin loaddata 2_articles.json
$ django-admin loaddata 3_addresses.json
After doing a bit of searching, I ended up writing this script. It searches through all directories named "fixtures" for .json files and runs a "python manage.py loaddata {fixture_name}.json". Sometimes ordering matters for foreign key constraints, so it leaves a fixture in the queue if the constraint cannot be resolved.
(Note: It requires the pip package simple_terminal that I wrote. And I set it up to be run by 'python manage.py runscript ', which requires django-extensions.)
# load_fixture.py
#
# A script that searches for all .json files in fixtures directories
# and loads them into the working database. This is meant to be run after
# dropping and recreating a database then running migrations.
#
# Usage: python manage.py runscript load_fixtures
from simple_terminal import Terminal
from django.core.management import call_command
from django.db.utils import IntegrityError
def load_fixture(fixture_location):
# runs command: python manage.py loaddata <fixture_location>
call_command('loaddata', fixture_location)
def run():
with Terminal() as t:
# get all .json files in a fixtures directory
fixture_locations = t.command(
'find . -name *.json | grep fixtures | grep -v env')
while fixture_locations:
# check that every iteration imports are occuring
errors = []
length_before = len(fixture_locations)
for fl in fixture_locations:
try:
# try to load fixture and if loaded remove it from the array
load_fixture(fl)
print("LOADED: {}".format(fl))
fixture_locations.remove(fl)
except IntegrityError as e:
errors.append(str(e))
# if import did not occur this iteration raise exception due to
# missing foreign key reference
length_after = len(fixture_locations)
if length_before == length_after:
raise IntegrityError(' '.join(errors))
This works well for me; it finds all files located in fixtures directories within the the src directory:
python manage.py loaddata \
$(ls -1 src/**/fixtures/* | tr '\n' '\0' | xargs -0 -n 1 basename | tr '\n' ' ')
python manage.py loaddata ./*/fixtures/*.json
This command will look for the folder fixture in all the directory and then it will pick up all the files with json extension and will install the fixtures.
This way you won't have to have just one folder for fixtures instead you can have fixtures on app level and in multiple apps.
It will work with the ideal folder structure like this -
app_name_1
├──fixtures
├────json_file_1.json
├────json_file_2.json
app_name_2
├──fixtures
├────json_file_3.json