Skip to content

Commit

Permalink
added infrastructure code for later javascript translating (currently…
Browse files Browse the repository at this point in the history
… not active)

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1529 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
Georg Bauer committed Dec 4, 2005
1 parent 946bd1e commit 5917fdc
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 15 deletions.
55 changes: 41 additions & 14 deletions django/bin/make-messages.py
Expand Up @@ -7,6 +7,8 @@

from django.utils.translation import templateize

pythonize_re = re.compile(r'\n\s*//')

localedir = None

if os.path.isdir(os.path.join('conf', 'locale')):
Expand Down Expand Up @@ -39,6 +41,9 @@
elif o == '-a':
all = True

if domain not in ('django', 'djangojs'):
print "currently make-messages.py only supports domains 'django' and 'djangojs'"
sys.exit(1)
if (lang is None and not all) or domain is None:
print "usage: make-messages.py -l <language>"
print " or: make-messages.py -a"
Expand Down Expand Up @@ -66,7 +71,28 @@

for (dirpath, dirnames, filenames) in os.walk("."):
for file in filenames:
if file.endswith('.py') or file.endswith('.html'):
if domain == 'djangojs' and file.endswith('.js'):
if verbose: sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
src = open(os.path.join(dirpath, file), "rb").read()
src = pythonize_re.sub('\n#', src)
open(os.path.join(dirpath, '%s.py' % file), "wb").write(src)
thefile = '%s.py' % file
cmd = 'xgettext %s -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy -o - "%s"' % (
os.path.exists(potfile) and '--omit-header' or '', domain, os.path.join(dirpath, thefile))
(stdin, stdout, stderr) = os.popen3(cmd, 'b')
msgs = stdout.read()
errors = stderr.read()
if errors:
print "errors happened while running xgettext on %s" % file
print errors
sys.exit(8)
old = '#: '+os.path.join(dirpath, thefile)[2:]
new = '#: '+os.path.join(dirpath, file)[2:]
msgs = msgs.replace(old, new)
if msgs:
open(potfile, 'ab').write(msgs)
os.unlink(os.path.join(dirpath, thefile))
elif domain == 'django' and (file.endswith('.py') or file.endswith('.html')):
thefile = file
if file.endswith('.html'):
src = open(os.path.join(dirpath, file), "rb").read()
Expand All @@ -91,22 +117,23 @@
if thefile != file:
os.unlink(os.path.join(dirpath, thefile))

(stdin, stdout, stderr) = os.popen3('msguniq %s' % potfile, 'b')
msgs = stdout.read()
errors = stderr.read()
if errors:
print "errors happened while running msguniq"
print errors
sys.exit(8)
open(potfile, 'w').write(msgs)
if os.path.exists(pofile):
(stdin, stdout, stderr) = os.popen3('msgmerge -q %s %s' % (pofile, potfile), 'b')
if os.path.exists(potfile):
(stdin, stdout, stderr) = os.popen3('msguniq %s' % potfile, 'b')
msgs = stdout.read()
errors = stderr.read()
if errors:
print "errors happened while running msgmerge"
print "errors happened while running msguniq"
print errors
sys.exit(8)
open(pofile, 'wb').write(msgs)
os.unlink(potfile)
open(potfile, 'w').write(msgs)
if os.path.exists(pofile):
(stdin, stdout, stderr) = os.popen3('msgmerge -q %s %s' % (pofile, potfile), 'b')
msgs = stdout.read()
errors = stderr.read()
if errors:
print "errors happened while running msgmerge"
print errors
sys.exit(8)
open(pofile, 'wb').write(msgs)
os.unlink(potfile)

19 changes: 19 additions & 0 deletions django/utils/text.py
@@ -1,5 +1,7 @@
import re

from django.conf.settings import DEFAULT_CHARSET

# Capitalizes the first letter of a string.
capfirst = lambda x: x and x[0].upper() + x[1:]

Expand Down Expand Up @@ -90,3 +92,20 @@ def compress_string(s):
zfile.write(s)
zfile.close()
return zbuf.getvalue()

ustring_re = re.compile(u"([\u0080-\uffff])")
def javascript_quote(s):

def fix(match):
return r"\u%04x" % ord(match.group(1))

if type(s) == str:
s = s.decode(DEFAULT_ENCODING)
elif type(s) != unicode:
raise TypeError, s
s = s.replace('\\', '\\\\')
s = s.replace('\n', '\\n')
s = s.replace('\t', '\\t')
s = s.replace("'", "\\'")
return str(ustring_re.sub(fix, s))

15 changes: 15 additions & 0 deletions django/utils/translation.py
Expand Up @@ -212,6 +212,21 @@ def get_language():
from django.conf.settings import LANGUAGE_CODE
return LANGUAGE_CODE

