i18n -- let's do it!

61 views
Skip to first unread message

Adrian Holovaty

unread,
Aug 1, 2005, 2:12:31 PM8/1/05
to django-d...@googlegroups.com
Thanks to Nebojša Đorđević for submitting some internationalization
patches. I admit this i18n stuff is completely new to me, although
I've taken some time to read the gettext documentation at
http://www.python.org/doc/current/lib/module-gettext.html and am
excited about adding this as soon as possible.

Nebojša, or somebody else who's knowledgable, can you provide a basic
checklist of all the things we need to do to get i18n up and running?
I've seen your patches, but I'd appreciate a higher-level overview of
all the steps we need to take.

Thanks,
Adrian

Radek Svarz

unread,
Aug 2, 2005, 1:54:43 AM8/2/05
to Django developers
As a tool for translators I suggest poEdit (http://www.poedit.org/). It
is very comfortable editor.

Nebojša Đorđević

unread,
Aug 2, 2005, 3:19:11 AM8/2/05
to django-d...@googlegroups.com
On 1-08-2005, at 20:12, Adrian Holovaty wrote:

> Nebojša, or somebody else who's knowledgable, can you provide a basic
> checklist of all the things we need to do to get i18n up and running?
> I've seen your patches, but I'd appreciate a higher-level overview of
> all the steps we need to take.

Well, I'm long way from knowledgeable getext user, but I can describe
steps I used to make it work:

1. create directory po/ inside django/ - for keeping source
translation files (django_en.po, django_sr.po, django_en_GB.po, etc.)
2. create locale directory locale/.../LC_MESSAGES also inside django/
for compiled files
i.e. for english locale: locale/en/LC_MESSAGES/
3. use attached python script to extract all marked messages from
django sources (uses xgettext from gettext distribution)
4. Make translation for required language
5. compile .po with msgfmt (part of gettext distribution) and put
this in
locale/<locale code>/LC_MESSAGES as django.po
6. add locale to INSTALLED_LOCALES in config

that's all.

In code:
- use _() functions (exported to global namespace from gettext) for
all messages.
- when you have list or dict of messages use something like this
(from gettext manual):

def N_(msg): return msg # gettext marker
messages = [ 'msg1', 'msg2', .. ]

# use message
...
return _(messages[x])


I also have some questions and things I don't (yet) understand about
gettext:

0. Use prefix for messages (like I have done in the patch) or don't?
1. Localize model names, thru verbose_name localization?
2. Get rid of _plural stuff and use gettext.ngettext() for that.
3. How to use multiple domains for user installed apps?
4. Localize template files? Add _("") tag to filters for use in
templates.
I have done this for test, works ok.
5. How to localize messages *inside* database?
- add additional table which holds all translations?
i.e. class Message(meta.Model): .... TextField(...) becomes:
class Message(...): ...
class LocMessage(..): locale field, text field, foreign key
Message
- some other solution?

Well, probably I need help of some who is more knowledgeable of
gettext than me :)

p.s. I have found this nice tutorial for Rails and i18n at http://
manuals.rubyonrails.com/read/chapter/82

makepo.py

Nebojša Đorđević - nesh

unread,
Aug 2, 2005, 7:54:56 AM8/2/05
to django-d...@googlegroups.com
OTOH, maybe we can go for database based solution like some PHP CMS I
seen?

This is, maybe, more user-friendlier way?

Good thing is that users can change messages within admin interface,
no need for external software on the server (for online editing of
translations) and all apps can have own message tables

Bad thing is that this solution requires more changes in django core
than gettext one, and requires adding admin interface for changing
messages, importing and exporting messages from/to external files
(probably import/export to gettext .po format).

For all that to work admin view, validators, etc. must be changed to
use some gettext-like function to get messages, and core must ensure
that all locale tables exists before going into request.

All message access must be cached to reduce load to database.

I have send example implementation in django-users some days ago.

I'm working on booth solutions, because I'm must have user changeable
messages for current project I'm working on.

Ksenia Marasanova

unread,
Aug 2, 2005, 10:46:16 AM8/2/05
to django-d...@googlegroups.com
2005/8/1, Adrian Holovaty <holo...@gmail.com>:

> Nebojša, or somebody else who's knowledgable, can you provide a basic
> checklist of all the things we need to do to get i18n up and running?
> I've seen your patches, but I'd appreciate a higher-level overview of
> all the steps we need to take.

This checklist is huge, but quite a lot of things seem reasonable:
http://developers.sun.com/dev/gadc/i18ntesting/checklists/allquestions/allquestions.html

2005/8/2, Nebojša Đorđević - nesh <ne...@studioquattro.co.yu>:


>
> OTOH, maybe we can go for database based solution like some PHP CMS I
> seen?

-1
It's my first i18n experience, but I think it's better to stick to an
accepted standard here.

--
Ksenia

Moof

unread,
Aug 2, 2005, 11:01:41 AM8/2/05
to django-d...@googlegroups.com
Ksenia Marasanova wrote:
> 2005/8/1, Adrian Holovaty <holo...@gmail.com>:
>
>>Nebojša, or somebody else who's knowledgable, can you provide a basic
>>checklist of all the things we need to do to get i18n up and running?
>>I've seen your patches, but I'd appreciate a higher-level overview of
>>all the steps we need to take.
>
>
> This checklist is huge, but quite a lot of things seem reasonable:
> http://developers.sun.com/dev/gadc/i18ntesting/checklists/allquestions/allquestions.html

A lot of them are l10n-specific rather than i18n, but that's fair enough. It
does sound like a good start. I'll give another look at it this evening and
see if I can generate a few tickets out of that to keep these things tracked
on trac.

We may need to revisit the unicode ticket at some point, though right now
the few tests I made to try and break the ticket again seem to work ok.

Moof
--
Giles Antonio Radford, alias Moof
"Too old to be a chicken and too young to be a dirty old man"
Serving up my ego over at <http://metamoof.net/>

Nebojša Đorđević - nesh

unread,
Aug 2, 2005, 2:30:03 PM8/2/05
to django-d...@googlegroups.com

On 2-08-2005, at 16:46, Ksenia Marasanova wrote:

> -1
> It's my first i18n experience, but I think it's better to stick to an
> accepted standard here.

I agree, after a day of digging through the net and gettext
documentation I think that gettext way is the right way :).

And another question:
Should we go for "full" localization -> use locale python module
to set appropriate locale for dates, numbers and stuff? Is this
possible in mod_python because python documentation says: "setlocale
() is not thread safe on most systems."? I'll must do some tests to
find out how is it works in mod_python environment.


About translation guide, if someone with more english knowledge than
me (thats means everyone :) ) is willing to make some sort of
translators manual I'm willing to help :). Look at http://drupal.org/
node/11130 as a example.

Moof

unread,
Aug 2, 2005, 7:05:29 PM8/2/05
to django-d...@googlegroups.com
Nebojša Đorđević - nesh wrote:

> I agree, after a day of digging through the net and gettext
> documentation I think that gettext way is the right way :).

If and only if it's independent of locale.

