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:
parent
a60b2050c6
commit
98d7430609
24
django/bin/compile-messages.py
Executable file
24
django/bin/compile-messages.py
Executable 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
47
django/bin/make-messages.py
Executable 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)
|
||||
|
@ -23,13 +23,13 @@
|
||||
{% endif %}
|
||||
|
||||
{% 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 %}
|
||||
<td class="x50"> </td>
|
||||
{% endif %}
|
||||
|
||||
{% 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 %}
|
||||
<td class="x75"> </td>
|
||||
{% endif %}
|
||||
@ -47,12 +47,12 @@
|
||||
{% block sidebar %}
|
||||
<div id="content-related">
|
||||
<div class="module" id="recent-actions-module">
|
||||
<h2>Recent Actions</h2>
|
||||
<h3>My Actions</h3>
|
||||
<h2>{% i18n _('Recent Actions') %}</h2>
|
||||
<h3>{% i18n _('My Actions') %}</h3>
|
||||
{% load auth.log %}
|
||||
{% get_admin_log 10 as admin_log for_user user %}
|
||||
{% if not admin_log %}
|
||||
<p>None available</p>
|
||||
<p>{% i18n _('None available') %}</p>
|
||||
{% else %}
|
||||
<ul class="actionlist">
|
||||
{% for entry in admin_log %}
|
||||
|
BIN
django/conf/locale/de/LC_MESSAGES/django.mo
Normal file
BIN
django/conf/locale/de/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
51
django/conf/locale/de/LC_MESSAGES/django.po
Normal file
51
django/conf/locale/de/LC_MESSAGES/django.po
Normal 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"
|
||||
|
@ -55,3 +55,10 @@ for k in dir(me):
|
||||
if not k.startswith('_') and k != 'me' and k != k.upper():
|
||||
delattr(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
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
"Default tags used by the template system, available to all templates."
|
||||
|
||||
import re
|
||||
import sys
|
||||
import template
|
||||
|
||||
from django.utils import translation
|
||||
|
||||
class CommentNode(template.Node):
|
||||
def render(self, context):
|
||||
return ''
|
||||
@ -283,6 +286,28 @@ class WidthRatioNode(template.Node):
|
||||
return ''
|
||||
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):
|
||||
"""
|
||||
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")
|
||||
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('cycle', do_cycle)
|
||||
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('templatetag', do_templatetag)
|
||||
template.register_tag('widthratio', do_widthratio)
|
||||
template.register_tag('i18n', do_i18n)
|
||||
|
||||
|
@ -53,7 +53,7 @@ class CriticalValidationError(Exception):
|
||||
|
||||
def isAlphaNumeric(field_data, all_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):
|
||||
if not alnumurl_re.search(field_data):
|
||||
|
38
django/middleware/locale.py
Normal file
38
django/middleware/locale.py
Normal 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
195
django/utils/translation.py
Normal 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
|
||||
|
2
setup.py
2
setup.py
@ -17,7 +17,7 @@ setup(
|
||||
'admin_templates/registration/*.html',
|
||||
'admin_media/css/*.css', 'admin_media/img/admin/*.gif',
|
||||
'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'],
|
||||
zip_safe = False,
|
||||
|
Loading…
x
Reference in New Issue
Block a user