I've been working that new-feature ticket starting with an idea Jannis
Leidel had posted in a GitHb [0]Gist some months ago and would like to
get some feedback from developers with real i18n experience about this
first iteration of a [1]patch for it.
Solving this would allow Django to support in a better way translation
of model names to a wider range of locales and could help with ticket
[2]#14844 by providing a base infrastructure for fixing it.
A brief description of the user-facing part of the proposed
implementation:
NOTE: The names are up for replacement in case they are too ugly too
generic or too prone to clash with existing code. I myself have renamed
them twice already (fortunately it is mostly a mass search and replace
operation).
It is composed of two parts:
Part 1:
=======
A new, optional
@classmethod
def verbose_names(cls, count=1)
that can be implemented by the programmer in her model. It is in charge
of returning the verbose name corresponding to /count/ model instances.
This means:
* The now deprecated Meta.verbose_name and Meta.verbose_name_plural
options have been promoted from the Meta inner class to the model
scope itself.
* Singular and plural forms treatment is unified by delegating in this
single method the job of returning them based on the value of
the /count/ parameter. Plural forms aren't limited to two (singular
case plus one plural form) as in English and most western European
languages.
Simple example for audience group #1: Authors of apps whose users use
and will always use one locale (e.g. English) so they set USE_I18N=False
and don't need to care for i18n::
1 class DjangoMascot(models.Model):
2 ...
3
4 @classmethod
5 def verbose_names(cls, count=1):
6 if count == 1:
7 return 'flying pony'
8 else:
9 return 'flying ponies'
verbose_names() can ignore one or more values of /count/ and implement
returning a valid verbose name only for certain values of such argument.
In that case Django will make sure default fallback values are provided
to consumers (see description of the public API in Part 2 and backward
compatibility sections below), e.g.::
1 class Poll(models.Model):
2 ...
3
4 # Developer didn't provide a verbose_names() classmethod.
5 # Django provided fallbacks means 'poll' and 'polls'
6 # will be returned when Poll.get_verbose_name(1) and
7 # Poll.get_verbose_name(<any values different from 1>) are
8 # called, respectively.
Other example::
1 class FirewallPolicy(models.Model):
2 ...
3
4 @classmethod
5 def verbose_names(cls, count=1):
6 if count != 1:
7 return 'firewall policies'
8 # Django provided fallback means 'firewall policy'
9 # will be returned when FirewallPolicy.get_verbose_name(1)
10 # is called
Example for audience group #2: Authors of i18n-aware apps and that
because of that use USE_I18N=True and functions from
django.utils.translation:
What currently is expressed in this fashion::
1 from django.utils.translation import ugettext_lazy as _
2
3 class Library(models.Model):
4 city_name = models.CharField(max_length=30)
5
6 class Meta
7 verbose_name = _('llbrary')
8 verbose_name_plural = _('llbraries')
Will need to be converted to this::
1 from django.utils.translation import ungettext_lazy
2
3 class Library(models.Model):
4 city_name = models.CharField(max_length=30)
5
6 @classmethod
7 def verbose_names(cls, count=1):
8 return ungettext_lazy('llbrary', 'libraries', count)
The fact that we can use ungettext_lazy() means that the gettext PO
catalog entries for this model will change from::
#: foo/models.py:7
msgid "library"
msgstr "<translation of 'library'>"
#: foo/models.py:8
msgid "libraries"
msgstr "<translation of 'libraries'>"
To this::
#: foo/models.py:8
msgid "library"
msgid_plural "libraries"
msgstr[0] "<translation of 'library' with plural form #1>"
...
msgstr[n] "<translation of 'library' with plural form #n+1>"
This is semantically more complete and will allow locales with more than
two plural forms to have better translations of phrases that involve
arbitrary quantities of Django models. E.g. Polish (pl) has three plural
forms and Irish (ga) has five.
Part 2:
=======
A django.db.Model.get_verbose_name(cls, count=1): classmethod that
provides a published API to obtain different flavors of verbose names of
the model at hand.
It is always available so it is always possible to get verbose
names of any model. This method isolates code that needs to get human
readable representations of model names from the particularities of
the verbose_names() model method described in Part 1, if any.
This method will seldomly be overridden by developers if at all. It's
implemented in the base Model class and contains the logic for backward
compatibility and to cope with the possible behaviors of user-provided
verbose_names().
All internal uses in Django itself have been converted to use this API.
Previously this was implemented by reading
<Model class>._meta.verbose_name and
<Model class>._meta.verbose_name_plural
Backward compatibility
----------------------
Backward compatibility has been implemented in a way such that:
o The presence of a verbose_names() classmethod in a model definition
has precedence over the Meta.verbose_name and
Meta.verbose_name_plural options
o Also, these options get their values from internal calls to this
method to preserve compatibility with other apps/code that will keep
using them during a transition period.
o The fallback get_verbose_name() return values in case the developer
doesn't implement verbose_names() (or implements it partially) in a
given model are the same Django has us accustomed to when we didn't
specify Meta.verbose_name and/or Meta.verbose_name_plural i.e.:
* CamelCase -> 'camel case' transformation for the singular case
* Singular verbose name + 's' for the plural case.
Deprecation process
-------------------
As implemented in the patch, currently PendingDeprecationWarning is
raised (one needs to specify the -Wall command line switch to the Python
interpreter to see them) when:
* Meta.verbose_name or Meta.verbose_name_plural are specified
in a model definition
* Model._meta.verbose_name or Model._meta.verbose_name_plural
are accessed (read) (implementation note: They've been converted
into properties)
In Django 1.4+1 the warnings will be changed to DeprecationWarning
Starting with Django 1.4+2 Meta.verbose_name and
Meta.verbose_name_plural will be ignored.
(Open) questions/Things that could be changed
---------------------------------------------
* Is it ok to add (class)method's to Model?, there is the possibility
of name clash with equally named existing attributes/method names in
user models.
* Should we make verbose_names() count argument not have a default value
(=1) and so make it mandatory to specify a value of model quantity?
* Why are these classmethods? Because there isn't anything in these
methods that might make them depend on a given model instance
characteristics to do their job and because many uses from other parts
of Django don't have a model instance at hand but rather the model
class.
* Further, maybe actually there is even no need to them be classmethods
and we can make them staticmethods?
* Can we drop the setter in the now properties _meta Option verbose_name
and verbose_name_plural? This would effectively convert them in
read-only options because we never officially supported modification
of these options at runtime.
Patch status
------------
Patch is in good shape, has documentation changes and tests although
I plan to expand them more.
Any kind of feedback is welcome!
0. https://gist.github.com/2000f763e15c260c0666
1. https://code.djangoproject.com/attachment/ticket/11688/11688-verbose_name_plural_evolution-1.diff
2. https://code.djangoproject.com/ticket/14844
Regards,
--
Ramiro Morales
Wonderful! Thanks for making verbose_names's really plural friendly!
--
: Vladimir Macek : http://macek.sandbox.cz : +420 608 978 164
: UNIX && Dev || Training : Python, Django : GPG key 1F059424
--
You received this message because you are subscribed to the Google Groups "Django internationalization and localization" group.
To post to this group, send email to djang...@googlegroups.com.
To unsubscribe from this group, send email to django-i18n...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-i18n?hl=en.
I think we all agree with this, i.e having full phrases being marked up
for translation is better than composing phrases with translated
fragments.
Problem is that the replacement for `Meta.verbose_name` and
`Meta.verbose_name_plural` we finally come up with also needs to:
* Maintain backward compatibility
* Not change verbose names behavior when i18n isn't being used. IMHO
disrupting the workflow native English developers (especially
English-speaking Django core developers) use (and the results
they get) the least possible this is particularly important to
increase the chances of a proposed fix being finally committed.)
* Simply accept the fact that bare human readable model verbose
names are also needed and very useful in many contexts. I believe that
if for some UI work one needs the 'Poll' literal (or its translation)
and instead one gets '1 Poll' it would be surprising to say the
least, not matter what locale is in effect.
So, making the implementation to always execute something like
ungettext('%(count)d poll', '%(count)d polls, count) % {'count': count, }
isn't the right thing to do either. At least with this particular API.
By the way, there is a piece of code in the admin app that after the patch
looks like this (`changecount` is the int variable that contains the number of
model instances involved)::
name = force_unicode(model.get_verbose_name(changecount))
msg = ungettext("%(count)s %(name)s was changed successfully.",
"%(count)s %(name)s were changed successfully.",
changecount) % {'count': changecount,
'name': name}
self.message_user(request, msg)
Here, we aren't using full phrase translation but we still are setting
things up in way so a) Translators can reorder the words, they just need
to keep in mind that the 'name' var will contain the right verbose name
for the plural form in use and b) Final users are shown correct model
verbose names in all locales.
(Having said this, I have been doing some work to see how a fix for
[1]#14844 -- "Getting the ``blocktrans` template tag to select the right
plural model verbose name for every locale" would look like, based on
the work for this ticket and at the same time to validate the proposed
implementation. Will attach my WIP patch to that ticket and post a
follow-up email to this thread with some further findings and thoughts
because they are related to this kind of problems.)
> The advantage is that we provide translators with a complete translation
> string and thus there is no need to do stitching in the template with a
> specific word order in mind. Numeral + noun is not the universally
> acceptable word order [1].
>
> [1] http://wals.info/chapter/89
Wow that links is a very good resource, thanks for sharing it.
> 2. I'm not very clear why we need both verbose_names and get_verbose_name.
> If get_verbose_name is going to be the published interface we probably don't
> need additional class methods, do we?
This is also related to the three constraints I mentioned above.
In fact my initial plan (reflected in the first patch) was to make
get_verbose_name() effectively a published interface but for consumers
that need model verbose name literals.
We need it to always exist because we need that it always returns
something more or less sane (be it what the model developer implemented
with the non-mandatory verbose_names() method or one of the backward
compatibility fallbacks)
(Remember that the developer that creates a model has the freedom to
provide or not a verbose_names() method, and to make it return
values for any subset of the plural forms. This freedom is needed to
mirror the one the developer has when using Meta.verbose_name and
Meta.verbose_name_plural.)
But in further discussion with Jannis Leidel via IRC we decided to not
leave get_verbose_name() at the model level and make it a method of the
still non-public inner `_meta` Options instance (possibly postponing its
publication until `_meta` gets officially documented). So, code that
needs to consume model verbose names need to stop using
`<Model class>._name._verbose_name*` and call `<Model
class>._name.get_verbose_name()`
instead.
I've changed this in a [2]second version of the patch I've just attached
to the ticket.
Thanks for reviewing and commenting.
1. https://code.djangoproject.com/ticket/14844
2. https://code.djangoproject.com/attachment/ticket/11688/11688-verbose_name_plural_evolution-2.diff
--
Ramiro Morales
Problem is that the replacement for `Meta.verbose_name` and
`Meta.verbose_name_plural` we finally come up with also needs to:
* Maintain backward compatibility
* Not change verbose names behavior when i18n isn't being used. IMHO
disrupting the workflow native English developers (especially
English-speaking Django core developers) use (and the results
they get) the least possible this is particularly important to
increase the chances of a proposed fix being finally committed.)
* Simply accept the fact that bare human readable model verbose
names are also needed and very useful in many contexts. I believe that
if for some UI work one needs the 'Poll' literal (or its translation)
and instead one gets '1 Poll' it would be surprising to say the
least, not matter what locale is in effect.
So, making the implementation to always execute something like
isn't the right thing to do either. At least with this particular API.
ungettext('%(count)d poll', '%(count)d polls, count) % {'count': count, }
By the way, there is a piece of code in the admin app that after the patch
looks like this (`changecount` is the int variable that contains the number of
model instances involved)::
name = force_unicode(model.get_verbose_name(changecount))
msg = ungettext("%(count)s %(name)s was changed successfully.",
"%(count)s %(name)s were changed successfully.",
changecount) % {'count': changecount,
'name': name}
self.message_user(request, msg)
Here, we aren't using full phrase translation but we still are setting
things up in way so a) Translators can reorder the words, they just need
to keep in mind that the 'name' var will contain the right verbose name
for the plural form in use and b) Final users are shown correct model
verbose names in all locales.