1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

i18n patch - references #65

git-svn-id: http://code.djangoproject.com/svn/django/branches/i18n@726 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Georg Bauer 2005-09-29 17:01:05 +00:00
parent a60b2050c6
commit 98d7430609
11 changed files with 412 additions and 7 deletions

24
django/bin/compile-messages.py Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/python
import os
import sys
import getopt
basedir = None
if os.path.isdir(os.path.join('conf', 'locale')):
basedir = os.path.abspath(os.path.join('conf', 'locale'))
elif os.path.isdir('locale'):
basedir = os.path.abspath('locale')
else:
print "this script should be run from the django svn tree or your project or app tree"
sys.exit(1)
for (dirpath, dirnames, filenames) in os.walk(basedir):
for file in filenames:
if file.endswith('.po'):
sys.stderr.write('processing file %s in %s\n' % (file, dirpath))
pf = os.path.splitext(os.path.join(dirpath, file))[0]
cmd = 'msgfmt -o %s.mo %s.po' % (pf, pf)
os.system(cmd)

47
django/bin/make-messages.py Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/python
import os
import sys
import getopt
basedir = None
if os.path.isdir(os.path.join('conf', 'locale')):
basedir = os.path.abspath(os.path.join('conf', 'locale'))
elif os.path.isdir('locale'):
basedir = os.path.abspath('locale')
else:
print "this script should be run from the django svn tree or your project or app tree"
sys.exit(1)
(opts, args) = getopt.getopt(sys.argv[1:], 'l:d:')
lang = None
domain = 'django'
for o, v in opts:
if o == '-l':
lang = v
elif o == '-d':
domain = v
if lang is None or domain is None:
print "usage: make-messages.py -l <language> -d <domain>"
sys.exit(1)
basedir = os.path.join(basedir, lang, 'LC_MESSAGES')
if not os.path.isdir(basedir):
os.makedirs(basedir)
lf = os.path.join(basedir, '%s.po' % domain)
for (dirpath, dirnames, filenames) in os.walk("."):
for file in filenames:
if file.endswith('.py') or file.endswith('.html'):
sys.stderr.write('processing file %s in %s\n' % (file, dirpath))
if os.path.isfile(lf):
cmd = 'xgettext -j -d %s -L Python -p %s %s' % (domain, basedir, os.path.join(dirpath, file))
else:
cmd = 'xgettext -d %s -L Python -p %s %s' % (domain, basedir, os.path.join(dirpath, file))
os.system(cmd)

View File

@ -23,13 +23,13 @@
{% endif %} {% endif %}
{% if model.perms.add %} {% if model.perms.add %}
<td class="x50"><a href="{{ model.admin_url }}add/" class="addlink">Add</a></td> <td class="x50"><a href="{{ model.admin_url }}add/" class="addlink">{% i18n _('Add') %}</a></td>
{% else %} {% else %}
<td class="x50">&nbsp;</td> <td class="x50">&nbsp;</td>
{% endif %} {% endif %}
{% if model.perms.change %} {% if model.perms.change %}
<td class="x75"><a href="{{ model.admin_url }}" class="changelink">Change</a></td> <td class="x75"><a href="{{ model.admin_url }}" class="changelink">{% i18n _('Change') %}</a></td>
{% else %} {% else %}
<td class="x75">&nbsp;</td> <td class="x75">&nbsp;</td>
{% endif %} {% endif %}
@ -47,12 +47,12 @@
{% block sidebar %} {% block sidebar %}
<div id="content-related"> <div id="content-related">
<div class="module" id="recent-actions-module"> <div class="module" id="recent-actions-module">
<h2>Recent Actions</h2> <h2>{% i18n _('Recent Actions') %}</h2>
<h3>My Actions</h3> <h3>{% i18n _('My Actions') %}</h3>
{% load auth.log %} {% load auth.log %}
{% get_admin_log 10 as admin_log for_user user %} {% get_admin_log 10 as admin_log for_user user %}
{% if not admin_log %} {% if not admin_log %}
<p>None available</p> <p>{% i18n _('None available') %}</p>
{% else %} {% else %}
<ul class="actionlist"> <ul class="actionlist">
{% for entry in admin_log %} {% for entry in admin_log %}

Binary file not shown.

View File

@ -0,0 +1,51 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-09-29 14:11+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
#: core/validators.py:56
msgid "This value must contain only letters, numbers and underscores."
msgstr "Der Wert darf nur Buchstaben, Ziffern und Unterstriche enthalten."
#: conf/admin_templates/404.html:3 conf/admin_templates/404.html:7
msgid "Page not found"
msgstr "Seite nicht gefunden"
#: conf/admin_templates/404.html:9
msgid "We're sorry, but the requested page could not be found."
msgstr ""
"Es tut uns leid, aber die angeforderte Seite kann nicht gefunden werden."
#: conf/admin_templates/index.html:26
msgid "Add"
msgstr "Zufügen"
#: conf/admin_templates/index.html:32
msgid "Change"
msgstr "Ändern"
#: conf/admin_templates/index.html:50
msgid "Recent Actions"
msgstr "Kürzliche Aktionen"
#: conf/admin_templates/index.html:51
msgid "My Actions"
msgstr "Meine Aktionen"
#: conf/admin_templates/index.html:55
msgid "None available"
msgstr "Keine vorhanden"