> And another question:
> Should we go for "full" localization -> use locale python module to
> set appropriate locale for dates, numbers and stuff? Is this possible
> in mod_python because python documentation says: "setlocale () is not
> thread safe on most systems."? I'll must do some tests to find out how
> is it works in mod_python environment.

-0.7

Web sites could well be multi-locale. We need to have a mechanism to set a
locale per user on the web side of things, both admin-side and client-side.
I know I've run multilingual websites before, it's a pain in the arse as
soon as you get into generated pages.

django-admin.py should probably use the python locale module though.

By "not threadsafe" it means that settign the locale is a global thing. So
you can't have two threads with different locales, and you mgiht end up
changing locale half way though the execution of a locale-dependent method.

Also, from experience, setlocale() is incredibly buggy on Windows.

In summary: Locale is out for web applications, really. We might do well to
make it easy to change locale from within django though, do some magic so
that per-session the strigns are localised ot the right language. This may
mean gettext is not suitable for us out of the box, as last I fiddled with
it, it assumed that it was taking its language from some application-global
language variable.

And really, if we're not planning to make django cope with multilingual
sites, then it's not worth it.

FWIW, this is hard, and also has not been done well yet, to my knowledge.

Moof

unread,
Aug 3, 2005, 6:25:20 AM8/3/05
to django-d...@googlegroups.com
Ksenia Marasanova wrote:

>>OTOH, maybe we can go for database based solution like some PHP CMS I
>>seen?
>
>
> -1
> It's my first i18n experience, but I think it's better to stick to an
> accepted standard here.

Havign had a closer look at the gettext module:

It sucks for what we want to use it for.

I think we may need to end up defining our own subclass of string.

The gettext format is fine, and I suggest we keep to that, but don't use the
standard gettext programming paradigm.

Right now, the "gettext way" is to "install" a locale at startup time, which
then places a function called _ in the module namespace. Then enclose your
internationalised strings in _(). This is an alias for translation.ugettext.
So far so good.

The problem here is that this means all the string are translated at module
loading time. And, more to the point, cannot be re-translated after that. In
other words: you can't change locale once you have translated the strings
once, which is a huge bummer if your'e tryign to display a multi-lingual
website.

I propose creating a new unicode string-like proxy class, which behaves to
all intents and purposes like a unicode string. We'd need to cope sensibly
with things like slicing and string interpolation (%), and so on, returning
proxies that will ultimately do the translation when the class' __unicode__
or __str__ method are called.

In other words: _(u'The book "%(title)s" is currently %(status)s') %
{"title": title, "status": statusstring{status]} will return an object that
has the interpolation dict stored inside it, and will do the interpolation
only when __str__ or __unicode__ is called, and will not actually store that
result.

How do sessions work in Django? This can be very easy to do if I can just
refer to django.sessions.currentSession() or something equally globally
addressable, then we can just store the locale per-session, ultimately
falling back to a default locale set in DJANGO_SETTINGS_MODULE.

Incdentally, to give you an idea of the scope of what we need to do while
internationalising: remove any mentions in internationalised strings of %s,
%d, %f, %r, and so on, and replace them with %(foo)s, %(bar)d and pass in a
dict. This is because word order does vary between languages, and you can't
guarantee that the positional arguments will be in the same position in
other strings. If we want to ditch Python 2.3 compatibility we could use PEP
292 substitutions, but I don't think tht's a good idea, as people will
forget to do it.

<Warning: Unicode rant, feel free to skip if I'm preaching to the converted>

And yes, these strings have to be unicode. No, it can't be an 8-bit string.
It'll just lead to headaches in the long run. People tryign to slice UTF-8
encoded strings will come into heavy difficulties, as well as anyone using
.index() or any one of the various string methods that assume that a
character is one byte long. In European languages there's no guarantee your
glyphs will be one ("e"), two ("é"), or even three bytes long ("€"), let
alone any Chinese or Japanese or Korean glyphs. Leave the 8bit strings where
they should be: at the input and output of the programme. Keep in mind many
CJK users will want to output UTF-16 rather than UTF-8, it makes for smaller
files, for them.

And yes, as many python bloggers have observed recently: Python's unicode
handling sucks. But it's all we've got.
</rant>

Moof - off to do an i18n page in the wiki.

Nebojša Đorđević - nesh

unread,
Aug 3, 2005, 6:48:07 AM8/3/05
to django-d...@googlegroups.com

On 3-08-2005, at 1:05, Moof wrote:

> By "not threadsafe" it means that settign the locale is a global
> thing. So
> you can't have two threads with different locales, and you mgiht
> end up
> changing locale half way though the execution of a locale-dependent
> method.
>
> Also, from experience, setlocale() is incredibly buggy on Windows.

Ok, too bad for that, now we must make some replacement for
formatting date, numbers and stuff.

Nebojša Đorđević - nesh

unread,
Aug 3, 2005, 7:52:09 AM8/3/05
to django-d...@googlegroups.com
On 3-08-2005, at 12:25, Moof wrote:

> Havign had a closer look at the gettext module:
>
> It sucks for what we want to use it for.
>
> I think we may need to end up defining our own subclass of string.
>
> The gettext format is fine, and I suggest we keep to that, but
> don't use the
> standard gettext programming paradigm.
>
> Right now, the "gettext way" is to "install" a locale at startup
> time, which
> then places a function called _ in the module namespace. Then
> enclose your
> internationalised strings in _(). This is an alias for
> translation.ugettext.
> So far so good.
>
> The problem here is that this means all the string are translated
> at module
> loading time. And, more to the point, cannot be re-translated after
> that. In
> other words: you can't change locale once you have translated the
> strings
> once, which is a huge bummer if your'e tryign to display a multi-
> lingual
> website.
>

Changing languages on the fly: http://docs.python.org/lib/node331.html

As I understood python gettext manual strings are translated when _()
are called, not at module load time. And _() is installed into
builtin namespace after .install() is called.

> How do sessions work in Django? This can be very easy to do if I
> can just
> refer to django.sessions.currentSession() or something equally
> globally
> addressable, then we can just store the locale per-session, ultimately
> falling back to a default locale set in DJANGO_SETTINGS_MODULE.
>

Currently I'm storing locale inside of a request object.
Probably I'll also add some template tags for getting localized
messages.

IMHO, the only place which require access to locale is inside
request object, there we call appropriate <locale>.install() to make
selected locale current and after that all _() functions will return
strings translated to the current locale. All messages which are to
be localized are output after a request is initialized.

> Incdentally, to give you an idea of the scope of what we need to do
> while
> internationalising: remove any mentions in internationalised
> strings of %s,
> %d, %f, %r, and so on, and replace them with %(foo)s, %(bar)d and
> pass in a
> dict. This is because word order does vary between languages, and
> you can't
> guarantee that the positional arguments will be in the same
> position in
> other strings. If we want to ditch Python 2.3 compatibility we
> could use PEP
> 292 substitutions, but I don't think tht's a good idea, as people will
> forget to do it.
>

