I am in the process of translating a Django app. I have translatable strings in files of a specific extensions (.vue files, but that's not very important for now).
I have to run the makemessages
command to parse those strings and generate .po files.
The documentation says:
makemessages: Runs over the entire source tree of the current directory and pulls out all strings marked for translation.
Also, the docs states that the default file extensions are: html, txt, py
and gives an example with explicitly defined extentions:
django-admin makemessages --locale=de --extension=html,txt --extension xml
My question is: how is makemessages
supposed to work with non-compiled files like txt
and xml
? How would I mark string for translation in files like that?
I know how to do this in a template or a .py file:
{% trans "Text to be translated" %}
gettext("Text to be translated")
# or
_("Text to be translated")
But what about other extensions? .txt, .xml? ... and eventually .vue?
The makemessages
command searches for files to translate and invokes the (x)gettext
utility to extract the strings marked for translation. It will behave differently depending on whether you tell it to use the django
or djangojs
--domain
.
When using the django
domain, it runs non-.py
files through django.utils.translation.templatize
to "Turn a Django template into something that is understood by xgettext
"). It basically turns the whole file into XXXXX
apart from the parts the lexer determines are relevant to gettext
, which keeps the line numbers intact, etc.
>>> from django.utils.translation import templatize
>>> content = """This is a {% trans "test" %}!
... {# Translators: these comments remain intact for translators #}
... {% blocktrans %}Only applies to --domain=django and non-.py files {% endblocktrans %}
...
... Everything else is {# ignored #}
... {% trans "EOM" %}
... :)"""
>>> print(templatize(content))
XXXX XX X gettext(u'test') X
# Translators: these comments remain intact for translators
gettext(u'Only applies to --domain=django and non-.py files ') SSSS SSSSSSS SS SSSSSSSSSSSSSSS SSS SSSSSSS SSSSS
XXXXXXXXXX XXXX XX
gettext(u'EOM')
XX
>>>
So for most non-Python, non-Django-template files, this will obliterate your translatable content.
When using the djangojs
domain, Django won't perform any such preprocessing on the files. (For versions of gettext
older than 0.18.3
, makemessages
would call django.utils.jslex.prepare_js_for_gettext
, which is somewhat less aggressive and just tweaks any escaping/regex syntax if necessary).
If you run makemessages -a -d djangojs -e "js,vue"
, Django will tell xgettext
to parse your .js
and .vue
files with --language=JavaScript
and a certain number of extra --keyword
configurations to support gettext_noop
, gettext_lazy
, etc (gettext
defaults to a keywordspec
of _, gettext, dgettext:2, dcgettext:2, ngettext:1,2, dngettext:2,3, pgettext:1c,2, dpgettext:2c,3
for JavaScript
). makemessages
will also pass --from-code=UTF-8
and --add-comments=Translators
arguments.
It's then over to gettext
to do the parsing based on those specifications, and its own understanding of the files based on the --language
specified.
Therefore your best bet for translating .txt
, .xml
, etc files with makemessages
is to use the djangojs
domain and see what gettext
picks up based on --language=JavaScript
, so you'd mark your strings as you would for JavaScript.
Or for Jinja2 templates, etc you could use an alternative solution such as Babel's Message Extraction
Or you could even customize makemessages
to pass different parameters to gettext
based on your requirements.
So for your Vue example...
If your .vue
file contains calls to gettext
in javascript code sections (default parsing doesn't seem to pick up on gettext
calls within template attributes, etc) you should find that makemessages
will extract these strings for translation (and compilemessages
will generate the required binary files after the .po
files are edited).
Then in order to see the translations when the code runs, you'll need to use the Django JavaScript Catalog, so ensure you include something like <script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
in your code so that the gettext
etc functions actually exist. (It sounds like you've already got this, but included for the sake of completeness.)
In your .vue
files you could use something like:
<script> // trick to ensure xgettext doesn't omit the code below: </closetag>
data(){
const _ = gettext;
// gettext default JavaScript keywordspec includes "_" shortcut
// and should extract the strings below by default
// -- or you could just use gettext('my string') directly
return {
heading: _('This is my translatable heading'),
button_text: _('Click here'),
}
}
</script>
<template>
<h1>{{ heading }}</h1>
<button type="button">{{ button_text }}</button>
</template>
-- the strings should be translated based on the currently activated language courtesty of Django's translation mechanism.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With