def catalog():
"""
This function returns the current active catalog for further processing.
This can be used if you need to modify the catalog or want to access the
whole message catalog instead of just translating one string.
"""
global _default, _active
t = _active.get(currentThread(), None)
if t is not None:
return t
if _default is None:
from django.conf import settings
_default = translation(settings.LANGUAGE_CODE)
return _default

def gettext(message):
"""
This function will be patched into the builtins module to provide the _
Expand Down
169 changes: 168 additions & 1 deletion django/views/i18n.py
@@ -1,5 +1,12 @@
import re
import os

import gettext as gettext_module

from django.utils import httpwrappers
from django.utils.translation import check_for_language
from django.utils.translation import check_for_language, activate, to_locale, get_language
from django.utils.text import javascript_quote
from django.conf import settings

def set_language(request):
"""
Expand All @@ -20,3 +27,163 @@ def set_language(request):
else:
response.set_cookie('django_language', lang_code)
return response

NullSource = """
/* gettext identity library */
function gettext(msgid) {
return msgid;
}
function ngettext(singular, plural, count) {
if (count == 1) {
return singular;
} else {
return plural;
}
}
function gettext_noop(msgid) {
return msgid;
}
"""

LibHead = """
/* gettext library */
var catalog = new Array();
"""

LibFoot = """
function gettext(msgid) {
var value = catalog[msgid];
if (typeof(value) == 'undefined') {
return msgid;
} else {
if (typeof(value) == 'string') {
return value;
} else {
return value[0];
}
}
}
function ngettext(singular, plural, count) {
value = catalog[singular];
if (typeof(value) == 'undefined') {
if (count == 1) {
return singular;
} else {
return plural;
}
} else {
return value[pluralidx(count)];
}
}
function gettext_noop(msgid) {
return msgid;
}
"""

SimplePlural = """
function pluralidx(count) {
if (count == 1) {
return 0;
} else {
return 1;
}
}
"""

InterPolate = r"""
function interpolate(fmt, obj, named) {
if (named) {
return fmt.replace(/%\(\w+\)s/, function(match){return String(obj[match.slice(2,-2)])});
} else {
return fmt.replace(/%s/, function(match){return String(obj.shift())});
}
}
"""

def javascript_catalog(request, domain='djangojs', packages=None):
"""
Returns the selected language catalog as a javascript library.
Receives the list of packages to check for translations in the
packages parameter either from an infodict or as a +-delimited
string from the request. Default is 'django.conf'.
Additionally you can override the gettext domain for this view,
but usually you don't want to do that, as JavaScript messages
go to the djangojs domain. But this might be needed if you
deliver your JavaScript source from Django templates.
"""
if request.GET:
if request.GET.has_key('language'):
if check_for_language(request.GET['language']):
activate(request.GET['language'])
if packages is None:
packages = ['django.conf']
if type(packages) in (str, unicode):
packages = packages.split('+')
default_locale = to_locale(settings.LANGUAGE_CODE)
locale = to_locale(get_language())
t = {}
paths = []
for package in packages:
p = __import__(package, {}, {}, [''])
path = os.path.join(os.path.dirname(p.__file__), 'locale')
paths.append(path)
#!!! add loading of catalogs from settings.LANGUAGE_CODE and request.LANGUAGE_CODE!
try:
catalog = gettext_module.translation(domain, path, [default_locale])
except IOError, e:
catalog = None
if catalog is not None:
t.update(catalog._catalog)
if locale != default_locale:
for path in paths:
try:
catalog = gettext_module.translation(domain, path, [locale])
except IOError, e:
catalog = None
if catalog is not None:
t.update(catalog._catalog)
src = [LibHead]
plural = None
for l in t[''].split('\n'):
if l.startswith('Plural-Forms:'):
plural = l.split(':',1)[1].strip()
if plural is not None:
# this should actually be a compiled function of a typical plural-form:
# Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
plural = [el.strip() for el in plural.split(';') if el.strip().startswith('plural=')][0].split('=',1)[1]
src.append('function pluralidx(n) {\n return %s;\n}\n' % plural)
else:
src.append(SimplePlural)
csrc = []
pdict = {}
for k, v in t.items():
if k == '':
continue
if type(k) in (str, unicode):
csrc.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(v)))
elif type(k) == tuple:
if not pdict.has_key(k[0]):
pdict[k[0]] = k[1]
else:
pdict[k[0]] = max(k[1], pdict[k[0]])
csrc.append("catalog['%s'][%d] = '%s';\n" % (javascript_quote(k[0]), k[1], javascript_quote(v)))
else:
raise TypeError, k
csrc.sort()
for k,v in pdict.items():
src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
src.extend(csrc)
src.append(LibFoot)
src.append(InterPolate)
src = ''.join(src)
return httpwrappers.HttpResponse(src, 'text/javascript')

0 comments on commit 5917fdc

Please sign in to comment.