Right now I writing simple python file checker (something like http://
developers.sun.com/dev/gadc/i18ntesting/checklists/appendixa/
chkgetmulti_perl.txt for finding that type of problems (and also
potential candidates for translation).

And because we can't use locale specific date/time/number/...
functions, that also must be added to implementation.

I forgot one thing, JavaScript files! What about them? If any of
messages are located inside JavaScript files we must make some kind
of preprocessor for them.

Moof

unread,
Aug 3, 2005, 8:12:58 AM8/3/05
to django-d...@googlegroups.com
Nebojša Đorđević - nesh wrote:

> Changing languages on the fly: http://docs.python.org/lib/node331.html
>
> As I understood python gettext manual strings are translated when _()
> are called, not at module load time. And _() is installed into builtin
> namespace after .install() is called.

Looking at the code from gettext.py:

def install(self, unicode=False):
import __builtin__
__builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext

def ugettext(self, message):
missing = object()
tmsg = self._catalog.get(message, missing)
if tmsg is missing:
if self._fallback:
return self._fallback.ugettext(message)
return unicode(message)
return tmsg

and while I'm not going to print out the whole of GNUTranslation._parse, I
can assure you that the relevant bit is:

if self._charset:
msg = unicode(msg, self._charset)
tmsg = unicode(tmsg, self._charset)
catalog[msg] = tmsg

Given this, I cannot possibly see how installing more than one language can
work. More to the point, even if it did, install() modifies __builtin__,
which sounds incredibly thread-unsafe to me. We can still use the
GNUTranslation objects, we just can't install them. _() needs to generate an
object that will run the translation through the appropriate object.

Also, creating our own string object allows us to do proper localised number
and date formatting on the fly.

Moof

Moof

unread,
Aug 3, 2005, 8:15:57 AM8/3/05
to django-d...@googlegroups.com
Nebojša Đorđević - nesh wrote:

> Ok, too bad for that, now we must make some replacement for formatting
> date, numbers and stuff.

I've creted a page on the Wiki with an overview of the i18n discussion as I
see it. it looks rather tainted to my prejudices at the moment, so I invite
you all to hack it apart with the stuff I may have left out.

I've left a link to a page defining my proposed string object, but I haven't
got round to writing that yet.

http://code.djangoproject.com/wiki/InterNationalization

Ksenia Marasanova

unread,
Aug 3, 2005, 8:23:30 AM8/3/05
to django-d...@googlegroups.com
> Given this, I cannot possibly see how installing more than one language can
> work. More to the point, even if it did, install() modifies __builtin__,
> which sounds incredibly thread-unsafe to me. We can still use the
> GNUTranslation objects, we just can't install them. _() needs to generate an
> object that will run the translation through the appropriate object.
>
> Also, creating our own string object allows us to do proper localised number
> and date formatting on the fly.

I didn't digget into it yet but it looks like the solution is do not
use "install()" method.
http://barry.warsaw.us/papers/mailman-freenix-2003/node8.html

And the source code:
http://cvs.sourceforge.net/viewcvs.py/mailman/mailman/Mailman/i18n.py?rev=2.10.2.2&view=markup
--
Ksenia

Nebojša Đorđević - nesh

unread,
Aug 4, 2005, 2:56:39 PM8/4/05
to django-d...@googlegroups.com
On 3-08-2005, at 14:23, Ksenia Marasanova wrote:

> I didn't digget into it yet but it looks like the solution is do not
> use "install()" method.
> http://barry.warsaw.us/papers/mailman-freenix-2003/node8.html

And here is implementation - put files in django/core/i18n or similar.

Usage:
set_language(language=None, domain='django', path=None):
load localization file and setup current translation

locale files are stored in:
<path>/<language>/<domain>.mo

parameters:
language string: locale code ('en', 'en_US', 'sr', ...)
domain string: translation domain
path string: path where file is located

set_locale('en', 'comments')

ugettext(s):
Do translation of the given string into current language and
auto-magically looking up substitution variables in the local and
global namespace of the caller. Returns unicode string with
translated message

ugettext('%(localevar)s test')

ungettext(singular, plural, n):
Same as above but correct plural form for number 'n' will be
selected.

class Language
for access for local translations. So you can use different
translations inside your applications without disturbing site setup.


All *gettext functions return unicode strings.

_() is not used as alias to gettext because IMHO this way is more
readable, xgettext correctly extracts booth functions.

I also implemented simple file caching scheme for use with
translation files, just to decrease access to the filesystem.

Probably set_language() will be used somewhere inside request
initialization to set translation space. Django core will be using
top level *gettext functions and applications will be using Language
class.

...

(returning after reading some mod_python docs)

Module globals are local for one apache child which serves one
request at the time, right? Changing module global in one request
can't affect other running request?
I don't know much about how mod_python handles this, and I'm testing
this on the apache with MaxRequestsPerChild set to 1.

__init__.py
filecache.py
i18n.py

Carlo C8E Miron

unread,
Aug 4, 2005, 3:19:55 PM8/4/05
to django-d...@googlegroups.com
hi folks

2005/8/2, Radek Svarz <radek...@gmail.com>:
>
> As a tool for translators I suggest poEdit (http://www.poedit.org/). It
> is very comfortable editor.

Another alternative is to use Rosetta

* https://launchpad.net/rosetta

Rosetta is the translation portal of (but not limited to) the Ubuntu
project. Translators can work through-the-web or by downloading,
editing and uploading the PO files. Zope 3 project has recently
switched using this facility.

HTH,

--
Carlo C8E Miron, ICQ #26429731
--
Disclaimer:
If I receive a message from you, you are agreeing that:
1. I am by definition, "the intended recipient".
2. All information in the email is mine to do with as I see fit and
make such financial profit, political mileage, or good joke as it
lends itself to. In particular, I may quote it on USENET or the WWW.
3. I may take the contents as representing the views of your company.
4. This overrides any disclaimer or statement of confidentiality that
may be included on your message.

Nebojša Đorđević - nesh

unread,
Aug 5, 2005, 5:05:22 PM8/5/05
to django-d...@googlegroups.com
I think I finally got it. This time I'm using same system as mailman
(http://barry.warsaw.us/papers/mailman-freenix-2003/node8.html).

i18n.zip belongs in django/utils directory and (test) translations in
django/locale/<language>/django.po|mo.

I deliberately done only one translation in admin view (Site
administration->AAASite administrationZZZ) to avoid clobbering diff
with a ton of changes.

Trying it is easy. Copy i18n stuff in django/utils, locale stuff in
django/locale, apply diff, make sure that you are using english
locale (that one is supplied) and go to /admin/ in your site and log
on. You will see that page title is changed to AAASite
administrationZZZ if you are using english locale, if not, you will
see standard Site administration title. Nothing much but it's a start :)

You can try to copy django.po in some other dir (like django/locale/
<language code>/), change example text 'AAASite administrationZZZ',
compile this message with 'msgfmt -odjango.mo django.po' (you will
need gettext package installed for that) and use that language as
default in browser to see your message.

Usage is simple, when a request is initialized it will try to get
locale from the cookie ('lang'), Accept-Language header from browser
and finally from config var DEFAULT_LOCALE. If all of that fails or
if it can't find .mo file for required language, it will simply use
NullTranslations class - all messages will be unchanged (see
setup_from_request() in i18n.py).

If it's impossible to use global module var for holding current
translation I'll will need some information on how to make Language
object accessible to all django modules and applications.

Currently i18n module is missing features like:
- URL handler for changing language (something like /language/
xxx/ or ?lang=xxx to set user preferred language)
- template tags for getting singular and plural messages within
template
- some context vars (current language, available languages, etc)
for use in template (i.e. for making language selection menus)
- JavaScript translations
- other things described here: http://code.djangoproject.com/
wiki/InterNationalization

BTW within i18n directory you can find draft l10n implementation -
currently I'm managed to make localized numeric and monetary classes
(that's easy, date/time will be hard one). Comments?

p.s. I hope that i18n will be soon a part of django (if I don't get
it right, maybe another one can, who is more knowledgeable about
mod_python, gettext, locale and similar stuff). I have closing
deadline on two of our projects which needs translated admin
interfaces and have multi-lingual pages and are already almost
finished except i18n stuff.

p.p.s. Speaking of sites, I can put a link with something like 'Made
with django' or similar on sites I'll be setup shortly, right? :)

---
Nebojša Đorđević - nesh
Studio Quattro - Niš - SCG


i18n.diff
i18n.zip
locale.zip

Nebojša Đorđević - nesh

unread,
Aug 11, 2005, 5:39:29 AM8/11/05
to django-d...@googlegroups.com
I think it's a time to go for implementation of i18n, so first things
first. Let's define i18n interface and behavior:

This is RFC :)

When discussion is done, update http://code.djangoproject.com/wiki/
InterNationalization with information gathered from here.

Files:
======

* i18n module is located inside django.utils.locale

* i18n will be using translations found in system locale paths

* django translations are located in
django/share/locale/<lang>/LC_MESSAGES/django.mo

* site wide translations are located in <site_root>/locale/<lang>.mo

* application translations are located in
<application_root>/locale/<lang>.mo

i18n module:
============

class _NullTranslation
----------------------

Wrapper around gettext.NullTranslations and base class for all
translations. Main difference from gettext.NullTranslations is added
method for getting locale name and check in add_fallback to prevent
adding already existing fall-back translation (because translations
are fetched from the cache). This class implements all *gettext
methods with added functionality to ease use of positional parameter's
in message strings (see:
http://barry.warsaw.us/papers/mailman-freenix-2003/node8.html)

class _Translation
------------------

wrapper around gettext.GNUTranslations (subclass of _NullTranslation)

get_translation(languages, app_name=None)
-----------------------------------------

Get translation object for first found language in <languages> in
paths specified above. In returned translation object fall-back
translations will be set to all matching translations found in above
described directories in this order:

1. site locale directory
2. django locale directory
3. system locale directory

Finally if 'en' is not specified in <languages> fall-back to 'en' will
be added (with same search paths as above with addition of application
specific locale directory if <app_name> is not None).
If all else fails, returns _NullTranslation.

set_translation(languages, app_name=None)
-----------------------------------------

Sets *global* translation via get_translation()

current_translation()
---------------------

Returns current *global* translation

*gettext functions
------------------

Alias to same functions in *global* translation object

available_locales()
-------------------

Return a list populated with all available locales installed found in
search paths described above (it will search all application
directories within site). Returned list will be composed from dicts
with following keys (see
http://www.loc.gov/standards/iso639-2/ascii_8bits.html):

* 'bibliographic' - alpha-3 (bibliographic) code

* 'terminologic' - alpha-3 (terminologic) code (when given)

* 'code' - alpha-2 code (when given)

* 'english' - English name of a language

* 'french' - French name of a language

* 'native' - native name of a language. If 'native' is not
defined it will be replaced with English
name of
a language. Not in ISO639-2.

request object:
===============

When request object is created it will try to find locale from cookie,
then from Accept-Language header and finnaly will be fall-back to
'en'. Request object will have two additional vars:

i18n - for holding negotiated translation object
i18n_requested - list of requested locales

This is done calling setup_locale() from HttpRequest inside
*Request constructor because *Request classes don't call base class
(HttpRequest) constructor.

context object:
===============

When context object is created it will create following vars (if not
exist already):

* i18n - translation object
* locale - currently selected locale
* avail_locales - list of all available locales

template tags:
==============

For easy extraction with xgettext one template tag is added:

{% i18n <gettext function>(...) %}
----------------------------------

This tag will run corresponding gettext function from i18n var in
current context. All context variables are accessible within it, so
you can do:

{% i18n
ngettext('%(foo)d singular', '%(foo)d plural', foo)
%}

Usage:
======

Django core will be using top level gettext aliases to global
translation object.

Applications will create it's own translation objects or will be
using supplied translation object from request.

Nebojša Đorđević - nesh

unread,
Aug 17, 2005, 2:44:36 PM8/17/05
to django-d...@googlegroups.com
On 11-08-2005, at 11:39, Nebojša Đorđević - nesh wrote:

> This is done calling setup_locale() from HttpRequest inside
> *Request constructor because *Request classes don't call base class
> (HttpRequest) constructor.

Update:

I just found-out that this is not correct way to do it :(

When I try to use sessions to store user locale, I found-out that
session attr is not initialized when request is created ... so, I
wrote middleware class LocaleMiddleware which will do just that,
check session and cookies, try accept-language, and set request i18n*
vars.

Because, nobody seems to comment my proposition, I'm soon will be
sending a patch - just i18n stuff, without any translation (except
one in the main.py).

BTW Is there any way to erase my previous patches and attachments
from Ticket #65? Just to keep things clean.

Nebojša Đorđević - nesh

unread,
Aug 18, 2005, 1:48:15 PM8/18/05
to django-d...@googlegroups.com
On 11-08-2005, at 11:39, Nebojša Đorđević - nesh wrote:

> set_translation(languages, app_name=None)
> -----------------------------------------
>
> Sets *global* translation via get_translation()
>
> current_translation()
> ---------------------
>
> Returns current *global* translation
>
> *gettext functions
> ------------------
>
> Alias to same functions in *global* translation object

Update 2:

All global translation functions and variables are not needed, if I'm
not mistaking, only places for outputting i18n texts are inside
functions that already receives translation via request object
(handler functions), see previous post about middleware i18n class.

Any translatable texts which are not inside handler functions (i.e.
validator messages) will be only marked as translatable strings
(using xpot syntax ''"foo" or ""'foo') and translated when output is
done inside handlers or templates.

This way requires less changes to existing code (not taking into
account marking translation strings and adding calls to translate
output in view functions).

I'll be sending patches soon.

Lalo Martins

unread,
Sep 12, 2005, 3:49:35 AM9/12/05
to Django developers
I'm one of the people who put i18n into Plone (and incidentally, I also
worked on Rosetta). The company where I'm working now is switching to
Django. So you can count on me to help with this i18n thing :-)

So, the way we do it in Plone (since someone asked on the wiki), is
more or less like this:

- We have a component called "translation service", which loads the
message catalogs and indexes them in memory.

- The templates have i18n markup; so the strings are translated when
the template is being rendered.

- I originally implemented PlacelessTranslationService to choose a
language based on the HTTP header, overridable by a cookie. Alas, that
didn't work out so well :-( turns out most people don't even know they
can set up language preferences in their browser. The more recent
versions of plone are migrating to a click-the-flag system (which
essentialy just sets the aforementioned cookie).

Nebojša Đorđević - nesh

unread,
Sep 12, 2005, 10:49:18 AM9/12/05
to django-d...@googlegroups.com
On 12-09-2005, at 9:49, Lalo Martins wrote:

> - We have a component called "translation service", which loads the
> message catalogs and indexes them in memory.

My i18n implementation currently load translations when a
request is created (using LocaleMiddleware) and store translation
object inside the request using (somewhat modified) gettext
translation objects. Also I modified DjangoContext to include i18n
object and some other info (available locales and current locale).

> - The templates have i18n markup; so the strings are translated when
> the template is being rendered.

Similarly, I created {% i18n <gettext function>('') %} tag for
that. I deliberately choose this syntax to enable using of xgetext
for extracting messages (with lang set to python) and to decrease
number of new tags added to template language.

> - I originally implemented PlacelessTranslationService to choose a
> language based on the HTTP header, overridable by a cookie. Alas,
> that
> didn't work out so well :-( turns out most people don't even know they
> can set up language preferences in their browser. The more recent
> versions of plone are migrating to a click-the-flag system (which
> essentialy just sets the aforementioned cookie).

Sadly, that's true. Currently I'm trying to get language from
HTTP header, then from session and at last from cookie. Additionally
I'm implemented a view to manually select language (with a
customizable URI, i.e. I'm currently using r'/lang/(?P<locale>.*?)/
$') and storing selected language in the session (if available) or
the cookie.

I'm currently finishing my first site with django and my i18n
implementation (beta version of the site will be probably published
this week), currently without translated admin interface (I don't
have time to do that for now - deadline is closing :( ), but all
other parts of the site are fully i18n-ized.

---
Nebojša Đorđević - nesh
Studio Quattro - Niš - SCG

http://djnesh.blogspot.com/ | http://djnesh-django.blogspot.com/
Registered Linux User 282159 [http://counter.li.org]



hugo

unread,
Sep 17, 2005, 5:29:20 AM9/17/05
to Django developers
> I'm currently finishing my first site with django and my i18n
> implementation (beta version of the site will be probably published
> this week), currently without translated admin interface (I don't
> have time to do that for now - deadline is closing :( ), but all
> other parts of the site are fully i18n-ized.

Ah, that's great news - so the patch will be a tested one? Great. I
really would like to see something like this to make it into the django
source soon, because it will be a blocking problem for me, too -
without at least i18n (l10n isn't really needed for me, as I can easily
circumvent those problems by just changing templates) I won't be able
to do much with it at work. The alternative for me would be to just
translate the strings in django validators to german and run with a
modified django source, but I would prefer a multi-language solution
over such a hack.

bye, hugo-

Nebojša Đorđević - nesh

unread,
Sep 30, 2005, 7:24:43 AM9/30/05
to django-d...@googlegroups.com
On 17-09-2005, at 11:29, hugo wrote:

> Ah, that's great news - so the patch will be a tested one? Great. I
> really would like to see something like this to make it into the
> django
> source soon, because it will be a blocking problem for me, too -
> without at least i18n (l10n isn't really needed for me, as I can
> easily
> circumvent those problems by just changing templates) I won't be able
> to do much with it at work. The alternative for me would be to just
> translate the strings in django validators to german and run with a
> modified django source, but I would prefer a multi-language solution
> over such a hack.


I'm just now see that you posted diffs in ticket #65 and created a
branch.

Great, currently I just have no time to work on i18n part except
parts needed for my project. After sending a patch I realized that I
also need that users of the site can modify translations (well, it's
just that I'm a bit lazy, and I'm want to delegate part of
translation process to the end-user ;) ), so I focus on user side of
the site. In attach with this message I'm sending my add-on
application for i18n.

I make new tag and filter:

{% gettext [n] %}message[{% plural %}plural message]{% endgettext %}
which support both singular and plural translation lookups. Also
{{ }} variables are not expanded for translation lookup, they are
expanded after translation is done similar to positional parameters
in python code.

{{ foo|translation }}
which only can be used for singular lookup

Well, I'm first used i18n ... syntax to make easier to extract
messages with xgettext but in that case we are loosing ability to
have messages longer than one line and messages can't be combined
with another filter (I'm using Markdown for all text fields in my
project).

Also, I'm sub-classed GNUTranslation to make it possible to store
translations in database because translate/compile cycle is not very
(end)user-friendly. With this I can have someone translate or to add
new language to user part of site thru web interface without any
intervention from me.

Probably we can merge this two parts together so both translation
storages are usable in the same project.
I'm extracted all needed stuff to make i18n plug-in application
without need to change anything in django-core.

My proposition for i18n usage follows:

Admin interface, messages, ..., will be using standard gettext
storage (.mo), because this messages are general to all sites using
django and will not have to be changed by end-users of a application.

OTOH, application specific messages, intended for user browsing the
site (not admin part) can be stored in database to ease adding and
changing translation for non-developers. From a quick look at your
code I don't think that merging will be a problem (If I can dedicate
some time I can make a patch for that), it's just matter of adding
another place for storing messages which returns Translation objects
from database.

And finally, stuff that I'm missing in your implementation:

- some way to get a list of available translations and translation
names for making language selection menus (like Lalo Martins said,
small number of users have browser set-up correctly) and associate
view function.
- means to get current selected locale and output charset, and inject
that in context object (i.e. <html xmlns="http://www.w3.org/1999/
xhtml" lang="{{ django_locale }}" xml:lang="{{ django_locale }}">)
- plural support


p.s. Well I hope that I'm making some sense in this message, my poor
english knowledge coupled with around the clock work with closing
deadlines makes interesting combination, I hope that attached code is
easier to understand :).

i18n.zip

hugo

unread,
Sep 30, 2005, 2:36:06 PM9/30/05
to Django developers
Hi!

>I'm just now see that you posted diffs in ticket #65 and created a
>branch.

Yep. I did it a bit differently than in youre patch: I tried to boil it
down to the smalles possible patch to have everything that is needed to
get i18n started, but not too much. I think it's much better to have
smaller, dedicated patches that are easier to understand and to apply.
So I only added the request middleware (that is totally optional - you
can just set a static translation language code if you don't need multi
language support) and the bare minimum to get a _() function (and some
helpers) to work with django. The rest of my patch will just be the
needed changes to django itself to provide the _() hooks to do the
actual translation.

I did subclass the GNUTranslations, but actually if you want a db
store, it's easy to integrate later on. For now I think staying with
gettext and .po/.mo is the way to go - you can easily deliver .mo files
with your django app to people that want to use your app, but you can't
as easily deliver database content.

Oh, and the current selected language is in request.LANGUAGE_CODE, so
you can find that. I didn't touch the default encoding as django itself
is just running on utf-8 - if that is changed some day to have a
configureable default encoding, it will be easy to incorporate that
into the i18n stuff. Changing the encoding based on some HTTP headers
will produce problems if the chosen encoding is less than utf-8 - you
can't allways transform stuff the right way. It's better to stick to
utf-8 anyway.

A list of available translations is more a developer choice than a
django choice: even if django delivers a butt-load of languages, this
doesn't say that the programmer wants all them selectable: for example
if the applications themselves only have fewer languages as
translations.

But it would be easy to write a template tag that produces a select-tag
for all installed languages in the base locale path of django. But
that's stuff that shouldn't go into the first i18n patch for django,
that's better left for additional patches - keep the i18n patch as
small as possible. i18n is complicated enough by itself ;-)

But all that doesn't say that there shouldn't be any patches later on
that make the i18n stuff friendlier or better or easier - my patch just
is there to make i18n _possible_ :-)

bye, hugo

Nebojša Đorđević - nesh

unread,
Oct 1, 2005, 7:40:37 AM10/1/05
to django-d...@googlegroups.com
On 30-09-2005, at 20:36, hugo wrote:

> I did subclass the GNUTranslations, but actually if you want a db
> store, it's easy to integrate later on. For now I think staying with
> gettext and .po/.mo is the way to go - you can easily deliver .mo
> files
> with your django app to people that want to use your app, but you
> can't
> as easily deliver database content.

I agree, I can continue to develop my database driven version as add-
on application which will be compatible with your i18n
implementation, so users can use .po/.mo based storage for the core
and database based for own applications.

About plural forms, I can use something like this in the code:

t = translation('foo', 'bar')
msg = t.ngettext(x, 'singular', 'plural')

but, your implementation for template tag don't have any plural
support. You can probably add something like {% i18n ngettext(x,
'singular', 'plural') %}, patch is attached (untested).

> But all that doesn't say that there shouldn't be any patches later on
> that make the i18n stuff friendlier or better or easier - my patch
> just
> is there to make i18n _possible_ :-)

And that's is great, I'm just started to think that no one but me is
interested in i18n part of the django :).