View File

@ -55,3 +55,10 @@ for k in dir(me):
if not k.startswith('_') and k != 'me' and k != k.upper(): if not k.startswith('_') and k != 'me' and k != k.upper():
delattr(me, k) delattr(me, k)
del me, k del me, k
# as the last step, install the translation machinery and
# remove the module again to not clutter the namespace.
from django.utils import translation
translation.install()
del translation

View File

@ -1,8 +1,11 @@
"Default tags used by the template system, available to all templates." "Default tags used by the template system, available to all templates."
import re
import sys import sys
import template import template
from django.utils import translation
class CommentNode(template.Node): class CommentNode(template.Node):
def render(self, context): def render(self, context):
return '' return ''
@ -283,6 +286,28 @@ class WidthRatioNode(template.Node):
return '' return ''
return str(int(round(ratio))) return str(int(round(ratio)))
class I18NNode(template.Node):
def __init__(self, cmd):
self.cmd = cmd
self.i18n_re = re.compile(r'^\s*_\((.*)\)\s*$')
def render(self, context):
m = self.i18n_re.match(self.cmd)
if m:
s = m.group(1)
if s.startswith("'") and s.endswith("'"):
s = s[1:-1]
elif s.startswith('"""') and s.endswith('"""'):
s = s[3:-3]
elif s.startswith('"') and s.endswith('"'):
s = s[1:-1]
else:
raise template.TemplateSyntaxError("i18n must be called as {% i18n _('some message') %}")
return translation.gettext(s) % context
else:
raise template.TemplateSyntaxError("i18n must be called as {% i18n _('some message') %}")
def do_comment(parser, token): def do_comment(parser, token):
""" """
Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
@ -745,6 +770,22 @@ def do_widthratio(parser, token):
raise template.TemplateSyntaxError("widthratio final argument must be an integer") raise template.TemplateSyntaxError("widthratio final argument must be an integer")
return WidthRatioNode(this_value_var, max_value_var, max_width) return WidthRatioNode(this_value_var, max_value_var, max_width)
def do_i18n(parser, token):
"""
translate a given string with the current
translation object.
For example::
{% i18n _('test') %}
"""
args = token.contents.split(' ', 1)
if len(args) != 2:
raise template.TemplateSyntaxError("'i18n' requires one argument (got %r)" % args)
return I18NNode(args[1].strip())
template.register_tag('comment', do_comment) template.register_tag('comment', do_comment)
template.register_tag('cycle', do_cycle) template.register_tag('cycle', do_cycle)
template.register_tag('debug', do_debug) template.register_tag('debug', do_debug)
@ -761,3 +802,5 @@ template.register_tag('load', do_load)
template.register_tag('now', do_now) template.register_tag('now', do_now)
template.register_tag('templatetag', do_templatetag) template.register_tag('templatetag', do_templatetag)
template.register_tag('widthratio', do_widthratio) template.register_tag('widthratio', do_widthratio)
template.register_tag('i18n', do_i18n)

View File

@ -53,7 +53,7 @@ class CriticalValidationError(Exception):
def isAlphaNumeric(field_data, all_data): def isAlphaNumeric(field_data, all_data):
if not alnum_re.search(field_data): if not alnum_re.search(field_data):
raise ValidationError, "This value must contain only letters, numbers and underscores." raise ValidationError, _("This value must contain only letters, numbers and underscores.")
def isAlphaNumericURL(field_data, all_data): def isAlphaNumericURL(field_data, all_data):
if not alnumurl_re.search(field_data): if not alnumurl_re.search(field_data):

View File

@ -0,0 +1,38 @@
"this is the locale selecting middleware that will look at accept headers"
from django.utils import translation
# this is a cache that will build a map from modules to applications
_module_to_app = {}
class LocaleMiddleware:
"""
This is a very simple middleware that parses a request
and decides what translation object to install in the current
thread context. This allows pages to be dynamically
translated to the language the user desires (if the language
is available, of course).
"""
def process_view(self, request, view_func, param_dict):
global _module_to_app
lang = translation.get_language_from_request(request)
def findapp(module):
app = _module_to_app.get(view_func.__module__, None)
if app is not None:
return app
from django.conf import settings
for app in settings.INSTALLED_APPS:
if module.startswith(app):
_module_to_app[module] = app
return app
return '*'
app = findapp(view_func.__module__)
translation.activate(app, lang)

195
django/utils/translation.py Normal file
View File

