20 |
$Id$ |
$Id$ |
21 |
""" |
""" |
22 |
|
|
23 |
_langPrefsRegistry = [] |
try: |
24 |
|
True |
25 |
|
except NameError: |
26 |
|
True=1 |
27 |
|
False=0 |
28 |
|
|
29 |
|
def lang_normalize(lang): |
30 |
|
return lang.replace('_', '-') |
31 |
|
|
32 |
|
def type_accepted(available, preferred): |
33 |
|
# ex: preferred is text/* and available is text/html |
34 |
|
av = available.split('/') |
35 |
|
pr = preferred.split('/') |
36 |
|
if len(av) < 2 or len(pr) < 2: |
37 |
|
return False |
38 |
|
return pr[1] == '*' and pr[0] == av[0] |
39 |
|
|
40 |
|
def lang_accepted(available, preferred): |
41 |
|
# ex: available is pt, preferred is pt-br |
42 |
|
return available.startswith(preferred) |
43 |
|
|
44 |
def registerLangPrefsMethod(prefs): |
def _false(*a, **kw): |
45 |
if type(prefs) is not type({}): |
pass |
|
prefs = {'klass':prefs,'priority':0} |
|
|
_langPrefsRegistry.append(prefs) |
|
|
_langPrefsRegistry.sort(lambda x, y: cmp(x['priority'], y['priority'])) |
|
46 |
|
|
47 |
def getLangPrefsMethod(env): |
class Negotiator: |
48 |
return _langPrefsRegistry[-1]['klass'](env) |
filters = { |
49 |
|
'content-type': (str.lower,), |
50 |
class DummyUserPreferredLanguages: |
'language': (str.lower, lang_normalize), |
51 |
|
} |
52 |
def __init__(self, env): |
|
53 |
self._env = env |
tests = { |
54 |
|
'content-type': type_accepted, |
55 |
def getPreferredLanguages(self): |
'language': lang_accepted, |
56 |
return self._env.getPreferredLanguages() |
} |
57 |
|
|
58 |
registerLangPrefsMethod(DummyUserPreferredLanguages) |
def getAccepted(self, request, kind='content-type'): |
59 |
|
get = request.get |
60 |
|
custom_name = ('user_%s' % kind).lower() |
61 |
|
if kind == 'content-type': |
62 |
|
header_name = ('HTTP_ACCEPT').upper() |
63 |
|
else: |
64 |
|
header_name = ('HTTP_ACCEPT_%s' % kind).upper() |
65 |
|
|
|
class BrowserLanguages: |
|
|
|
|
|
def __init__(self, context): |
|
|
get = context.get |
|
|
|
|
66 |
try: |
try: |
67 |
user_langs = get('user_language', '') |
user_accepts = get(custom_name, '') |
68 |
http_langs = get('HTTP_ACCEPT_LANGUAGE', '') |
http_accepts = get(header_name, '') |
69 |
except: |
except: |
70 |
from traceback import print_exc |
from traceback import print_exc |
71 |
print_exc() |
print_exc() |
|
self.langs = () |
|
72 |
return |
return |
73 |
if user_langs and http_langs and user_langs == context.cookies.get('user_language'): |
if user_accepts and http_accepts and user_accepts == request.cookies.get('custom_name'): |
74 |
user_langs = user_langs.split(',') |
user_accepts = user_accepts.split(',') |
75 |
http_langs = http_langs.split(',') |
http_accepts = http_accepts.split(',') |
76 |
for l in user_langs: |
for l in user_accepts: |
77 |
if l not in http_langs: |
if l not in http_accepts: |
78 |
req_langs = user_langs + http_langs |
req_accepts = user_accepts + http_accepts |
79 |
break |
break |
80 |
else: |
else: |
81 |
# user_langs is a subset of http_langs |
# user_accepts is a subset of http_accepts |
82 |
context.RESPONSE.expireCookie('user_language', path='/') |
request.RESPONSE.expireCookie('custom_name', path='/') |
83 |
req_langs = http_langs |
req_accepts = http_accepts |
84 |
else: |
else: |
85 |
req_langs = (user_langs +','+ http_langs).split(',') |
req_accepts = (user_accepts +','+ http_accepts).split(',') |
86 |
|
|
87 |
langs = [] |
accepts = [] |
88 |
i=0 |
i=0 |
89 |
length=len(req_langs) |
length=len(req_accepts) |
90 |
|
filters = self.filters.get(kind, ()) |
91 |
|
|
92 |
# parse quality strings and build a tuple |
# parse quality strings and build a tuple |
93 |
# like ((float(quality), lang), (float(quality), lang)) |
# like ((float(quality), lang), (float(quality), lang)) |
94 |
# which is sorted afterwards |
# which is sorted afterwards |
95 |
# if no quality string is given then the list order |
# if no quality string is given then the list order |
96 |
# is used as quality indicator |
# is used as quality indicator |
97 |
for lang in req_langs: |
for accept in req_accepts: |
98 |
lang = lang.strip().lower().replace('_', '-') |
for normalizer in filters: |
99 |
if lang: |
accept = normalizer(accept) |
100 |
l = lang.split(';', 2) |
if accept: |
101 |
|
l = accept.split(';', 2) |
102 |
quality = [] |
quality = [] |
103 |
|
|
104 |
if len(l) == 2: |
if len(l) == 2: |
113 |
if quality == []: |
if quality == []: |
114 |
quality = float(length-i) |
quality = float(length-i) |
115 |
|
|
116 |
language = l[0] |
accepts.append((quality, l[0])) |
|
langs.append((quality, language)) |
|
117 |
i += 1 |
i += 1 |
118 |
|
|
119 |
# sort and reverse it |
# sort and reverse it |
120 |
langs.sort() |
accepts.sort() |
121 |
langs.reverse() |
accepts.reverse() |
122 |
|
|
123 |
# filter quality string |
return [accept[1] for accept in accepts] |
|
langs = map(lambda x: x[1], langs) |
|
|
|
|
|
self.langs = langs |
|
|
|
|
|
def getPreferredLanguages(self): |
|
|
""" |
|
|
""" |
|
|
return self.langs |
|
|
|
|
|
registerLangPrefsMethod({'klass':BrowserLanguages,'priority':10 }) |
|
|
|
|
|
class Negotiator: |
|
124 |
|
|
125 |
|
def negotiate(self, choices, request, kind='content-type'): |
126 |
def getLanguage(self, langs, env): |
choices = tuple(choices) |
127 |
langs = tuple(langs) |
cache_name = '_pts_negotiator_cache_%s' % kind |
128 |
try: |
try: |
129 |
cache = env.other['_pts_negotiator_cache'] |
cache = request.other[cache_name] |
130 |
except KeyError: |
except KeyError: |
131 |
# Store cache in request object |
# Store cache in request object |
132 |
cache = {} |
cache = {} |
133 |
env.set('_pts_negotiator_cache', cache) |
request.set(cache_name, cache) |
134 |
try: |
try: |
135 |
return cache[langs] |
return cache[choices] |
136 |
except KeyError: |
except KeyError: |
137 |
cache[langs] = self._getLanguage(langs, env) |
cache[choices] = self._negotiate(choices, request, kind) |
138 |
return cache[langs] |
return cache[choices] |
139 |
|
|
140 |
def _getLanguage(self, langs, env): |
def _negotiate(self, choices, request, kind): |
141 |
envprefs = getLangPrefsMethod(env) |
userchoices = self.getAccepted(request, kind) |
142 |
userlangs = envprefs.getPreferredLanguages() |
# Prioritize on the user preferred choices. Return the first user |
143 |
# Prioritize on the user preferred languages. Return the first user |
# preferred choice that the object has available. |
144 |
# preferred language that the object has available. |
test = self.tests.get(kind, _false) |
145 |
for lang in userlangs: |
for choice in userchoices: |
146 |
if lang in langs: |
if choice in choices: |
147 |
return lang |
return choice |
148 |
for l_avail in langs: |
for l_avail in choices: |
149 |
if l_avail.startswith(lang): |
if test(l_avail, choice): |
150 |
return l_avail |
return l_avail |
151 |
return None |
return None |
152 |
|
|
153 |
def getLanguages(self, env): |
# backwards compatibility... should be deprecated |
154 |
envprefs = getLangPrefsMethod(env) |
def getLanguage(self, langs, request): |
155 |
return envprefs.getPreferredLanguages() |
return self.negotiate(langs, request, 'language') |
156 |
|
|
157 |
|
def getLanguages(self, request): |
158 |
|
return self.getAccepted(request, 'language') |
159 |
|
|
160 |
|
|
161 |
negotiator = Negotiator() |
negotiator = Negotiator() |