tag.diff

hugo

unread,
Oct 1, 2005, 8:48:42 AM10/1/05
to Django developers
Hi,

> your implementation for template tag don't have any plural
> support. You can probably add something like {% i18n ngettext(x,
> 'singular', 'plural') %}, patch is attached (untested).

Yep, plural support was missing up to now. I took your patch and
applied it and added some more stuff to smooth it out. Now there is a
ngettext helper and ngettext i18n form. And there are unit tests for
i18n templating.

bye, Georg

Nebojša Đorđević - nesh

unread,
Oct 3, 2005, 5:29:29 AM10/3/05
to django-d...@googlegroups.com
On 30-09-2005, at 20:36, hugo wrote:

> Oh, and the current selected language is in request.LANGUAGE_CODE, so
> you can find that. I didn't touch the default encoding as django
> itself

Great, but I want to have globally accessible language code (i.e. to
select news based on current language), so I added method to get
language to your Translation object and a helper function
get_language to get current language. Also I added LANGUAGE_CODE and
LANGUAGES to DjangoContext so templates can have access to list of
available languages (from config) and currently selected language. I
can write tag for that, but this way is more general (IMO).

Also in get_language_from_request I added support to set language via
GET parameter and store that selection in the session or cookie, so
you can set current locale (and store it) with .../?django_locale=de.