@ -0,0 +1,195 @@
"translation helper functions"
import os
import gettext as gettext_module
try:
import threading
hasThreads = True
except ImportError:
hasThreads = False
if hasThreads:
currentThread = threading.currentThread
else:
def currentThread():
return 'no threading'
# translations are cached in a dictionary for
# every language+app tuple. The active translations
# are stored by threadid to make them thread local.
_translations = {}
_active = {}
# the default translation is based on the settings file
_default = None
# this is a cache for accept-header to translation
# object mappings to prevent the accept parser to
# run multiple times for one user
_accepted = {}
class DjangoTranslation(gettext_module.GNUTranslations):
"""
This class sets up the GNUTranslations context with
regard to output charset. Django allways uses utf-8
as the output charset.
"""
def __init__(self, *args, **kw):
gettext_module.GNUTranslations.__init__(self, *args, **kw)
self.__charset = self.charset()
self.set_output_charset('utf-8')
self.__app = '?.?.?'
self.__language = '??'
def set_app_and_language(self, app, language):
self.__app = app
self.__language = language
def __repr__(self):
return "<DjangoTranslation app:%s lang:%s>" % (self.__app, self.__language)
def translation(appname, language):
"""
This function returns a translation object.
app must be the fully qualified name of the
application.
This function will first look into the app
messages directory for the django message file,
then in the project messages directory for the
django message file and last in the global
messages directory for the django message file.
"""
t = _translations.get((appname, language), None)
if t is not None:
return t
from django.conf import settings
globalpath = os.path.join(os.path.dirname(settings.__file__), 'locale')
try:
t = gettext_module.translation('django', globalpath, [language, settings.LANGUAGE_CODE], DjangoTranslation)
t.set_app_and_language(appname, language)
except IOError: t = gettext_module.NullTranslations()
_translations[(appname, language)] = t
if appname != '*':
parts = appname.split('.')
project = __import__(parts[0], {}, {}, [])
app = __import__(appname, {}, {}, ['views'])
apppath = os.path.join(os.path.dirname(app.__file__), 'locale')
projectpath = os.path.join(os.path.dirname(project.__file__), 'locale')
try:
t = gettext_module.translation('django', projectpath, [language, settings.LANGUAGE_CODE], DjangoTranslation)
t.set_app_and_language(appname, language)
except IOError: t = None
if t is not None:
t.add_fallback(_translations[(appname, language)])
_translations[(appname, language)] = t
try:
t = gettext_module.translation('django', apppath, [language, settings.LANGUAGE_CODE], DjangoTranslation)
t.set_app_and_language(appname, language)
except IOError: t = None
if t is not None:
t.add_fallback(_translations[(appname, language)])
_translations[(appname, language)] = t
return _translations[(appname, language)]
def activate(appname, language):
"""
This function fetches the translation object for a given
tuple of application name and language and installs it as
the current translation object for the current thread.
"""
t = translation(appname, language)
_active[currentThread()] = t
def deactivate():
"""
This function deinstalls the currently active translation
object so that further _ calls will resolve against the
default translation object, again.
"""
del _active[currentThread()]
def gettext(message):
"""
This function will be patched into the builtins module to
provide the _ helper function. It will use the current
thread as a discriminator to find the translation object
to use. If no current translation is activated, the
message will be run through the default translation
object.
"""
global _default, _active
t = _active.get(currentThread(), None)
if t is not None:
return t.gettext(message)
if _default is None:
from django.conf import settings
_default = translation('*', settings.LANGUAGE_CODE)
return _default.gettext(message)
def get_language_from_request(request):
"""
analyze the request to find what language the user
wants the system to show.
"""
global _accepted
if hasattr(request, 'session'):
lang = getattr(request.session, 'django_language', None)
if lang is not None:
return lang
lang = request.COOKIES.get('django_language', None)
if lang is not None:
return lang
from django.conf import settings
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
if accept is not None:
t = _accepted.get(accept, None)
if t is not None:
return t
def _parsed(el):
p = el.find(';q=')
if p >= 0:
lang = el[:p].strip()
order = int(float(el[p+3:].strip())*100)
else:
lang = el
order = 100
return (lang, order)
langs = [_parsed(el) for el in accept.split(',')]
langs.sort(lambda a,b: cmp(a[1], b[1]))
globalpath = os.path.join(os.path.dirname(settings.__file__), 'locale')
for lang, order in langs:
if os.path.isfile(os.path.join(globalpath, lang, 'LC_MESSAGES', 'django.mo')):
_accepted[accept] = lang
return lang
return settings.LANGUAGE_CODE
def install():
"""
This installs the gettext function as the default
translation function under the name _.
"""
__builtins__['_'] = gettext

View File

@ -17,7 +17,7 @@ setup(
'admin_templates/registration/*.html', 'admin_templates/registration/*.html',
'admin_media/css/*.css', 'admin_media/img/admin/*.gif', 'admin_media/css/*.css', 'admin_media/img/admin/*.gif',
'admin_media/img/admin/*.png', 'admin_media/js/*.js', 'admin_media/img/admin/*.png', 'admin_media/js/*.js',
'admin_media/js/admin/*js'], 'admin_media/js/admin/*js', 'locale/*/*/*.mo'],
}, },
scripts = ['django/bin/django-admin.py'], scripts = ['django/bin/django-admin.py'],
zip_safe = False, zip_safe = False,