| 1 |
# (C) Copyright 2002-2006 Nuxeo SAS <http://nuxeo.com> |
|---|
| 2 |
# Author: Florent Guillaume <fg@nuxeo.com> |
|---|
| 3 |
# |
|---|
| 4 |
# This program is free software; you can redistribute it and/or modify |
|---|
| 5 |
# it under the terms of the GNU General Public License version 2 as published |
|---|
| 6 |
# by the Free Software Foundation. |
|---|
| 7 |
# |
|---|
| 8 |
# This program is distributed in the hope that it will be useful, |
|---|
| 9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 |
# GNU General Public License for more details. |
|---|
| 12 |
# |
|---|
| 13 |
# You should have received a copy of the GNU General Public License |
|---|
| 14 |
# along with this program; if not, write to the Free Software |
|---|
| 15 |
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
|---|
| 16 |
# 02111-1307, USA. |
|---|
| 17 |
# |
|---|
| 18 |
# $Id$ |
|---|
| 19 |
|
|---|
| 20 |
import re |
|---|
| 21 |
|
|---|
| 22 |
import Globals |
|---|
| 23 |
from DocumentTemplate.DT_Util import ustr |
|---|
| 24 |
from OFS.SimpleItem import SimpleItem |
|---|
| 25 |
|
|---|
| 26 |
NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*" |
|---|
| 27 |
_interp_regex = re.compile(r'(?<!\$)(\$(?:%(n)s|{%(n)s}))' % {'n': NAME_RE}) |
|---|
| 28 |
_get_var_regex = re.compile(r'%(n)s' % {'n': NAME_RE}) |
|---|
| 29 |
_charset_regex = re.compile( |
|---|
| 30 |
r'text/[0-9a-z]+\s*;\s*charset=([-_0-9a-z]+)(?:(?:\s*;)|\Z)', |
|---|
| 31 |
re.IGNORECASE) |
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
def _findEncoding(): |
|---|
| 35 |
encoding = 'iso-8859-15' |
|---|
| 36 |
get_request = getattr(Globals, 'get_request', None) |
|---|
| 37 |
if get_request is None: |
|---|
| 38 |
request = None |
|---|
| 39 |
else: |
|---|
| 40 |
request = get_request() |
|---|
| 41 |
if request is not None: |
|---|
| 42 |
ct = request.RESPONSE.headers.get('content-type') |
|---|
| 43 |
if ct: |
|---|
| 44 |
match = _charset_regex.match(ct) |
|---|
| 45 |
if match: |
|---|
| 46 |
encoding = match.group(1) |
|---|
| 47 |
return encoding |
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
class Domain(SimpleItem): |
|---|
| 51 |
"""Translation domain.""" |
|---|
| 52 |
# Inherit from a Persistent base class to be able to lookup placefully. |
|---|
| 53 |
|
|---|
| 54 |
meta_type = 'Placeful Domain' # XXX unused |
|---|
| 55 |
|
|---|
| 56 |
# __implements__ = IDomain |
|---|
| 57 |
|
|---|
| 58 |
def __init__(self, domain): |
|---|
| 59 |
self._domain = domain |
|---|
| 60 |
|
|---|
| 61 |
def getMessageCatalog(self, lang=None): |
|---|
| 62 |
"""Get the message catalog implementing this domain.""" |
|---|
| 63 |
raise NotImplementedError |
|---|
| 64 |
|
|---|
| 65 |
def noTranslation(self, mapping=None, default=None, **kw): |
|---|
| 66 |
"""Return the correct value when there is no translation.""" |
|---|
| 67 |
if default is None: |
|---|
| 68 |
# In Zope 2.6, we have to return None to use the default |
|---|
| 69 |
return None |
|---|
| 70 |
else: |
|---|
| 71 |
# In Zope 2.7, we cannot return None but we have the default |
|---|
| 72 |
return self._interpolate(default, mapping) |
|---|
| 73 |
|
|---|
| 74 |
# |
|---|
| 75 |
# IDomain API |
|---|
| 76 |
# |
|---|
| 77 |
|
|---|
| 78 |
def translate(self, msgid, mapping=None, context=None, |
|---|
| 79 |
target_language=None, default=None): |
|---|
| 80 |
"""Translate a msgid, maybe doing ${keyword} substitution. |
|---|
| 81 |
|
|---|
| 82 |
msgid is the message id to be translated. |
|---|
| 83 |
mapping is a set of mapping to be applied on ${keywords}. |
|---|
| 84 |
""" |
|---|
| 85 |
# msgid can be '${name} was born in ${country}'. |
|---|
| 86 |
# mapping can be {'country': 'Antarctica', 'name': 'Lomax'}. |
|---|
| 87 |
# context must be adaptable to IUserPreferredLanguages. |
|---|
| 88 |
|
|---|
| 89 |
mc = self.getMessageCatalog(lang=target_language) |
|---|
| 90 |
text = mc.queryMessage(msgid, default=default) |
|---|
| 91 |
if text is None: |
|---|
| 92 |
# No default was passed, and msgid has no translation. |
|---|
| 93 |
# We'll get what's in between the tags where the translate |
|---|
| 94 |
# has been invoked within the template |
|---|
| 95 |
if default is None: |
|---|
| 96 |
default = msgid |
|---|
| 97 |
res = self.noTranslation(mapping=mapping, default=default) |
|---|
| 98 |
else: |
|---|
| 99 |
res = self._interpolate(text, mapping) |
|---|
| 100 |
# Make sure we always return unicode |
|---|
| 101 |
if not isinstance(res, unicode): |
|---|
| 102 |
# This can happen if a non-unicode default is passed |
|---|
| 103 |
res = unicode(res, 'iso-8859-15') |
|---|
| 104 |
return res |
|---|
| 105 |
|
|---|
| 106 |
# |
|---|
| 107 |
# Internal |
|---|
| 108 |
# |
|---|
| 109 |
|
|---|
| 110 |
def _interpolate(self, text, mapping): |
|---|
| 111 |
"""Interpolate ${keyword} substitutions.""" |
|---|
| 112 |
if not mapping: |
|---|
| 113 |
return text |
|---|
| 114 |
|
|---|
| 115 |
# Find all the spots we want to substitute. |
|---|
| 116 |
to_replace = _interp_regex.findall(text) |
|---|
| 117 |
|
|---|
| 118 |
# Now substitute with the variables in mapping. |
|---|
| 119 |
encoding = None |
|---|
| 120 |
for string in to_replace: |
|---|
| 121 |
var = _get_var_regex.findall(string)[0] |
|---|
| 122 |
if mapping.has_key(var): |
|---|
| 123 |
subst = ustr(mapping[var]) |
|---|
| 124 |
try: |
|---|
| 125 |
text = text.replace(string, subst) |
|---|
| 126 |
except UnicodeError: |
|---|
| 127 |
# The string subst contains high-bit chars. |
|---|
| 128 |
# Assume it's encoded in the output encoding. |
|---|
| 129 |
# (This will be the case if Localizer was used.) |
|---|
| 130 |
if encoding is None: |
|---|
| 131 |
encoding = _findEncoding() |
|---|
| 132 |
subst = unicode(subst, encoding, 'ignore') |
|---|
| 133 |
text = text.replace(string, subst) |
|---|
| 134 |
|
|---|
| 135 |
return text |
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
class DummyDomain(Domain): |
|---|
| 139 |
def translate(self, *args, **kw): |
|---|
| 140 |
return self.noTranslation(**kw) |
|---|
| 141 |
def getSelectedLanguage(self): |
|---|
| 142 |
return 'en' |
|---|
| 143 |
def getDefaultLanguage(self): |
|---|
| 144 |
return 'en' |
|---|
| 145 |
def getSupportedLanguages(self): |
|---|
| 146 |
return ('en',) |
|---|