Currently I'm moving my projects to use your implementation so (at
least) I can provide some feedback.

Also I'm sending preliminary Serbian translation.

i18n.diff
sr.zip

Nebojša Đorđević - nesh

unread,
Oct 3, 2005, 5:35:56 AM10/3/05
to django-d...@googlegroups.com
On 3-09-2005, at 11:29, Nebojša Đorđević - nesh wrote:

> Also I added LANGUAGE_CODE and LANGUAGES to DjangoContext

I'm forget to say that LANGUAGES will be in model choices format, i.e.:

LANGUAGES = (('en', 'English'), ('sr', 'Srpski'))

so I can use it as a choices for my models.

hugo

unread,
Oct 3, 2005, 6:11:56 AM10/3/05
to Django developers
> Great, but I want to have globally accessible language code (i.e. to
> select news based on current language), so I added method to get
> language to your Translation object and a helper function
> get_language to get current language. Also I added LANGUAGE_CODE and

> LANGUAGES to DjangoContext so templates can have access to list of
> available languages (from config) and currently selected language. I
> can write tag for that, but this way is more general (IMO).

I am adding it to my branch. I did provide a default LANGUAGES
definition in the global settings that lists all languages that are
already available - the idea being that users can just take that list
and show it to their users, or extend it with local languages they
provided.

