faster and leaner languages, should be 100% backward compatible, plurals work again

This commit is contained in:
mdipierro
2012-08-27 10:57:26 -05:00
parent a3dfc9f621
commit 3ff4176e1f
3 changed files with 135 additions and 152 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.00.0 (2012-08-27 10:13:44) dev Version 2.00.0 (2012-08-27 10:57:21) dev
+4 -4
View File
@@ -23,9 +23,9 @@ except ImportError:
GIT_MISSING = 'requires python-git module, but not installed or incompatible version' GIT_MISSING = 'requires python-git module, but not installed or incompatible version'
from gluon.languages import (regex_language, read_possible_languages, from gluon.languages import (regex_language, read_possible_languages,
read_possible_plurals, lang_sampling, lang_sampling,
read_dict, write_dict, read_plural_dict, read_dict, write_dict, read_plural_dict,
write_plural_dict) write_plural_dict, PLURAL_RULES)
if DEMO_MODE and request.function in ['change_password','pack','pack_plugin','upgrade_web2py','uninstall','cleanup','compile_app','remove_compiled_app','delete','delete_plugin','create_file','upload_file','update_languages','reload_routes','git_push','git_pull']: if DEMO_MODE and request.function in ['change_password','pack','pack_plugin','upgrade_web2py','uninstall','cleanup','compile_app','remove_compiled_app','delete','delete_plugin','create_file','upload_file','update_languages','reload_routes','git_push','git_pull']:
@@ -950,7 +950,7 @@ def design():
languages = sorted(all_languages) languages = sorted(all_languages)
plural_rules = {} plural_rules = {}
all_plurals=read_possible_plurals() all_plurals = PLURAL_RULES
for langfile,lang in all_languages.iteritems(): for langfile,lang in all_languages.iteritems():
lang=lang.strip() lang=lang.strip()
match_language = regex_language.match(lang) match_language = regex_language.match(lang)
@@ -961,7 +961,7 @@ def design():
plang = lang_sampling(match_language, all_plurals.keys()) plang = lang_sampling(match_language, all_plurals.keys())
if plang: if plang:
plural=all_plurals[plang] plural=all_plurals[plang]
plural_rules[langfile]=(plural[0],plang,plural[1],plural[3]) plural_rules[langfile]=(plural[0],plang,plural[4],plural[3])
else: else:
plural_rules[langfile]=(0,lang,'plural_rules-%s.py'%lang,'') plural_rules[langfile]=(0,lang,'plural_rules-%s.py'%lang,'')
+98 -115
View File
@@ -34,7 +34,7 @@ osep = os.sep
isdir = os.path.isdir isdir = os.path.isdir
is_gae = settings.global_settings.web2py_runtime_gae is_gae = settings.global_settings.web2py_runtime_gae
DEFAULT_ACCEPT_LANGUAGE = 'en' DEFAULT_LANGUAGE = 'en'
# DEFAULT PLURAL-FORMS RULES: # DEFAULT PLURAL-FORMS RULES:
# language doesn't use plural forms # language doesn't use plural forms
@@ -76,23 +76,24 @@ regex_param=re.compile(r'{(?P<s>.+?)}')
regex_language = \ regex_language = \
re.compile('^([a-zA-Z]{2})(\-[a-zA-Z]{2})?(\-[a-zA-Z]+)?$') re.compile('^([a-zA-Z]{2})(\-[a-zA-Z]{2})?(\-[a-zA-Z]+)?$')
regex_langfile = re.compile('^[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$') regex_langfile = re.compile('^[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
regex_langinfo = re.compile("^[^'\"]*['\"]([^'\"]*)['\"]\s*:\s*['\"]([^'\"]*)['\"].*$")
regex_backslash = re.compile(r"\\([\\{}%])") regex_backslash = re.compile(r"\\([\\{}%])")
regex_plural = re.compile('%({.+?})') regex_plural = re.compile('%({.+?})')
regex_plural_dict = re.compile('^{(?P<w>[^()[\]][^()[\]]*?)\((?P<n>[^()\[\]]+)\)}$') # %%{word(varname or number)} regex_plural_dict = re.compile('^{(?P<w>[^()[\]][^()[\]]*?)\((?P<n>[^()\[\]]+)\)}$') # %%{word(varname or number)}
regex_plural_tuple = re.compile('^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word} regex_plural_tuple = re.compile('^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
regex_plural_q = re.compile('^asdf$') # %%{?word?cnt}, %%{??cnt} or %%{?cnt}
regex_plural_rules = re.compile('^plural_rules-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$') regex_plural_rules = re.compile('^plural_rules-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
upper_fun = lambda s: unicode(s,'utf-8').upper().encode('utf-8') # UTF8 helper functions
title_fun = lambda s: unicode(s,'utf-8').title().encode('utf-8') def upper_fun(s):
cap_fun = lambda s: unicode(s,'utf-8').capitalize().encode('utf-8') return unicode(s,'utf-8').upper().encode('utf-8')
def title_fun(s):
return unicode(s,'utf-8').title().encode('utf-8')
def cap_fun(s):
return lambda s: unicode(s,'utf-8').capitalize().encode('utf-8')
ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f') ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}") ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
# cache of translated messages: # cache of translated messages:
# of structure: # global_language_cache:
# { 'languages/xx.py': # { 'languages/xx.py':
# ( {"def-message": "xx-message", # ( {"def-message": "xx-message",
# ... # ...
@@ -100,35 +101,38 @@ ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
# 'languages/yy.py': ( {dict}, lock_object ) # 'languages/yy.py': ( {dict}, lock_object )
# ... # ...
# } # }
tcache={}
global_language_cache={}
def get_from_cache(cache, val, fun): def get_from_cache(cache, val, fun):
lock=cache[1] lang_dict, lock = cache
lock.acquire() lock.acquire()
try: try:
result=cache[0].get(val); result = lang_dict.get(val);
finally: finally:
lock.release() lock.release()
if result: if result:
return result return result
lock.acquire() lock.acquire()
try: try:
result=cache[0].setdefault(val, fun()) result = lang_dict.setdefault(val, fun())
finally: finally:
lock.release() lock.release()
return result return result
def clear_cache(cache): def clear_cache(filename):
lock=cache[1] cache = global_language_cache.setdefault(
filename, ({}, allocate_lock()))
lang_dict, lock = cache
lock.acquire() lock.acquire()
try: try:
cache[0].clear(); lang_dict.clear();
finally: finally:
lock.release() lock.release()
def lang_sampling(lang_tuple, langlist): def lang_sampling(lang_tuple, langlist):
""" search *lang_tuple* in *langlist* """
search *lang_tuple* in *langlist*
Args: Args:
lang_tuple (tuple of strings): ('aa'[[,'-bb'],'-cc']) lang_tuple (tuple of strings): ('aa'[[,'-bb'],'-cc'])
@@ -159,11 +163,8 @@ def lang_sampling(lang_tuple, langlist):
def read_dict_aux(filename): def read_dict_aux(filename):
fp = portalocker.LockedFile(filename, 'r') lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
lang_text = fp.read().replace('\r\n', '\n') clear_cache(filename)
fp.close()
# clear cache of processed messages:
clear_cache(tcache.setdefault(filename, ({}, allocate_lock())))
try: try:
return safe_eval(lang_text) or {} return safe_eval(lang_text) or {}
except Exception, e: except Exception, e:
@@ -180,7 +181,8 @@ def read_dict(filename):
def get_lang_info(lang, langdir): def get_lang_info(lang, langdir):
"""retrieve lang information from *langdir*/*lang*.py file. """
retrieve lang information from *langdir*/*lang*.py file.
Read few strings from lang.py file until keys !langname!, Read few strings from lang.py file until keys !langname!,
!langcode! or keys greater then '!*' were found !langcode! or keys greater then '!*' were found
@@ -193,27 +195,10 @@ def get_lang_info(lang, langdir):
e.g.: ('en', 'English', 1338549043.0) e.g.: ('en', 'English', 1338549043.0)
""" """
filename = ospath.join(langdir, lang+'.py') filename = ospath.join(langdir, lang+'.py')
langcode=langname='' d = read_dict(filename)
f = portalocker.LockedFile(filename, 'r') langcode = d.get('!langcode!',DEFAULT_LANGUAGE)
try: langname = d.get('!langname!',langcode)
while not (langcode and langname): return (langcode, langname or langcode, ostat(filename).st_mtime)
line = f.readline()
if not line:
break
match=regex_langinfo.match(line)
if match:
k = match.group(1)
if k == '!langname!':
langname = match.group(2)
elif k == '!langcode!':
langcode = match.group(2)
elif k[0:1] > '!':
break
finally:
f.close()
if not langcode:
langcode = lang if lang != 'default' else 'en'
return langcode, langname or langcode, ostat(filename).st_mtime
def read_possible_languages(appdir): def read_possible_languages(appdir):
langs = {} langs = {}
@@ -223,9 +208,9 @@ def read_possible_languages(appdir):
if regex_langfile.match(filename) or filename=='default.py': if regex_langfile.match(filename) or filename=='default.py':
lang = filename[:-3] lang = filename[:-3]
langs[lang] = get_lang_info(lang, langdir) langs[lang] = get_lang_info(lang, langdir)
if not 'default' in langs: if not 'en' in langs:
# if default.py is not found, add default value: # if default.py is not found, add default value:
langs['default'] = ('en', 'English', 0) langs['en'] = ('en', 'English', 0)
return langs return langs
def read_global_plural_rules(filename): def read_global_plural_rules(filename):
@@ -240,14 +225,13 @@ def read_global_plural_rules(filename):
e.g.: (3, <function>, <function>, ok) e.g.: (3, <function>, <function>, ok)
""" """
env = {} env = {}
lock = portalocker.LockedFile(filename, 'r') data = portalocker.read_locked(filename)
try: try:
execfile(filename) in env exec(data) in env
status='ok' status='ok'
except Exception, e: except Exception, e:
status='Syntax error in %s (%s)' % (filename, e) status='Syntax error in %s (%s)' % (filename, e)
logging.error(status) logging.error(status)
lock.close()
nplurals = env.get('nplurals', DEFAULT_NPLURALS) nplurals = env.get('nplurals', DEFAULT_NPLURALS)
get_plural_id = env.get('get_plural_id', DEFAULT_GET_PLURAL_ID) get_plural_id = env.get('get_plural_id', DEFAULT_GET_PLURAL_ID)
construct_plural_form = env.get('construct_plural_form', construct_plural_form = env.get('construct_plural_form',
@@ -256,7 +240,8 @@ def read_global_plural_rules(filename):
def read_possible_plurals(): def read_possible_plurals():
""" create list of all possible plural rules files """
create list of all possible plural rules files
result is cached to increase speed result is cached to increase speed
""" """
pdir = abspath('gluon','contrib','rules') pdir = abspath('gluon','contrib','rules')
@@ -268,21 +253,20 @@ def read_possible_plurals():
fname = ospath.join(pdir, pname) fname = ospath.join(pdir, pname)
n, f1, f2, status = read_global_plural_rules(fname) n, f1, f2, status = read_global_plural_rules(fname)
if status == 'ok': if status == 'ok':
plurals[lang] = (lang, n, f1, f2) plurals[lang] = (lang, n, f1, f2, pname)
plurals['default'] = ('default', plurals['default'] = ('default',
DEFAULT_NPLURALS, DEFAULT_NPLURALS,
DEFAULT_GET_PLURAL_ID, DEFAULT_GET_PLURAL_ID,
DEFAULT_CONSTRUCTOR_PLURAL_FORM) DEFAULT_CONSTRUCTOR_PLURAL_FORM,
None)
return plurals return plurals
PLURAL_RULES = read_possible_plurals() PLURAL_RULES = read_possible_plurals()
def read_plural_dict_aux(filename): def read_plural_dict_aux(filename):
fp = portalocker.LockedFile(filename, 'r') lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
lang_text = fp.read().replace('\r\n', '\n')
fp.close()
try: try:
return safe_eval(lang_text) or {} return eval(lang_text) or {}
except Exception, e: except Exception, e:
status='Syntax error in %s (%s)' % (filename, e) status='Syntax error in %s (%s)' % (filename, e)
logging.error(status) logging.error(status)
@@ -422,13 +406,10 @@ class lazyT(object):
return lazyT(self.m, symbols, self.T, self.f, self.t, self.M) return lazyT(self.m, symbols, self.T, self.f, self.t, self.M)
class translator(object): class translator(object):
""" """
this class is instantiated by gluon.compileapp.build_environment this class is instantiated by gluon.compileapp.build_environment
as the T object as the T object
:: ::
T.force(None) # turns off translation T.force(None) # turns off translation
T.force('fr, it') # forces web2py to translate using fr.py or it.py T.force('fr, it') # forces web2py to translate using fr.py or it.py
@@ -436,11 +417,12 @@ class translator(object):
notice 1: there is no need to force since, by default, T uses notice 1: there is no need to force since, by default, T uses
http_accept_language to determine a translation file. http_accept_language to determine a translation file.
notice 2:
notice 2: en and en-en are considered different languages! en and en-en are considered different languages!
notice 3:
notice 3: if language xx-yy is not found force() probes other similar if language xx-yy is not found force() probes other similar
languages using such algorithm: xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py languages using such algorithm:
xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py
""" """
def __init__(self, request): def __init__(self, request):
@@ -448,8 +430,8 @@ class translator(object):
self.folder = request.folder self.folder = request.folder
self.langpath = ospath.join(self.folder,'languages') self.langpath = ospath.join(self.folder,'languages')
self.filenames = set(os.listdir(self.langpath)) self.filenames = set(os.listdir(self.langpath))
self.cache = tcache.setdefault(None, ({},allocate_lock()))
self.http_accept_language = request.env.http_accept_language self.http_accept_language = request.env.http_accept_language
# self.cache # filled in self.force()
# self.accepted_language = None # filled in self.force() # self.accepted_language = None # filled in self.force()
# self.language_file = None # filled in self.force() # self.language_file = None # filled in self.force()
# self.plural_language = None # filled in self.force() # self.plural_language = None # filled in self.force()
@@ -468,12 +450,9 @@ class translator(object):
self.filter = markmin self.filter = markmin
self.ftag = 'markmin' self.ftag = 'markmin'
def get_possible_languages(self): def get_possible_languages(self):
possible_languages = [ return [lang[:-3] for lang in self.filenames \
lang[:-3] for lang in self.filenames \
if regex_langfile.match(lang)] if regex_langfile.match(lang)]
return possible_languages
def set_current_languages(self, *languages): def set_current_languages(self, *languages):
""" """
@@ -488,7 +467,8 @@ class translator(object):
self.force(self.http_accept_language) self.force(self.http_accept_language)
def set_plural(self, language): def set_plural(self, language):
""" initialize plural forms subsystem """
initialize plural forms subsystem
invoked from self.force() invoked from self.force()
""" """
lang = language[:2] lang = language[:2]
@@ -496,11 +476,14 @@ class translator(object):
self.nplurals, self.nplurals,
self.get_plural_id, self.get_plural_id,
self.construct_plural_form, self.construct_plural_form,
self.plural_filename
) = PLURAL_RULES.get(language,PLURAL_RULES['default']) ) = PLURAL_RULES.get(language,PLURAL_RULES['default'])
filename = 'plural-%s.py' % self.plural_language for lang in (language, language[:5], language[:2]):
filename = 'plural-%s.py' % lang
if filename in self.filenames: if filename in self.filenames:
self.plural_file = ospath.join(self.langpath,filename) self.plural_file = ospath.join(self.langpath,filename)
self.plural_dict = read_plural_dict(self.plural_file) self.plural_dict = read_plural_dict(self.plural_file)
break
else: else:
self.plural_file = None self.plural_file = None
self.plural_dict = {} self.plural_dict = {}
@@ -508,7 +491,7 @@ class translator(object):
def plural(self, word, n): def plural(self, word, n):
""" """
get plural form of word for number *n* get plural form of word for number *n*
NOTE: *word" MUST be defined in current language NOTE: *word* MUST be defined in current language
(T.accepted_language) (T.accepted_language)
invoked from T()/M() in %%{} tag invoked from T()/M() in %%{} tag
@@ -520,25 +503,27 @@ class translator(object):
(str): word in appropriate singular/plural form (str): word in appropriate singular/plural form
""" """
nplurals = self.nplurals nplurals = self.nplurals
if word: if int(n)==1:
id = self.get_plural_id(abs(int(n))) return word
if id > 0: elif word:
id = min(int(n)-1,1) # self.get_plural_id(abs(int(n)))
# id = 0 first plural form
# id = 1 second plural form
# etc.
forms = self.plural_dict.get(word, []) forms = self.plural_dict.get(word, [])
if forms: if len(forms)>=id:
form = forms.get(id-1) # have this plural form
if form: return forms[id-1]
return form else:
# guessing this plural form
forms += ['']*(nplurals-len(forms)-1)
form = self.construct_plural_form(word, id) form = self.construct_plural_form(word, id)
if len(forms) < nplurals-1:
forms.extend('' for i in xrange(nplurals-len(forms)-1))
forms[id-1] = form forms[id-1] = form
self.plural_dict[word] = forms self.plural_dict[word] = forms
if (self.plural_file and if self.plural_file and not is_gae:
not settings.global_settings.web2py_runtime_gae):
write_plural_dict(self.plural_file, write_plural_dict(self.plural_file,
self.plural_dict) self.plural_dict)
return form return form
return word
def get_possible_languages_info(self, lang=None): def get_possible_languages_info(self, lang=None):
""" """
@@ -556,9 +541,9 @@ class translator(object):
langname(from !langname! key), langname(from !langname! key),
langfile_mtime ) } langfile_mtime ) }
""" """
if lang: info = read_possible_languages(self.folder)
return read_possible_languages(self.folder).get(lang) if lang: info = ingo.get(lang)
return read_possible_languages(self.folder) return info
def force(self, *languages): def force(self, *languages):
""" """
@@ -572,7 +557,6 @@ class translator(object):
default language will be selected if none default language will be selected if none
of them matches possible_languages. of them matches possible_languages.
""" """
global tcache
language = '' language = ''
if isinstance(languages,str): if isinstance(languages,str):
languages = regex_language.findall(languages.lower()) languages = regex_language.findall(languages.lower())
@@ -593,21 +577,20 @@ class translator(object):
break break
else: else:
if 'default.py' in self.filenames: if 'default.py' in self.filenames:
language = DEFAULT_ACCEPT_LANGUAGE language = DEFAULT_LANGUAGE
langfile = 'default.py' langfile = 'default.py'
else: else:
language = DEFAULT_ACCEPT_LANGUAGE language = DEFAULT_LANGUAGE
langfile = None langfile = None
self.accepted_language = language self.accepted_language = language
if langfile: if langfile:
self.language_file = ospath.join(self.langpath,langfile) self.language_file = ospath.join(self.langpath,langfile)
self.t = read_dict(self.language_file) self.t = read_dict(self.language_file)
self.cache = tcache.setdefault(self.language_file,
({},allocate_lock()))
else: else:
self.language_file = None self.language_file = None
self.t = {} self.t = {}
self.cache = tcache[None] self.cache = global_language_cache.setdefault(
self.language_file,({},allocate_lock()))
self.set_plural(language) self.set_plural(language)
return languages return languages
@@ -639,25 +622,29 @@ class translator(object):
prefix = '@'+(ftag or 'userdef')+'\x01' prefix = '@'+(ftag or 'userdef')+'\x01'
else: else:
prefix = '@'+self.ftag+'\x01' prefix = '@'+self.ftag+'\x01'
message = get_from_cache(self.cache, prefix+message, message = get_from_cache(
self.cache, prefix+message,
lambda: get_tr(message, prefix, filter)) lambda: get_tr(message, prefix, filter))
if symbols or symbols == 0 or symbols == "": if symbols or symbols == 0 or symbols == "":
if isinstance(symbols, dict): if isinstance(symbols, dict):
symbols.update( (key, xmlescape(value).translate(ttab_in)) symbols.update(
(key, xmlescape(value).translate(ttab_in))
for key, value in symbols.iteritems() for key, value in symbols.iteritems()
if not isinstance(value, NUMBERS) ) if not isinstance(value, NUMBERS) )
else: else:
if not isinstance(symbols, tuple): if not isinstance(symbols, tuple):
symbols = (symbols,) symbols = (symbols,)
symbols = tuple(value if isinstance(value, NUMBERS) symbols = tuple(
value if isinstance(value, NUMBERS)
else xmlescape(value).translate(ttab_in) else xmlescape(value).translate(ttab_in)
for value in symbols) for value in symbols)
message = self.params_substitution(message, symbols) message = self.params_substitution(message, symbols)
return XML(message.translate(ttab_out)) return XML(message.translate(ttab_out))
def M(self, message, symbols={}, language=None, lazy=None, filter=None, ftag=None): def M(self, message, symbols={}, language=None,
""" get cached translated markmin-message with inserted parametes lazy=None, filter=None, ftag=None):
"""
get cached translated markmin-message with inserted parametes
if lazy==True lazyT object is returned if lazy==True lazyT object is returned
""" """
if lazy is None: if lazy is None:
@@ -804,19 +791,21 @@ class translator(object):
""" """
get cached translated message with inserted parameters(symbols) get cached translated message with inserted parameters(symbols)
""" """
message = get_from_cache(self.cache, message, lambda: self.get_t(message)) message = get_from_cache(self.cache, message,
lambda: self.get_t(message))
if symbols or symbols == 0 or symbols == "": if symbols or symbols == 0 or symbols == "":
if isinstance(symbols, dict): if isinstance(symbols, dict):
symbols.update( (key, str(value).translate(ttab_in)) symbols.update(
(key, str(value).translate(ttab_in))
for key, value in symbols.iteritems() for key, value in symbols.iteritems()
if not isinstance(value, NUMBERS) ) if not isinstance(value, NUMBERS) )
else: else:
if not isinstance(symbols, tuple): if not isinstance(symbols, tuple):
symbols = (symbols,) symbols = (symbols,)
symbols = tuple(value if isinstance(value, NUMBERS) symbols = tuple(
value if isinstance(value, NUMBERS)
else str(value).translate(ttab_in) else str(value).translate(ttab_in)
for value in symbols) for value in symbols)
message = self.params_substitution(message, symbols) message = self.params_substitution(message, symbols)
return message.translate(ttab_out) return message.translate(ttab_out)
@@ -830,15 +819,16 @@ def findT(path, language='en'):
cp = ospath.join(path, 'controllers') cp = ospath.join(path, 'controllers')
vp = ospath.join(path, 'views') vp = ospath.join(path, 'views')
mop = ospath.join(path, 'modules') mop = ospath.join(path, 'modules')
for file in listdir(mp, '^.+\.py$', 0) + listdir(cp, '^.+\.py$', 0)\ for filename in \
listdir(mp, '^.+\.py$', 0)+listdir(cp, '^.+\.py$', 0)\
+listdir(vp, '^.+\.html$', 0)+listdir(mop, '^.+\.py$', 0): +listdir(vp, '^.+\.html$', 0)+listdir(mop, '^.+\.py$', 0):
fp = portalocker.LockedFile(file, 'r') data = portalocker.read_locked(filename)
data = fp.read()
fp.close()
items = regex_translate.findall(data) items = regex_translate.findall(data)
for item in items: for item in items:
try: try:
message = eval(item) message = safe_eval(item)
except:
continue # silently ignore inproperly formatted strings
if not message.startswith('#') and not '\n' in message: if not message.startswith('#') and not '\n' in message:
tokens = message.rsplit('##', 1) tokens = message.rsplit('##', 1)
else: else:
@@ -848,8 +838,6 @@ def findT(path, language='en'):
message = tokens[0].strip()+'##'+tokens[1].strip() message = tokens[0].strip()+'##'+tokens[1].strip()
if message and not message in sentences: if message and not message in sentences:
sentences[message] = message sentences[message] = message
except:
pass
if not '!langcode!' in sentences: if not '!langcode!' in sentences:
sentences['!langcode!'] = ( sentences['!langcode!'] = (
'en' if language in ('default', 'en') else language) 'en' if language in ('default', 'en') else language)
@@ -875,8 +863,3 @@ def update_all_languages(application_path):
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod() doctest.testmod()