django: Translation with variables inside - django

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}

Related

django translation ngettext not working with gettext in the same file

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 :)

Is there a better way to assign msgid in django translations?

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"

How to return a lazy translation object with placeholders?

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.

Django - Internationalization won't work

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.

Decoding utf-8 in Django while using unicode_literals in Python 2.7

I'm using Django to manage a Postgres database. I have a value stored in the database representing a city in Spain (Málaga). My Django project uses unicode strings for everything by putting from __future__ import unicode_literals at the beginning of each of the files I created.
I need to pull the city information from the database and send it to another server using an XML request. There is logging in place along the way so that I can observe the flow of data. When I try and log the value for the city I get the following traceback:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 1: ordinal not in range(128)
Here is the code I use to log the values I'm passing.
def createXML(self, dict):
"""
.. method:: createXML()
Create a single-depth XML string based on a set of tuples
:param dict: Set of tuples (simple dictionary)
"""
xml_string = ''
for key in dict:
self.logfile.write('\nkey = {0}\n'.format(key))
if (isinstance(dict[key], basestring)):
self.logfile.write('basestring\n')
self.logfile.write('value = {0}\n\n'.format(dict[key].decode('utf-8')))
else:
self.logfile.write('value = {0}\n\n'.format(dict[key]))
xml_string += '<{0}>{1}</{0}>'.format(key, dict[key])
return xml_string
I'm basically saving all the information I have in a simple dictionary and using this function to generate an XML formatted string - this is beyond the scope of this question.
The error I am getting had me wondering what was actually being saved in the database. I have verified the value is utf-8 encoded. I created a simple script to extract the value from the database, decode it and print it to the screen.
from __future__ import unicode_literals
import psycopg2
# Establish the database connection
try:
db = psycopg2.connect("dbname = 'dbname' \
user = 'user' \
host = 'IP Address' \
password = 'password'")
cur = db.cursor()
except:
print "Unable to connect to the database."
# Get database info if any is available
command = "SELECT state FROM table WHERE id = 'my_id'"
cur.execute(command)
results = cur.fetchall()
state = results[0][0]
print "my state is {0}".format(state.decode('utf-8'))
Result: my state is Málaga
In Django I'm doing the following to create the HTTP request:
## Create the header
http_header = "POST {0} HTTP/1.0\nHost: {1}\nContent-Type: text/xml\nAuthorization: Basic {2}\nContent-Length: {3}\n\n"
req = http_header.format(service, host, auth, len(self.xml_string)) + self.xml_string
Can anyone help me correct the problem so that I can write this information to the database and be able to create the req string to send to the other server?
Am I getting this error as a result of how Django is handling this? If so, what is Django doing? Or, what am I telling Django to do that is causing this?
EDIT1:
I've tried to use Django's django.utils.encoding on this state value as well. I read a little from saltycrane about a possible hiccup Djano might have with unicode/utf-8 stuff.
I tried to modify my logging to use the smart_str functionality.
def createXML(self, dict):
"""
.. method:: createXML()
Create a single-depth XML string based on a set of tuples
:param dict: Set of tuples (simple dictionary)
"""
xml_string = ''
for key in dict:
if (isinstance(dict[key], basestring)):
if (key == 'v1:State'):
var_str = smart_str(dict[key])
for index in range(0, len(var_str)):
var = bin(ord(var_str[index]))
self.logfile.write(var)
self.logfile.write('\n')
self.logfile.write('{0}\n'.format(var_str))
xml_string += '<{0}>{1}</{0}>'.format(key, dict[key])
return xml_string
I'm able to write the correct value to the log doing this but I narrowed down another possible problem with the .format() string functionality in Python. Of course my Google search of python format unicode had the first result as Issue 7300, which states that this is a known "issue" with Python 2.7.
Now, from another stackoverflow post I found a "solution" that does not work in Django with the smart_str functionality (or at least I've been unable to get them to work together).
I'm going to continue digging around and see if I can't find the underlying problem - or at least a work-around.
EDIT2:
I found a work-around by simply concatenating strings rather than using the .format() functionality. I don't like this "solution" - it's ugly, but it got the job done.
def createXML(self, dict):
"""
.. method:: createXML()
Create a single-depth XML string based on a set of tuples
:param dict: Set of tuples (simple dictionary)
"""
xml_string = ''
for key in dict:
xml_string += '<{0}>'.format(key)
if (isinstance(dict[key], basestring)):
xml_string += smart_str(dict[key])
else:
xml_string += str(dict[key])
xml_string += '<{0}>'.format(key)
return xml_string
I'm going to leave this question unanswered as I'd love to find a solution that lets me use .format() the way it was intended.
This is correct approach (problem was with opening file. With UTF-8 You MUST use codecs.open() :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import codecs
class Writer(object):
logfile = codecs.open("test.log", "w", 'utf-8')
def createXML(self, dict):
xml_string = ''
for key, value in dict.iteritems():
self.logfile.write(u'\nkey = {0}\n'.format(key))
if (isinstance(value, basestring)):
self.logfile.write(u'basestring\n')
self.logfile.write(u'value = {0}\n\n'.format( value))
else:
self.logfile.write(u'value = {0}\n\n'.format( value ))
xml_string += u'<{0}>{1}</{0}>'.format(key, value )
return xml_string
And this is from python console:
In [1]: from test import Writer
In [2]: d = { 'a' : u'Zażółć gęślą jaźń', 'b' : u'Och ja Ci zażółcę' }
In [3]: w = Writer()
In [4]: w.createXML(d)
Out[4]: u'<a>Za\u017c\xf3\u0142\u0107 g\u0119\u015bl\u0105 ja\u017a\u0144</a><b>Och ja Ci za\u017c\xf3\u0142c\u0119</b>'
And this is test.log file:
key = a
basestring
value = Zażółć gęślą jaźń
key = b
basestring
value = Och ja Ci zażółcę