I did change the LANGUAGE_CODE stuff for the DjangoContext a bit,
because LANGUAGE_CODE is only defined on the request if you have the
i18n middleware loaded - now the DjangoContext either carries
request.LANGUAGE_CODE or settings.LANGUAGE_CODE.

Commit will follow soon, I need to take a few tests, first.

Thanks for your input (and more thanks that you don't feel put off
because I did the patch a bit different than your first patch ;) )

bye, Georg

Carlo C8E Miron

unread,
Oct 3, 2005, 6:20:01 AM10/3/05
to django-d...@googlegroups.com
ciao (djangonauts™ ;) )

2005/10/3, Nebojša Đorđević - nesh <ne...@studioquattro.co.yu>:


> Also in get_language_from_request I added support to set language via
> GET parameter and store that selection in the session or cookie, so
> you can set current locale (and store it) with .../?django_locale=de.

what about dropping the "django_" part of the name? something like
"../?locale=de" seems neatier to me... or maybe i18n_locale, if
polluting namespace bother you...

hugo

unread,
Oct 3, 2005, 8:22:36 AM10/3/05
to Django developers
> what about dropping the "django_" part of the name? something like
> "../?locale=de" seems neatier to me... or maybe i18n_locale, if
> polluting namespace bother you...

I chosen django_language (not django_locale - we are only switching
languages, not locales currently ;-) ) because it's unlikely that
someone else will use it. And since the language selection would be
just done by some HTML form, I don't think the name really is
important. You don't usually pass that variable on from a user point of
view.

But it could be switched to just "language" easily - if the overall
vote is for language instead of django_language, I will change that.

Reminds me, I should extend the code from only using GET to using POST,
too - so that the language selection form can be a method="POST" form
to prevent search bots to switch languages ...

bye, Georg

Nebojša Đorđević - nesh

unread,
Oct 4, 2005, 6:25:42 AM10/4/05
to django-d...@googlegroups.com
On 3-09-2005, at 12:11, hugo wrote:

> I did change the LANGUAGE_CODE stuff for the DjangoContext a bit,
> because LANGUAGE_CODE is only defined on the request if you have the
> i18n middleware loaded - now the DjangoContext either carries
> request.LANGUAGE_CODE or settings.LANGUAGE_CODE.
>

