I am currently relying on django translations and I am curious to know if there is a better way to pick msgid for translations rather than the usual approach.
For instance:
In order to mark something for translation you have to do the following.
variable_name = _("Some Name")
and Django picks the msgid in the following way
msgid "Some Name"
msgstr "Some Name"
I currently would like to see if there is a way in which I can either pass a key to gettext
_("my string", "my_key")
or
An implementation in which the variable name becomes the msgid automatically when django picks up the variable.
msgid "variable_name"
msgstr "Some Name"
Any idea or suggestion would be really helpful.
The msgid can't be overwritten, it's a source string for translation. But you can use the django.utils.translation.pgettext() function to provide a contextual information:
from django.utils.translation import pgettext
# Use pgettext to specify a custom ID and the original string
print(pgettext("variable_name", "Some Name"))
This will appear in the .po file as:
msgctxt "variable_name"
msgid "Some Name"
msgstr ""
In case your string needs pluralization, there is a django.utils.translation.npgettext() function:
from django.utils.translation import npgettext
def show_items(count):
message = npgettext(
'variable_name',
'You have one apple',
'You have {count} apples',
count
)
print(message.format(count=count))
show_items(1) # Output: You have one apple
show_items(3) # Output: You have 3 apples
This will appear in the .po file as:
msgctxt "variable_name"
msgid "You have one apple"
msgid_plural "You have {count} apples"
Related
this is my setup to generate translations for both singular and plurar text
from django.utils.translations import ngettext as _
from django.utils.translations import gettext
num = 3
my_plural_string = _("{num} apple", "{num} apples", num).format(num=num)
my_single_string = gettext("this is a text")
When using ngettext and gettext in the same file the generated .po file doesn't include the msgid_plural attribute for the first string
#: .\test_app\test_translation.py:10
msgid "{num} apple"
msgstr ""
#: .\test_app\test_translation.py:11
msgid "this is a text"
msgstr ""
I don't understand what is the problem in this code? Do I need to manually add the msgid_plural to each plural form of the strings ?
the weird part about this situation is if you change the imports like this
from django.utils.translation import ngettext
from django.utils.translation import gettext as _
It will work as expected :)
The django gettext translation always displays the translated phrase (English), never my original phrase in the html (Dutch).
Normal translation with {% trans "" %} works well. Only the javascript gives this problem.
I don't have a /locale/nl/, as my default strings are already in Dutch.
I've set the translation up as in documentation (in settings, urls.py and html), and with gettext('string') in Javascript file. The Django catalog in http://localhost:8081/jsi18n/ shows the original Dutch strings:
django.catalog = {
"<p>De sluitdatum is verlopen op ": "<p>The closing date has passed on ",
"<p>De vragenlijst is ": "<p>The questionnaire is ",
"<p>U bent nog niet geaccepteerd voor deze vragenlijst.</p><p>U ontvangt een uitnodiging per email wanneer u bent geaccepteerd!</p>": "<p>You are not yet accepted for this survey.</p><p>You will receive an invitation by email when you are accepted!</p>",
"<p>U heeft de vragenlijst voltooid!</p> <p><small>U kunt uw antwoorden nog aanpassen tot de vragenlijst is gesloten.</small></p>": "<p>You have completed the questionnaire!</p> <p><small>You can change your answers until the survey close date</small></p>",
"Er zijn dubbele emailaddressen gevonden: ": "There are duplicate emailaddresses",
"Volgende": "Next",
"Vorige": "Previous"
};
But these sentences never get shown, in Dutch.
I think you forgot add translated strings in your djangojs.po file (for original default locale. Dutch in this case).
Or maybe you should add javascript-catalog url inside i18n_patterns():
i18n_urlpatterns = i18n_patterns(
url(r'^jsi18n/$', javascript_catalog, js_info_dict, name='javascript-catalog'),
)
urlpatterns += i18n_urlpatterns
Notice that in this case Django returns 2 different js files:
http://localhost:8000/en/jsi18n/
http://localhost:8000/ru/jsi18n/
In my Django v1.6.5 project running on Python v2.7.x, I have a Model that returns its configuration as a string. I need the returned string to be a gettext_lazy object, so I can evaluate it in any language required later.
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _, string_concat
...
class MyModel(models.Model):
key = models.CharField(...)
value = models.CharField(...)
#property
def config_str(self):
return _('some configuration')
This seems to work fine in these scenarios:
Static string: (see above) - works!
String concatenation: return string_concat(self.key, _(' equals '), self.value) - works!
What is not working, is using gettext_lazy with placeholders, a la:
return _('“%(key)s” equals “%(value)s”' % {key: self.key, value: self.value})
or using the .format() mechanism:
return _('“{key}” equals “{value}”').format(key=self.key, value=self.value)
When I do this, my .po file does contain:
#, python-format
msgid "“%(key)s” equals “%(value)s”" or
msgid "«{key}» equals «{value}»"
but even when I populate this Eg.:
msgstr "«%(key)s» est égal à «%(value)s»" or
msgstr "«{key}» est égal à «{value}»"
and I run compilemessages, the translation seems to be ignored. When I translate the promise returned by the model instance, I always get an EN string with the placeholders filled E.g., '“foo” equals “bar”'. Note, I get an EN string even when the first calling context is FR (for example). This tells me that the translations just aren't even occurring. It is my theory that by the time I eval the lazy object, gettext is looking for the literal string "“foo” equals “bar”" (for example) in the translation catalog rather than something with placeholders and named values.
With this in mind, I've also tried wrapping the whole format() in the lazy object like this:
return _('“{key}” equals “{value}”'.format(key=self.key, value=self.value))
But it seems to have made zero difference. =/
I can get by with string_concat() for now, but sometimes, the placeholders will need to be moved around in some translations, so I'd like to figure this out.
I'm beginning to think that one simply cannot use placeholders with gettext_lazy.
NOTE: I have reviewed django: Translation with variables inside, but a) that has no accepted answer and b) he's using gettext, not gettext_lazy.
OK, the solution here is to provide an extra layer of laziness (Thanks, Django core dev: Florian Apolloner AKA “apollo13”).
Here's my modified function that WORKS:
from django.utils import six
from django.utils.functional import lazy
class MyModel(models.Model):
key = models.CharField(...)
value = models.CharField(...)
#property
def configuration_string(self):
def wrapper():
return _('“{key}” equals “{value}”').format(
key=self.key,
value=self.value
)
return lazy(
wrapper,
six.text_type
)
The only thing is, where I use this, I must remember to eval the wrapper function as follows:
from django.utils.encoding import force_text
config = my_model_instance.configuration_string
# NOTE: Evaluate the lazy function wrapper inside the force_text()
config_str = force_text(config())
Now, in my case, I need to support cases where 3rd party devs write the function configuration_string returning either the lazy function wrapper, a lazily evaluated translation string or just a regular string, so I use:
import types
from django.utils.encoding import force_text
from django.functional import Promise
config = my_model_instance.configuration_string
if isinstance(config, types.FunctionType):
config_str = force_text(config())
elif isinstance(config, Promise):
config_str = force_text(config)
else:
config_str = config
Thanks again to Apollo13 for guidance!
I had a very similar problem and found that using gettext_noop instead of gettext_lazy worked for me available since Django 1.11.
I have the following piece of code:
from django.utils.translation import ugettext as _
task = _('You have %s friends') %(c1.task)
// This is translation
#: compositions/views.py:69
#, fuzzy, python-format
msgid "You have %s friends"
msgstr "У вас %s друга"
But for some reason this msgstr does not work...
Maybe try using string placeholders - from the django documentation:
The strings you pass to _() or ugettext() can take placeholders,
specified with Python’s standard named-string interpolation syntax.
Example:
def my_view(request, m, d):
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
return HttpResponse(output)
Applying this to your example, you'd get:
task = _('You have %(num_friends)s friends') % {'num_friends': c1.task}
I have the following line of code which basically returns an error - a translated error - along with a view in a case of a failure:
from django.utils.translation import ugettext_lazy as _
.....
return render_to_response('login.html',{'form': form,'error_message':_("User is not in the group %s" % group_name),'settings':SettingsManager.get()},context_instance=RequestContext(request))
And I created the message files, and compiled them. But the thing is it is only displayed as I wrote it - in English, User is not in the group %s" % group_name. Part of my django.po file:
#: application/views.py:1003
#, python-format
msgid "User is not in the group %s"
msgstr "Kullanıcı %s grubunda değil."
ANy ideas how to solve it? I cannot see the problem.
P.S: All the other translations are working like a charm, but this one is not.
Thanks in advance.
Try
_("User is not in the group %s") % group_name
instead of
_("User is not in the group %s" % group_name)
that way the translation machinery has a constant string to look up.