Oooops, I forgot to check for that :)

> Thanks for your input (and more thanks that you don't feel put off
> because I did the patch a bit different than your first patch ;) )

I'm just glad to see that i18n support is coming to life. Your
version is definitely simpler than mine, I didn't have enough time to
write i18n stuff right, so it's great that someone do it for me :)

Nebojša Đorđević - nesh

unread,
Oct 4, 2005, 7:36:56 AM10/4/05
to django-d...@googlegroups.com
Another helper function which I use often to get language dependent
objects from database. I think that's this case is common enough to
put support for that in the framework (see below).

---- code ----
from django.utils.translation import get_language
from django.core.exceptions import ObjectDoesNotExist

def get_for_lang(func, fallback='no translation',
lang_field='locale', text_field='text', alt_lang=[]):
""" get translation from db

func: function to call to get object
fallback: fallback text
lang_field: locale id field
text_field: text to return field
alt_lang: list of alternative locales, 'en' is added if not
present
"""

kwargs = {'%s__exact' % lang_field: get_language()}

text = None

# try preferred language
try:
obj = func(**kwargs)
return getattr(obj, text_field)
except ObjectDoesNotExist:
pass

if not isinstance(alt_lang, (tuple, list)):
alt_lang = [alt_lang]
#
if 'en' not in alt_lang:
alt_lang.append('en')
#

for lng in alt_lang:
try:
kwargs = {'%s__exact' % lang_field: lng}
obj = func(**kwargs)
text = getattr(obj, text_field)
break
except ObjectDoesNotExist:
pass
# for

if text is None:
return fallback

return text
# get_for_lang
--------

OTOH, maybe is better to add new I18NCharField and I18NTextField
(with better names than mine ;) ) to models to support this common
case. I don't know enough about models to make that myself, but in
time ... :)

Example:

class Article(meta.Model):
model_no = CharField(...)
price = FloatField(...)
...
name = I18NCharField(maxlength=200)
description = I18NTextField()

def __repr__(self):
return self.get_name()

this will create additional models like this:

class ArticleName(meta.Model):
article = meta.ForeignKey(Article, edit_inline=meta.TABULAR,
num_in_admin=len(LANGUAGES), max_num_in_admin=len
(LANGUAGES))
locale = meta.CharField(maxlength=10, choices=LANGUAGES,
db_index=True)
name = meta.CharField(maxlength=200, core=True)

def __repr__(self):
return '[%s] %s' % (self.locale, self.name)

class META:
ordering = ['locale']
unique_together=(('article', 'locale'),)
# class
# ArticleName

class ArticleDescription(meta.Model):
article = meta.ForeignKey(Article, edit_inline=meta.TABULAR,
num_in_admin=len(LANGUAGES), max_num_in_admin=len
(LANGUAGES))
locale = meta.CharField(maxlength=10, choices=LANGUAGES,
db_index=True)
description = meta.TextField(core=True)

def __repr__(self):
return '[%s] %s' % (self.locale, self.description)

class META:
ordering = ['locale']
unique_together=(('article', 'locale'),)
# class
# ArticleName

and also add this functions to the Article model: get_name() and get_
description() which will return appropriate heading or body field
(i.e. using get_for_lang from above) from object based on current
selected language.

I'm found while writing current application that this pattern is very
common (almost a 2/3 of my models are using some type of localized
name/description fields) and it will be great that i18n/model
framework supports that without any additional code.

Any thoughts?

hugo

unread,
Oct 5, 2005, 9:28:29 AM10/5/05
to Django developers
Hi,

>I'm found while writing current application that this pattern is very
>common (almost a 2/3 of my models are using some type of localized
>name/description fields) and it will be great that i18n/model
>framework supports that without any additional code.

I agree that this is a rather common pattern - but I don't think it's
something that the i18n branch should solve, but something that could
go into another patch. The reason being that "metamodels" like that are
things where not everybody will agree on - it might be that some want
to do it just by adding additional attributes to their model while
others might want to normalize it out into a dependend table and stuff
like that.

So I'd opt to leave that stuff out of the i18n branch for now and to
concentrate on the "translations for string constants" part and some
other branch or patch can introduce the "multilinguality of models"
patterns later.

That way it's easier for Jacob and Adrian to see what the patch
actually will do and to decide wether they need some changes before
merging it into trunk.

Oh, and I think I would quite like I18NCharField or I18NTextField -
with automatic creation of the dependend table and a way in the admin
to select what language content to edit. But that's just me. And maybe
it should wait for the new_admin branch of rjwittams, as his stuff
might make the extension of the admin to incorporate those I18N*Fields
easier.

bye, Georg

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 5:33:18 AM10/11/05
to django-d...@googlegroups.com
On 5-10-2005, at 15:28, hugo wrote:

From docs:

> - $PROJECTPATH/apps/<app>/locale/<language>/LC_MESSAGES/django.(po|mo)
> - $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
> - $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

Currently I have setup where I have two (almost) separate project
which use same set of templates, so I'll be needing some way to
manually set path to locale directory because my templates are
outside of the booth projects.

Is there any chance to add config option to set additional locale paths?

Also, I think that is a time to change http://code.djangoproject.com/
wiki/InterNationalization page to reflect current status for i18n
branch and, maybe, add docs from translation.txt somewhere on the wiki.

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 5:43:25 AM10/11/05
to django-d...@googlegroups.com
On 11-10-2005, at 11:33, Nebojša Đorđević - nesh wrote:

from make_messages.py:

> if file.endswith('.html'):
> src = open(os.path.join(dirpath, file), "rb").read()
> open(os.path.join(dirpath, '%s.py' % file),
> "wb").write(templateize(src))
> thefile = '%s.py' % file
> if verbose: sys.stdout.write('processing file %s in %s
> \n' % (file, dirpath))
> cmd = 'xgettext %s -d %s -L Python -o - "%s"' % (

Is it's really necessary to change extension of html files to
html.py? xgettext will be treat all files as python source because "-
L Python" anyway. Petar Marić <petar...@gmail.com> told me that
poedit have some issues with filenames in comments which xgettext
generates.

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 6:06:56 AM10/11/05
to django-d...@googlegroups.com
On 5-10-2005, at 15:28, hugo wrote:

> So I'd opt to leave that stuff out of the i18n branch for now and to
> concentrate on the "translations for string constants" part and some
> other branch or patch can introduce the "multilinguality of models"
> patterns later.

Well, creating new field types are totally undocumented, I'll try to
dig trough the source if I have time, and make i18n fields as a add-
on. I'll try to not make any reference to i18n branch to keep things
separate and general as possible.

I have lots of i18n dependent fields and I hate to do copy & paste
every-time when I need them.

update:
I looked at the model sources and I admit that I'm lost :( My
starting point are ManyToMany field, but I failed to understand how
to do it). Can someone post some examples (or better documentation)
how to do this type of stuff?

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 6:24:52 AM10/11/05
to django-d...@googlegroups.com
On 11-10-2005, at 12:06, Nebojša Đorđević - nesh wrote:

This is little off-topic.

I'm looking to make both Serbian translations (latin and cyrillic).
But, I'm failed to found any reference to _standard_ codes for that.
Only idea I have is to make two codes - sr_LAT and sr_CYR and to map
sr (which majority of browsers sends) to sr_LAT (we discuss that on
local web development forum and agreed that is most viable solution).

For that I'll will need some type of language code mapping to happen
when request is processed. gettext use locale_alias table from
locale.py but this is hard-coded in the source. Have anyone know to
whom I need to address to add that mapping? Will that do the trick?
Or I must add this mapping stuff to i18n code?

Petar Marić

unread,
Oct 11, 2005, 6:59:04 AM10/11/05
to django-d...@googlegroups.com
On 10/11/05, Nebojša Đorđević - nesh <ne...@studioquattro.co.yu> wrote:
> Is it's really necessary to change extension of html files to
> html.py? xgettext will be treat all files as python source because "-
> L Python" anyway. Petar Marić <petar...@gmail.com> told me that
> poedit have some issues with filenames in comments which xgettext
> generates.

Yes, I had to search&replace ".html.py" to ".html" in order to use
poedit reference tool. There was also an issue with incorrect line
numbers, but I see that was fixed in changeset 801
(http://code.djangoproject.com/changeset/801).

I just wanted to say that you guys are doing a bloody good job with
i18n, and django in general :)
--
Петар Марић
Irrigation of the land with seawater desalinated by fusion power is
ancient. It's called 'rain'. - Michael McClary

Petar Marić

unread,
Oct 11, 2005, 7:08:59 AM10/11/05
to django-d...@googlegroups.com
On 10/11/05, Nebojša Đorđević - nesh <ne...@studioquattro.co.yu> wrote:
> This is little off-topic.
>
> I'm looking to make both Serbian translations (latin and cyrillic).
> But, I'm failed to found any reference to _standard_ codes for that.
> Only idea I have is to make two codes - sr_LAT and sr_CYR and to map
> sr (which majority of browsers sends) to sr_LAT (we discuss that on
> local web development forum and agreed that is most viable solution).
>
> For that I'll will need some type of language code mapping to happen
> when request is processed. gettext use locale_alias table from
> locale.py but this is hard-coded in the source. Have anyone know to
> whom I need to address to add that mapping? Will that do the trick?
> Or I must add this mapping stuff to i18n code?

Offtopic: I think it's for the best to recode the current translation
file to cyrillic, because we can use automated tools to make latin
translation out of that. Saves quite a some time.
If it's allright with you nesh, I would get right on it :)

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 7:24:51 AM10/11/05
to django-d...@googlegroups.com
On 11-10-2005, at 13:08, Petar Marić wrote:

> Offtopic: I think it's for the best to recode the current translation
> file to cyrillic, because we can use automated tools to make latin
> translation out of that. Saves quite a some time.
> If it's allright with you nesh, I would get right on it :)

Great, go for it. Maybe I can make on-the-fly converter for that?
This way we can circumvent sr_* stuff, and just give user a selection
to use latin or cyrillic version of the site.

to Petar: We can continue this discussion on DPT or ICQ.

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 7:44:08 AM10/11/05
to django-d...@googlegroups.com
On 11-10-2005, at 13:24, Nebojša Đorđević - nesh wrote:

Hey C8E, maybe I'm missed but I just found out that you registered
django on roseta (https://launchpad.net/products/django) :). When we
can use this to make django translation repository?

Petar Marić

unread,
Oct 11, 2005, 8:21:45 AM10/11/05
to django-d...@googlegroups.com
On 10/11/05, Nebojša Đorđević - nesh <ne...@studioquattro.co.yu> wrote:
> to Petar: We can continue this discussion on DPT or ICQ.

Agreed.

hugo

unread,
Oct 11, 2005, 10:02:16 AM10/11/05
to Django developers
That's a leftover - it is just there because the template stuff won't
translate well with xgettext (the multitude of quotes just make
xgettext woozy in it's head), so they are translated to some other
format. The current make-messages.py changes the references back to the
original .html, now - so that problem should be gone.

hugo

unread,
Oct 11, 2005, 10:05:59 AM10/11/05
to Django developers
>to Petar: We can continue this discussion on DPT or ICQ.

Generally, discussions about the i18n stuff should take place somewhere
where I can see them, if it's expected to be something I should take
into account in the branch :-)

Best way for online discussion would be the #django IRC channel on
freenode.net, as that a) is visited by me quite often and b) is
publicly logged for later reference.

bye, Georg

hugo

unread,
Oct 11, 2005, 10:11:37 AM10/11/05
to Django developers
Hi,

just some heads-up on the i18n stuff: I am now mostly done with the
first part of implementing the base code. So I switched my gallery site
- http://viele-bunte-bilder.de/ - to use my branch and to run it. So
you can see the first app running with full i18n.

The source to that gallery stuff is online:

https://simon.bofh.ms/cgi-bin/trac-django-projects.cgi/browser/gallery/trunk/

The project is using the django translations (of course they aren't
complete, yet - that will come later, when the source is about ready)
and project-local translations.


bye, Georg

Nebojša Đorđević - nesh

unread,
Oct 11, 2005, 10:14:39 AM10/11/05
to django-d...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 11-10-2005, at 16:05, hugo wrote:

> Generally, discussions about the i18n stuff should take place
> somewhere
> where I can see them, if it's expected to be something I should take
> into account in the branch :-)


Well, this is more Serbian only oriented discussion. I don't think
that others are interested in our "same language - two alphabets"
problem. It's just a matter of not polluting list with this type of
problems.

When we came up with solution and if some type of changes to i18n is
required we will post all relevant info here (and patches if any).

> Best way for online discussion would be the #django IRC channel on
> freenode.net, as that a) is visited by me quite often and b) is
> publicly logged for later reference.

Agreed.

- ---
Nebojša Đorđević - nesh
Studio Quattro - Niš - SCG

http://djnesh.blogspot.com/ | http://djnesh-django.blogspot.com/
Registered Linux User 282159 [http://counter.li.org]



-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (Darwin)

iD8DBQFDS8jPt2W58T89D+0RAjNfAKCHK6n5gs0rwwSa1gFgE77uIRRDZgCeJ2nr
AEmG7vAjfvkRQFii1J8vTDA=
=0aFu
-----END PGP SIGNATURE-----

hugo

unread,
Oct 11, 2005, 10:24:22 AM10/11/05
to Django developers
Hi,

>Currently I have setup where I have two (almost) separate project
>which use same set of templates, so I'll be needing some way to
>manually set path to locale directory because my templates are
>outside of the booth projects.

I added an optional LOCALE_PATHS setting that you can use in your
settings module. If set, it needs to be a list of paths (like
TEMPLATE_PATHS) where the translation system looks for language files.
This isn't tested, though, as I don't have the need for that feature at
the moment - try it and tell me wether it works ;-)

Oh, and the paths in LOCALE_PATHS are used directly - there is no
"locale/" subdirectory attached, the system directly expects the
language code below the paths.

bye, Georg

Reply all
Reply to author
Forward
0 new messages