From e6f5a8b138b793e8936c2af59d436f5a0b05b027 Mon Sep 17 00:00:00 2001
From: Keith Mitchell
Date: Mon, 5 Dec 2011 17:11:44 -0800
Subject: [PATCH] Remove unused live translation code
The "live" translate interface is unused, and
not currently maintained. Removing until it is
needed in the future.
---
r2/r2/config/routing.py | 4 -
r2/r2/controllers/api.py | 20 -
r2/r2/controllers/i18n.py | 187 -----
r2/r2/controllers/validator/validator.py | 5 -
r2/r2/lib/logger.py | 153 ----
r2/r2/lib/pages/pages.py | 18 -
r2/r2/lib/translation.py | 846 +----------------------
r2/r2/templates/admintranslations.html | 113 ---
r2/r2/templates/translatedstring.html | 93 ---
r2/r2/templates/translation.html | 181 -----
r2/r2/templates/translator_message.html | 47 --
11 files changed, 28 insertions(+), 1639 deletions(-)
delete mode 100644 r2/r2/controllers/i18n.py
delete mode 100644 r2/r2/lib/logger.py
delete mode 100644 r2/r2/templates/admintranslations.html
delete mode 100644 r2/r2/templates/translatedstring.html
delete mode 100644 r2/r2/templates/translation.html
delete mode 100644 r2/r2/templates/translator_message.html
diff --git a/r2/r2/config/routing.py b/r2/r2/config/routing.py
index 5fa0694ad..fefed3676 100644
--- a/r2/r2/config/routing.py
+++ b/r2/r2/config/routing.py
@@ -83,10 +83,6 @@ def make_map(global_conf={}, app_conf={}):
mc('/feedback', controller='feedback', action='feedback')
mc('/ad_inq', controller='feedback', action='ad_inq')
- mc('/admin/i18n', controller='i18n', action='list')
- mc('/admin/i18n/:action', controller='i18n')
- mc('/admin/i18n/:action/:lang', controller='i18n')
-
mc('/admin/usage', controller='usage')
# Used for editing ads
diff --git a/r2/r2/controllers/api.py b/r2/r2/controllers/api.py
index 1cff924d6..0d4df29c6 100644
--- a/r2/r2/controllers/api.py
+++ b/r2/r2/controllers/api.py
@@ -1698,14 +1698,6 @@ class ApiController(RedditController):
c.user._commit()
- @noresponse(VAdmin(),
- tr = VTranslation("lang"),
- user = nop('user'))
- def POST_deltranslator(self, tr, user):
- if tr:
- tr.author.remove(user)
- tr.save()
-
@noresponse(VUser(),
VModhash(),
action = VOneOf('action', ('sub', 'unsub')),
@@ -1734,18 +1726,6 @@ class ApiController(RedditController):
# some other proc has already handled this subscribe request.
return
- @noresponse(VAdmin(),
- tr = VTranslation("id"))
- def POST_disable_lang(self, tr):
- if tr:
- tr._is_enabled = False
-
- @noresponse(VAdmin(),
- tr = VTranslation("id"))
- def POST_enable_lang(self, tr):
- if tr:
- tr._is_enabled = True
-
@validatedForm(VAdmin(),
hexkey=VLength("hexkey", max_length=32),
diff --git a/r2/r2/controllers/i18n.py b/r2/r2/controllers/i18n.py
deleted file mode 100644
index 6131cbc54..000000000
--- a/r2/r2/controllers/i18n.py
+++ /dev/null
@@ -1,187 +0,0 @@
-# The contents of this file are subject to the Common Public Attribution
-# License Version 1.0. (the "License"); you may not use this file except in
-# compliance with the License. You may obtain a copy of the License at
-# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
-# License Version 1.1, but Sections 14 and 15 have been added to cover use of
-# software over a computer network and provide for limited attribution for the
-# Original Developer. In addition, Exhibit A has been modified to be consistent
-# with Exhibit B.
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
-# the specific language governing rights and limitations under the License.
-#
-# The Original Code is Reddit.
-#
-# The Original Developer is the Initial Developer. The Initial Developer of the
-# Original Code is CondeNet, Inc.
-#
-# All portions of the code written by CondeNet are Copyright (c) 2006-2010
-# CondeNet, Inc. All Rights Reserved.
-################################################################################
-from pylons import request, g
-from reddit_base import RedditController
-from r2.lib.pages import UnfoundPage, AdminTranslations, \
- AdminPage, Translator_Message
-
-from r2.lib.translation import Translator, TranslatorTemplate, get_translator
-from validator import *
-from gettext import _translations
-
-from r2.lib.wrapped import Wrapped
-
-class VTranslator(Validator):
- def run(self, lang):
- if (not c.user_is_admin and
- (not c.user_is_loggedin or not lang or
- c.user.name not in Translator.get_author(lang))):
- abort(404, 'page not found')
-
-class VTranslationEnabled(Validator):
- def run(self):
- if not g.translator:
- abort(403, 'forbidden')
-
-
-class I18nController(RedditController):
-
- @validate(VTranslationEnabled(),
- VAdmin(),
- lang = nop('lang'),
- a = VExistingUname('name'))
- def POST_adduser(self, lang, a):
- from r2.lib.db import queries
- if a and Translator.exists(lang):
- tr = get_translator(locale = lang)
- tr.author.add(a.name)
- tr.save()
-
- # send the user a message
- body = Translator_Message(lang, a).render("html")
- subject = "Thanks for offering to help translate!"
- m, inbox_rel = Message._new(c.user, a, subject, body, request.ip)
- queries.new_message(m, inbox_rel)
-
- return self.redirect("/admin/i18n")
-
-
- @validate(VTranslationEnabled(),
- VAdmin())
- def GET_list(self):
- res = AdminPage(content = AdminTranslations(),
- title = 'translate reddit',
- show_sidebar = False).render()
- return res
-
-
- @validate(VTranslationEnabled(),
- VAdmin(),
- lang = nop('name'))
- def POST_new(self, lang):
- if lang and not Translator.exists(lang):
- tr = get_translator(locale = lang)
- tr.save()
- return self.redirect("/admin/i18n")
-
-
- @validate(VTranslationEnabled(),
- VTranslator('lang'),
- lang = nop('lang'))
- def GET_edit(self, lang):
- if not lang and c.user_is_admin:
- content = Wrapped(TranslatorTemplate())
- elif Translator.exists(lang):
- content = Wrapped(get_translator(locale = lang))
- else:
- content = UnfoundPage()
- res = AdminPage(content = content,
- title = 'translate reddit',
- show_sidebar = False).render()
- return res
-
- @validate(VTranslationEnabled(),
- VTranslator('lang'),
- lang = nop('lang'),
- numbers = VBoolean("numbers"))
- def GET_try(self, lang, numbers):
- if lang:
- tr = get_translator(locale = lang)
- tr.save(compile=True, include_index = numbers)
-
- tran_keys = _translations.keys()
- for key in tran_keys:
- if key.endswith(tr._out_file('mo')):
- del _translations[key]
-
- return self.redirect("http://%s.%s/" %
- (lang, g.domain))
- return abort(404, 'page not found')
-
- @validate(VTranslationEnabled(),
- VTranslator('lang'),
- post = nop('post'),
- try_trans = nop('try'),
- lang = nop('lang'))
- def POST_edit(self, lang, post, try_trans):
- if (lang and not Translator.exists(lang)):
- return self.redirect('/admin/i18n')
-
- if lang:
- tr = get_translator(locale = lang)
- else:
- tr = TranslatorTemplate()
-
- enabled = set()
- for k, val in request.post.iteritems():
- if k.startswith('trans_'):
- k = k.split('_')
- val = val.replace('\n', ' ').replace('\r', ' ')
- # check if this is a translation string
- if k[1:] and tr.get(k[1]):
- tr.set(k[1], val, indx = int(k[2] if k[2:] else -1))
- # check if this is an admin editing the source/comment lines
- elif c.user_is_admin and tr.sources.get(k[1]):
- source = tr.sources.get(k[1])
- tr.source_trans[source] = val
- elif c.user_is_admin and k.startswith('enabled_'):
- k = k.split('_')
- enabled.add(k[1])
-
- # update the enabled state of the buttons
- if c.user_is_admin and enabled:
- strings = set(tr.string_dict.keys())
- disabled = strings - enabled
- for s in strings:
- tr.set_enabled(s, True)
- for s in disabled:
- tr.set_enabled(s, False)
-
- if request.post.get('nplurals'):
- try:
- tr.plural_names = [request.post.get('pluralform_%d' % i) \
- for i in xrange(tr.nplurals)]
- tr.nplurals = int(request.post.get('nplurals'))
- except ValueError:
- pass
- if request.post.get('langname'):
- tr.name = request.post['langname']
- if request.post.get('enlangname'):
- tr.en_name = request.post['enlangname']
-
- tr.save(compile=bool(try_trans))
-
- if try_trans:
- tran_keys = _translations.keys()
- for key in tran_keys:
- if key.endswith(tr._out_file('mo')):
- del _translations[key]
-
- return self.redirect("http://%s/?lang=%s" %
- (g.domain, lang))
-
- whereto = request.post.get('bttn_num', '')
- if whereto:
- whereto = 'bttn_num_%s' % whereto
- return self.redirect("/admin/i18n/edit/%s#%s" % (lang or '', whereto))
- return res
-
diff --git a/r2/r2/controllers/validator/validator.py b/r2/r2/controllers/validator/validator.py
index 8dd441cd8..94a0db379 100644
--- a/r2/r2/controllers/validator/validator.py
+++ b/r2/r2/controllers/validator/validator.py
@@ -1345,11 +1345,6 @@ class VCnameDomain(Validator):
except UnicodeEncodeError:
self.set_error(errors.BAD_CNAME)
-class VTranslation(Validator):
- def run(self, param):
- from r2.lib.translation import Translator
- if Translator.exists(param):
- return Translator(locale = param)
# NOTE: make sure *never* to have res check these are present
# otherwise, the response could contain reference to these errors...!
diff --git a/r2/r2/lib/logger.py b/r2/r2/lib/logger.py
deleted file mode 100644
index 66e2471b1..000000000
--- a/r2/r2/lib/logger.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# The contents of this file are subject to the Common Public Attribution
-# License Version 1.0. (the "License"); you may not use this file except in
-# compliance with the License. You may obtain a copy of the License at
-# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
-# License Version 1.1, but Sections 14 and 15 have been added to cover use of
-# software over a computer network and provide for limited attribution for the
-# Original Developer. In addition, Exhibit A has been modified to be consistent
-# with Exhibit B.
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
-# the specific language governing rights and limitations under the License.
-#
-# The Original Code is Reddit.
-#
-# The Original Developer is the Initial Developer. The Initial Developer of the
-# Original Code is CondeNet, Inc.
-#
-# All portions of the code written by CondeNet are Copyright (c) 2006-2010
-# CondeNet, Inc. All Rights Reserved.
-################################################################################
-from __future__ import with_statement
-import cPickle as pickle
-import os, shutil, time
-from utils import Storage
-from datetime import datetime, timedelta
-
-
-class LoggedSlots(object):
-
- def __init__(self, logfile, **kw):
- for k, v in kw.iteritems():
- super(LoggedSlots, self).__setattr__(k, v)
- self.__logfile = logfile
- self.load_slots()
-
- def __setattr__(self, k, v):
- super(LoggedSlots, self).__setattr__(k, v)
- if k in self.__slots__:
- self.dump_slots()
-
- def load_slots(self):
- d = self._get_slots(self.__logfile)
- for k, v in d.iteritems():
- super(LoggedSlots, self).__setattr__(k, v)
-
- def dump_slots(self):
- if self.__logfile:
- with WithWriteLock(self.__logfile) as handle:
- d = {}
- for s in self.__slots__:
- try:
- d[s] = getattr(self, s)
- except AttributeError:
- continue
- pickle.dump(d, handle)
-
- @classmethod
- def _get_slots(self, file):
- if os.path.exists(file):
- with open(file) as handle:
- return Storage(pickle.load(handle))
- return Storage()
-
-
-
-class WriteLockExistsException(): pass
-
-class WithWriteLock():
- def __init__(self, file_name, mode = 'w', force = False, age = 60):
- self.fname = file_name
- self.lock_file = file_name + ".write_lock"
- self.time = datetime.now()
- self.handle = None
- self.created = True
-
- if self.exists():
- if force:
- self.destroy()
- elif not self.try_expire(age):
- raise WriteLockExistsException
-
- # back up the file to be written to
- if os.path.exists(self.fname):
- shutil.copyfile(self.fname, self.backup_file)
- # write out a lock file
- with open(self.lock_file, 'w') as handle:
- pickle.dump(self.time, handle)
- # lastly, open the file!
- self.handle = open(file_name, mode)
-
- def write(self, *a, **kw):
- self.handle.write(*a, **kw)
-
-
- @property
- def backup_file(self):
- return "%s-%s.bak" % (self.fname,
- time.mktime(self.time.timetuple()))
-
- def exists(self):
- return os.path.exists(self.lock_file)
-
- def try_expire(self, age):
- '''destroys an existing lock file if it is more than age seconds old'''
- with open(self.lock_file, 'r') as handle:
- time = pickle.load(handle)
- if self.time - time > timedelta(0, age):
- self.destroy()
- return True
- return False
-
- def destroy(self):
- # close any open handles
- if self.handle:
- self.handle.close()
- self.handle = None
-
- # wipe the lock file and the back-up
- if self.created:
- self.created = False
- if self.exists():
- os.unlink(self.lock_file)
- if os.path.exists(self.backup_file):
- os.unlink(self.backup_file)
-
- def close(self):
- self.destroy()
-
- def rollback(self):
- # close any open handles
- if self.handle:
- self.handle.close()
- self.handle = None
-
- if self.created:
- # clobber any changes to the file with our archive
- if os.path.exists(self.backup_file):
- shutil.copyfile(self.backup_file, self.fname)
-
- #destroy as usual
- self.destroy()
-
- def __enter__(self):
- return self
-
-
- def __exit__(self, type, value, tb):
- if tb is None:
- self.close()
- else:
- self.rollback()
-
diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py
index 78e0c49bd..b78456b68 100644
--- a/r2/r2/lib/pages/pages.py
+++ b/r2/r2/lib/pages/pages.py
@@ -1918,24 +1918,6 @@ class Bookmarklets(Templated):
Templated.__init__(self, buttons = buttons)
-class Translator_Message(Templated):
- def __init__(self, locale, user):
- from r2.lib.translation import Translator
- self.user = user
- self.locale = locale
- self.lang_name = Translator.get_name(self.locale)
- self.en_name = Translator.get_en_name(self.locale)
- Templated.__init__(self)
-
-class AdminTranslations(Templated):
- """The translator control interface, used for determining which
- user is allowed to edit which translation file and for providing a
- summary of what translation files are done and/or in use."""
- def __init__(self):
- from r2.lib.translation import list_translations
- Templated.__init__(self)
- self.translations = list_translations()
-
class UserAwards(Templated):
"""For drawing the regular-user awards page."""
def __init__(self):
diff --git a/r2/r2/lib/translation.py b/r2/r2/lib/translation.py
index ee1d817f7..d36a3aaf5 100644
--- a/r2/r2/lib/translation.py
+++ b/r2/r2/lib/translation.py
@@ -19,28 +19,27 @@
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
# CondeNet, Inc. All Rights Reserved.
################################################################################
-from __future__ import with_statement
-from distutils.cmd import Command
-from distutils.errors import DistutilsOptionError
-
-from pylons.i18n import _
from babel import Locale
-import os, re
-import cPickle as pickle
-from wrapped import Templated
-from utils import Storage
-from hashlib import md5
-
-from logger import WithWriteLock, LoggedSlots
-from r2.lib.i18n import I18N_PATH as _i18n_path
-
from datetime import datetime, timedelta
+import gettext
+from hashlib import md5
+import json
+import os
+import pylons
+from pylons.i18n import _
+from pylons.i18n.translation import translation, LanguageError, NullTranslations
+import random
+import re
import time
-import pylons
-from pylons.i18n.translation import translation, LanguageError, NullTranslations
+from r2.lib.i18n import I18N_PATH as _i18n_path
+from r2.lib.utils import Storage
-def _get_translator(lang, graceful_fail = False, **kwargs):
+
+_domain = 'r2'
+
+
+def _get_translator(lang, graceful_fail=False, **kwargs):
from pylons import config as conf
"""Utility method to get a valid translator object from a language name"""
if not isinstance(lang, list):
@@ -67,540 +66,26 @@ def set_lang(lang, graceful_fail = False, **kwargs):
registry.replace(pylons.translator, translator)
-comment = re.compile(r'\A\s*#')
-msgid = re.compile(r'\A\s*msgid\s+"')
-msgid_pl = re.compile(r'\A\s*msgid_plural\s+"')
-msgstr = re.compile(r'\A\s*msgstr(\[\d\])?\s+"')
-str_only = re.compile(r'\A\s*"')
-
-substr = re.compile("(%(\([^\)]+\))?([\d\.]+)?[a-zA-Z])")
-
-source_line = re.compile(": (\S+\.[^\.]+):(\d+)")
-
-from r2.config.templates import tpm
-
-tpm.add('translator', 'html', file = "translation.html")
-
-
-
-_domain = 'r2'
-
-def hax(string):
- """when site translation strings change, this will allow the subsequent
- language translations to be updated without giving the translator more
- work"""
- hax_dict = { }
- return hax_dict.get(string, string)
-
-
-class TranslatedString(Templated):
- class _SubstString:
- def __init__(self, string, enabled = True):
- self.str = hax(string)
- subst = substr.findall(string)
- self.subst = set(x[0] for x in subst)
- self.name = set(x[1].strip(')').strip('(') for x in subst if x[1])
-
- def __str__(self):
- return self.str
-
- def unicode(self):
- if isinstance(self.str, unicode):
- return self.str
- return unicode(self.str, "utf-8")
-
- def __repr__(self):
- return self.str
-
- def _po_str(self, header, cut = 60, include_index = False):
- s = self.str
- if include_index:
- s = ("/*%s:*/" % include_index) + s
- if len(s) > cut:
- if '\\n' in s:
- txt = [i + "\\n" for i in s.split('\\n') if i]
- else:
- txt = [s]
- res = '%s ""\n' % header
- for t in txt:
- res += '"%s"\n' % t.replace('"', '\\"')
- else:
- res = '%s "%s"\n' % (header, s.replace('"', '\\"'))
- if isinstance(res, unicode):
- return res.encode('utf8')
- return res
-
- def highlight(self, func):
- res = self.str
- for x in self.subst:
- res = res.replace(x, func(x))
- return res
-
- def valid(self):
- try:
- # enforce validatation for named replacement rules only
- if self.name:
- x = self.str % dict((k, 0) for k in self.name if k)
- return True
- except:
- return False
-
- def compatible(self, other):
- # compatibility implies every substitution rule in other
- # must be in self.
- return other.valid() and (not self.name or other.subst.issubset(self.subst))
-
- def __init__(self, translator, sing, plural = '', message = '',
- enabled = True, locale = '', tip = '', index = 0):
- Templated.__init__(self)
-
- self.translator = translator
- self.message = message
- self.enabled = enabled
- self.locale = locale
- self.tip = ''
- self.index = 0
-
- # figure out where this item appears in the source
- source = source_line.findall(message)
- if source:
- self.source, self.line = source[0]
- else:
- self.source = self.line = ''
-
- self.msgid = self._SubstString(sing)
- self.msgid_plural = self._SubstString(plural)
- if str(self.msgid_plural):
- self.msgstr = []
- else:
- self.msgstr = self._SubstString('')
-
- @property
- def singular(self):
- return self.msgid.unicode() or ''
-
- def _singular(self, func):
- return self.msgid.highlight(func)
-
- @property
- def plural(self):
- return self.msgid_plural.unicode() or ''
-
- def _plural(self, func):
- return self.msgid_plural.highlight(func)
-
- def is_translated(self):
- if str(self.msgid_plural):
- return bool(self.msgstr) and any([x.str for x in self.msgstr])
- else:
- return bool(self.msgstr.str)
-
-
- def translation(self, indx = 0):
- if self.plural:
- if indx < len(self.msgstr):
- return self.msgstr[indx].unicode() or ''
- return ''
- else:
- return self.msgstr.unicode() or ''
-
- def add(self, translation, index = -1):
- new = self._SubstString(translation)
- if unicode(self.msgid_plural):
- if index >= 0:
- while index >= len(self.msgstr):
- self.msgstr.append('')
- self.msgstr[index] = new
- else:
- self.msgstr.append(new)
- else:
- self.msgstr = new
-
- def __getitem__(self, indx):
- return self.translation(indx)
-
- def __setitem__(self, indx, value):
- return self.add(value, index = indx)
-
- @property
- def md5(self):
- return md5(unicode(self.singular) + unicode(self.plural)).hexdigest()
-
- def __repr__(self):
- return ""
-
- def to_string(self, include_index = False):
- res = ''
- if self.message:
- res = '#' + self.message.replace('\n', '\n#')
- if res[-1] == '#': res = res[:-1]
- res += self.msgid._po_str('msgid', include_index = False)
- if unicode(self.msgid_plural):
- res += self.msgid_plural._po_str('msgid_plural',
- include_index = False)
- for i in range(0, min(len(self.msgstr), self.translator.nplurals)):
- res += self.msgstr[i]._po_str('msgstr[%d]'%i,
- include_index = include_index)
- else:
- res += self.msgstr._po_str('msgstr', include_index = include_index)
- res += "\n"
- try:
- return str(res)
- except UnicodeEncodeError:
- return unicode(res + "\n").encode('utf-8')
-
- def __str__(self):
- return self.to_string(False)
-
-
- def is_valid(self, indx = -1):
- if self.plural:
- if indx < 0:
- return all(self.is_valid(i) for i in range(0,len(self.msgstr)))
- elif indx < len(self.msgstr):
- return self.msgid.compatible(self.msgstr[indx]) #or \
- #self.msgstr.compatible(self.msgstr[indx])
- return True
- else:
- return self.msgid.compatible(self.msgstr)
-
-
-class GettextHeader(TranslatedString):
- def __init__(self, translator, sing, plural = '', message = '',
- enabled = True, locale = ''):
- TranslatedString.__init__(self, translator, '', '', message=message,
- enabled = False, locale = locale)
- self.headers = []
-
- def add(self, translation, index = -1):
- if index < 0:
- header_keys = set()
- self.msgstr = self._SubstString(translation)
- for line in translation.split('\\n'):
- line = line.split(':')
- header_keys.add(line[0])
- self.headers.append([line[0], ':'.join(line[1:])])
- # http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms
- if "Plural-Forms" not in header_keys:
- self.headers.append(["Plural-Forms",
- "nplurals=2; plural=(n != 1);"])
-
- elif self.headers and len(self.headers) > index:
- self.headers[index][1] = translation
- t = '\\n'.join('%s:%s' % tuple(h) for h in self.headers if h[0])
- self.msgstr = self._SubstString(t)
-
- def __repr__(self):
- return ""
-
-class Translator(LoggedSlots):
-
- __slots__ = ['enabled', 'num_completed', 'num_total', 'author',
- 'nplurals', 'plural_names', 'source_trans', 'name',
- 'en_name', '_is_enabled']
-
- def __init__(self, domain = _domain, path = _i18n_path,
- locale = ''):
- self.strings = []
- self.string_dict = {}
- self.sources = {}
-
- self.locale = locale
- self.input_file = TranslatorTemplate.outfile(locale)
-
- def _out_file(extension = None):
- d = dict(extension=extension) if extension is not None else {}
- return self.outfile(locale, path=path, domain=domain, **d)
- self._out_file = _out_file
-
- # create directory for translation
- if not os.path.exists(os.path.dirname(self._out_file('po'))):
- os.makedirs(os.path.dirname(self._out_file('po')))
-
- LoggedSlots.__init__(self, self._out_file('data'),
- plural_names = ['singular', 'plural'],
- nplurals = 2,
- source_trans = {},
- author = set([]),
- enabled = {},
- num_completed = 0,
- num_total = 0,
- en_name = locale,
- name = locale,
- _is_enabled = False
- )
-
- # no translation, use the infile to make one
- if not os.path.exists(self._out_file('po')):
- self.from_file(self.input_file)
- # translation exists: make sure infile is not newer
- else:
- i_stat = os.stat(self.input_file)
- o_stat = os.stat(self._out_file('po'))
- if i_stat.st_mtime > o_stat.st_mtime:
- self.from_file(self.input_file)
- self.load_specialty_strings()
- self.from_file(self._out_file('po'), merge=True)
- self.save()
- else:
- self.from_file(self._out_file('po'))
- self.load_specialty_strings()
-
-
- def is_valid(self):
- for x in self.get_invalid():
- return False
- return True
-
- def get_invalid(self):
- for k, indx in self.string_dict.iteritems():
- if not self.strings[indx].is_valid():
- yield (k, self.strings[indx].singular)
-
- def from_file(self, file, merge = False):
- indx = 0
- with open(file, 'r') as handle:
- line = True
- while line:
- line = handle.readline()
- msg = ''
- while comment.match(line):
- msg += '#'.join(line.split('#')[1:])
- line = handle.readline()
- if msgid.match(line):
- txt_pl = ''
- r, txt_sing, line = get_next_str_block(line, handle)
- # grab plural if it exists
- if msgid_pl.match(line):
- r, txt_pl, line = get_next_str_block(line, handle)
- if txt_sing or txt_pl:
- ts = TranslatedString(self, txt_sing, txt_pl,
- message = msg,
- locale = self.locale)
- else:
- ts = GettextHeader(self, txt_sing, txt_pl,
- message = msg,
- locale = self.locale)
- key = ts.md5
- if not self.enabled.has_key(key) or self.enabled[key]:
- indx += 1
- ts.index = indx
-
- while msgstr.match(line):
- r, translation, line = get_next_str_block(line, handle)
- ts.add(translation)
-
- if not merge and not self.string_dict.has_key(key):
- self.string_dict[key] = len(self.strings)
- self.strings.append(ts)
- self.sources[md5(ts.source).hexdigest()] = ts.source
- elif merge and self.string_dict.has_key(key):
- i = self.string_dict[key]
- self.strings[i].msgstr = ts.msgstr
- self.sources[md5(ts.source).hexdigest()] = ts.source
-
- def load_specialty_strings(self):
- indx = len(self.strings)
- from r2.lib.strings import rand_strings
- for name, rs in rand_strings:
- for desc in rs:
- message = ": randomstring:%s\n" % name
- ts = TranslatedString(self, desc, "", message = message,
- locale = self.locale)
- key = ts.md5
- if not self.string_dict.has_key(key):
- self.string_dict[key] = len(self.strings)
- self.strings.append(ts)
- else:
- ts = self.strings[self.string_dict[key]]
- self.sources[md5(ts.source).hexdigest()] = ts.source
- ts.enabled = True
- indx += 1
- ts.index = indx
-
-
- def __getitem__(self, key):
- return self.strings[self.string_dict[key]]
-
- def get(self, key, alt = None):
- indx = self.string_dict.get(key)
- if indx is not None:
- return self.strings[self.string_dict[key]]
- else:
- return alt
-
- def set(self, key, val, indx = -1):
- s = self.get(key)
- if s: s[indx] = val
-
-
- def to_file(self, file, compile=False, mo_file=None,
- include_index = False):
- with WithWriteLock(file) as handle:
- for string in self.strings:
- indx = include_index and string.index
- handle.write(string.to_string(include_index = indx))
- if compile and self.is_valid():
- if mo_file:
- out_file = mo_file
- elif file.endswith(".po"):
- out_file = file[:-3] + ".mo"
- else:
- out_file = file + ".mo"
-
- cmd = 'msgfmt -o "%s" "%s"' % (out_file, file)
- print cmd
- with os.popen(cmd) as handle:
- x = handle.read()
- if include_index:
- with WithWriteLock(file) as handle:
- for string in self.strings:
- handle.write(string.to_string(include_index = False))
-
-
- def __iter__(self):
- for x in self.strings:
- yield x
-
- def __repr__(self):
- return ""
-
- @classmethod
- def outfile(cls, locale, domain = _domain, path = _i18n_path,
- extension = 'po'):
- return os.path.join(path, locale, 'LC_MESSAGES',
+def load_data(lang, path=_i18n_path, domain=_domain, extension='data'):
+ filename = os.path.join(path, locale, 'LC_MESSAGES',
domain + '.' + extension)
-
- @classmethod
- def in_use(cls, locale, domain = _domain, path = _i18n_path):
- return os.path.exists(cls.outfile(locale, domain=domain, path=path,
- extension='mo'))
+ with open(filename) as datafile:
+ data = json.load(datafile)
- @classmethod
- def exists(cls, locale, domain = _domain, path = _i18n_path):
- return os.path.exists(cls.outfile(locale, domain=domain, path=path))
-
-
- def save(self, compile = False, include_index = False):
- self.to_file(self._out_file('po'), compile = compile,
- mo_file = self._out_file('mo'),
- include_index = include_index)
- self.gen_stats()
- self.dump_slots()
-
- def __repr__(self):
- return ""
-
-
- @classmethod
- def get_slots(cls, locale = None):
- locale = locale or pylons.g.lang
- f = cls.outfile(locale, extension='data')
- return LoggedSlots._get_slots(f)
-
- def load_slots(self):
- LoggedSlots.load_slots(self)
- # clobber enabled and translation using primary template
- if self.input_file != self._out_file():
- parent_slots = TranslatorTemplate.get_slots()
- self.enabled = parent_slots.get('enabled',{})
- self.source_trans = parent_slots.get('source_trans', {})
-
- #if self.enabled:
- # for key, state in self.enabled.iteritems():
- # self.set_enabled(key, state)
-
- def gen_stats(self):
- enabled = {}
- num_completed = 0
- num_strings = 0
- for h, indx in self.string_dict.iteritems():
- s = self.strings[indx]
- enabled[h] = s.enabled
- if s.enabled:
- num_strings +=1
- if s.is_translated():
- num_completed += 1
- self.enabled = enabled
- self.num_completed = num_completed
- self.num_total = num_strings
-
-
- @classmethod
- def get_author(cls, locale):
- slots = cls.get_slots(locale)
- return slots.get("author", set([]))
-
- @classmethod
- def get_name(cls, locale):
- slots = cls.get_slots(locale)
- return slots.get("name", locale)
-
- @classmethod
- def get_en_name(cls, locale):
- slots = cls.get_slots(locale)
- return slots.get("en_name", locale)
-
- @classmethod
- def is_enabled(cls, locale):
- slots = cls.get_slots(locale)
- return slots.get("_is_enabled", False)
-
- @classmethod
- def get_complete_frac(cls, locale):
- infos = cls.get_slots(locale)
- comp = infos.get('num_completed', 0)
- tot = infos.get('num_total', 1)
- return float(comp)/float(tot)
-
- def set_enabled(self, key, state = True):
- indx = self.string_dict.get(key, None)
- if indx is not None:
- self.strings[indx].enabled = state
-
-def list_translations(path = _i18n_path):
- trans = []
- for lang in os.listdir(path):
- x = os.path.join(path, lang, 'LC_MESSAGES')
- if os.path.exists(x) and os.path.isdir(x):
- if Translator.exists(lang):
- trans.append(lang)
- return trans
-
-class rebuild_translations(Command):
- user_options = []
- def initialize_options(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self, path = _i18n_path):
- _rebuild_trans(path)
-
-def _rebuild_trans(path = _i18n_path):
- for lang in os.listdir(path):
- x = os.path.join(path, lang, 'LC_MESSAGES')
- if os.path.exists(x) and os.path.isdir(x):
- if Translator.exists(lang):
- print "recompiling '%s'" % (lang)
- t = get_translator(lang)
- t.save(compile = True)
-
-
-def get_active_langs(path = _i18n_path, default_lang = 'en'):
+def get_active_langs(path=_i18n_path, default_lang='en'):
trans = []
trans_name = {}
for lang in os.listdir(path):
x = os.path.join(path, lang, 'LC_MESSAGES')
if os.path.isdir(x):
- name = [Translator.get_name(lang), '']
- if Translator.is_enabled(lang) and Translator.in_use(lang):
- if lang != default_lang:
- trans.append(lang)
- if Translator.get_complete_frac(lang) < .5:
- name[1] = ' (*)'
+ data = load_data(lang)
+ name = [data['name'], '']
+ if data['_is_enabled'] and lang != default_lang:
+ trans.append(lang)
+ completion = float(data['num_completed']) / float(data['num_total'])
+ if completion < .5:
+ name[1] = ' (*)'
trans_name[lang] = name
trans.sort()
# insert the default language at the top of the list
@@ -608,278 +93,3 @@ def get_active_langs(path = _i18n_path, default_lang = 'en'):
if default_lang not in trans_name:
trans_name[default_lang] = default_lang
return trans, trans_name
-
-
-class TranslatorTemplate(Translator):
- @classmethod
- def outfile(cls, locale, domain = _domain, path = _i18n_path,
- extension = 'pot'):
- return os.path.join(path, domain + '.' + extension)
-
- # defunct to_file since pot file is uneditable
- def to_file(*a, **kw):
- pass
-
-
-class AutoTranslator(Translator):
- def __init__(self, **kw):
- Translator.__init__(self, **kw)
- for string in self.strings:
- if not string.is_translated():
- string.add(self.translate(string.singular), index = 0)
- if string.plural:
- string.add(self.translate(string.plural), index = 1)
-
- def translate(self, string):
- s = string.split("%")
- s, d = s[0], s[1:]
- substr = re.compile("((\([^\)]+\))?([\d\.]+)?[a-zA-Z])(.*)")
- def _sub(m):
- g = m.groups()
- return "%s%s" % (g[0], self.trans_rules(g[-1]))
-
- d = [self.trans_rules(s)] + [substr.sub(_sub, x) for x in d]
- return '%'.join(d)
-
- def trans_rules(self, text):
- return text
-
-class Transliterator(AutoTranslator):
- def __init__(self, **kw):
- Translator.__init__(self, **kw)
- for string in self.strings:
- if not string.is_translated() \
- and not isinstance(string, GettextHeader):
- if string.plural:
- string.add(self.translate(string.msgstr[0].unicode()),
- index = 0)
- string.add(self.translate(string.msgstr[1].unicode()),
- index = 1)
- else:
- string.add(self.translate(string.msgstr.unicode()),
- index = 0)
-
-
-class USEnglishTranslator(AutoTranslator):
- def trans_rules(self, string):
- return string
-
-
-class TamilTranslator(Transliterator):
- transliterator = dict([(u'a', u'\u0b85'),
- (u'A', u'\u0b86'),
- (u'i', u'\u0b87'),
- (u'I', u'\u0b88'),
- (u'u', u'\u0b89'),
- (u'U', u'\u0b8a'),
- (u'e', u'\u0b8e'),
- (u'E', u'\u0b8f'),
- (u'o', u'\u0b92'),
- (u'O', u'\u0b93'),
-
- (u'g', u'\u0b95\u0bcd'),
- (u'c', u'\u0b95\u0bcd'),
- (u'k', u'\u0b95\u0bcd'),
- (u'q', u'\u0b95\u0bcd'),
- (u'G', u'\u0b95\u0bcd'),
- (u'K', u'\u0b95\u0bcd'),
-
- (u's', u'\u0b9a\u0bcd'),
- (u'C', u'\u0b9a\u0bcd'),
-
- (u't', u'\u0b9f\u0bcd'),
- (u'D', u'\u0b9f\u0bcd'),
- (u'T', u'\u0b9f\u0bcd'),
- (u'N', u'\u0ba3\u0bcd'),
- (u'd', u'\u0ba4\u0bcd'),
- (u'$', u'\u0ba8\u0bcd'),
- (u'n', u'\u0ba9\u0bcd'),
- (u'B', u'\u0baa\u0bcd'),
- (u'b', u'\u0baa\u0bcd'),
- (u'f', u'\u0baa\u0bcd'),
- (u'p', u'\u0baa\u0bcd'),
- (u'F', u'\u0baa\u0bcd'),
- (u'P', u'\u0baa\u0bcd'),
- (u'm', u'\u0bae\u0bcd'),
- (u'M', u'\u0bae\u0bcd'),
- (u'y', u'\u0baf\u0bcd'),
- (u'r', u'\u0bb0\u0bcd'),
- (u'R', u'\u0bb1\u0bcd'),
- (u'l', u'\u0bb2\u0bcd'),
- (u'L', u'\u0bb3\u0bcd'),
- (u'Z', u'\u0bb4\u0bcd'),
- (u'z', u'\u0bb4\u0bcd'),
- (u'v', u'\u0bb5\u0bcd'),
- (u'w', u'\u0bb5\u0bcd'),
- (u'V', u'\u0bb5\u0bcd'),
- (u'W', u'\u0bb5\u0bcd'),
-
- (u'Q', u'\u0b83'),
-
- (u'h', u'\u0bb9\u0bcd'),
- (u'j', u'\u0b9c\u0bcd'),
- (u'J', u'\u0b9c\u0bcd'),
- (u'S', u'\u0bb8\u0bcd'),
- (u'H', u'\u0bb9\u0bcd'),
-
- (u'Y', u'\u0b20\u0bcd'),
- (u'^', u'\u0b20')])
-
- ligatures = ((u'\u0ba9\u0bcd\u0b95\u0bcd', u'\u0B99\u0BCD'), # ng
- (u'\u0ba9\u0bcd\u0b9c\u0bcd', u'\u0B9e\u0BCD'), # nj
- (u'\u0b95\u0bcd\u0bb9\u0bcd', u'\u0b9a\u0bcd'), # ch -> C
- (u'\u0b9f\u0bcd\u0bb9\u0bcd', u'\u0ba4\u0bcd'), # th -> d
- (u'\u0ba4\u0bcd\u0bb9\u0bcd', u'\u0ba4\u0bcd'), # dh -> d
-
- (u'\u0b85\u0b85', u'\u0b86'), # aa -> A
- (u'\u0b85\u0b87', u'\u0b90'), # ai
- (u'\u0b85\u0b89', u'\u0b94'), # au
- (u'\u0b87\u0b87', u'\u0b88'), # ii -> I
- (u'\u0b89\u0b89', u'\u0b8a'), # uu -> U
- (u'\u0b8e\u0b8e', u'\u0b8f'), # ee -> E
- (u'\u0b92\u0b92', u'\u0b93'), # oo -> O
- # remove accent from consonants and convert to ligature
- # based on the subsequent vowell
- (u'\u0bcd\u0b85', u''),
- (u'\u0bcd\u0b86', u'\u0bbe'),
- (u'\u0bcd\u0b87', u'\u0bbf'),
- (u'\u0bcd\u0b88', u'\u0bc0'),
- (u'\u0bcd\u0b89', u'\u0bc1'),
- (u'\u0bcd\u0b8a', u'\u0bc2'),
- (u'\u0bcd\u0b8e', u'\u0bc6'),
- (u'\u0bcd\u0b8f', u'\u0bc7'),
- (u'\u0bcd\u0b90', u'\u0bc8'),
- (u'\u0bcd\u0b92', u'\u0bca'),
- (u'\u0bcd\u0b93', u'\u0bcb'),
- (u'\u0bcd\u0b94', u'\u0bcc'),
- )
-
- def trans_rules(self, string):
- t = u''.join(self.transliterator.get(x, x) for x in string)
- for k, v in self.ligatures:
- t = t.replace(k, v)
- return t
-
-class SerbianCyrillicTranslator(Transliterator):
- letters = \
- (( "A" , u'\u0410'),
- ( "B" , u'\u0411'),
- ( "V" , u'\u0412'),
- ( "G" , u'\u0413'),
- ( "D" , u'\u0414'),
- ( u'\u0110' , u'\u0402'),
- ( "E" , u'\u0415'),
- ( u"\u017d" , u'\u0416'),
- ( "Z" , u'\u0417'),
- ( "I" , u'\u0418'),
- ( "J" , u'\u0408'),
- ( "K" , u'\u041a'),
- ( "L" , u'\u041b'),
- ( "Lj" , u'\u0409'),
- ( "M" , u'\u041c'),
- ( "N" , u'\u041d'),
- ( "Nj" , u'\u040a'),
- ( "O" , u'\u041e'),
- ( "P" , u'\u041f'),
- ( "R" , u'\u0420'),
- ( "S" , u'\u0421'),
- ( "T" , u'\u0422'),
- ( u"\u0106" , u'\u040b'),
- ( "U" , u'\u0423'),
- ( "F" , u'\u0424'),
- ( 'H' , u'\u0425'),
- ( "C" , u'\u0426'),
- ( u"\u010c", u'\u0427'),
- ( u"D\u017e", u'\u040f'),
- ( u"\u0160", u'\u0428'),
-
- ( "a" , u'\u0430'),
- ( "b" , u'\u0431'),
- ( "v" , u'\u0432'),
- ( "g" , u'\u0433'),
- ( "d" , u'\u0434'),
- ( u'\u0111' , u'\u0452'),
- ( "e" , u'\u0435'),
- ( u"\u017e" , u'\u0436'),
- ( "z" , u'\u0437'),
- ( "i" , u'\u0438'),
- ( "j" , u'\u0458'),
- ( "k" , u'\u043a'),
- ( "l" , u'\u043b'),
- ( "lj" , u'\u0459'),
- ( "m" , u'\u043c'),
- ( "n" , u'\u043d'),
- ( "nj" , u'\u045a'),
- ( "o" , u'\u043e'),
- ( "p" , u'\u043f'),
- ( "r" , u'\u0440'),
- ( "s" , u'\u0441'),
- ( "t" , u'\u0442'),
- ( u"\u0107" , u'\u045b'),
- ( "u" , u'\u0443'),
- ( "f" , u'\u0444'),
- ( 'h' , u'\u0445'),
- ( "c" , u'\u0446'),
- ( u"\u010d", u'\u0447'),
- ( u"d\u017e", u'\u045f'),
- ( u"\u0161", u'\u0448'))
- ligatures = [(x,y) for x, y in letters if len(x) == 2]
- letters = dict((x, y) for x, y in letters if len(x) == 1)
- def trans_rules(self, string):
- for x, y in self.ligatures:
- string = string.replace(x, y)
- return "".join(self.letters.get(s, s) for s in string)
-
-import random
-class LeetTranslator(AutoTranslator):
- def trans_rules(self, string):
- print string
- key = dict(a=["4","@"],
- b=["8"], c=["("],
- d=[")", "|)"], e=["3"],
- f=["ph"], g=["6"],
- i=["1", "!"], j=["_/"],
- k=["X"], l=["1"], o=["0"],
- q=["0_"], s=["5", "$"], t=["7"],
- z=["2"])
- s = string.lower()
- s = (random.choice(key.get(x, [x])) for x in s)
- return ''.join(s)
-
-def get_translator(locale):
- #if locale == 'sr':
- # return SerbianCyrillicTranslator(locale = locale)
- if locale == 'leet':
- return LeetTranslator(locale = locale)
- elif locale.startswith('en'):
- return USEnglishTranslator(locale = locale)
- elif locale == 'ta':
- return TamilTranslator(locale = locale)
- return Translator(locale = locale)
-
-def get_next_str_block(line, handle):
- res = ''
- before, middle, after = qnd_parse(line)
- txt = [middle]
- res += line
- line = handle.readline()
- # grab multi-line strings for this element
- while True:
- if not str_only.match(line): break
- res += line
- b, m, a = qnd_parse(line)
- txt.append(m)
- line = handle.readline()
- return res, (''.join(txt)).replace('\\"', '"'), line
-
-
-def qnd_parse(line):
- p = line.split('#')
- after = '#'.join(p[1:])
- if after: after = "#" + after
- s = p[0].split('"')
- after = s[-1] + after
- before = s[0]
- middle = '"'.join(s[1:-1])
- return before, middle, after
-
diff --git a/r2/r2/templates/admintranslations.html b/r2/r2/templates/admintranslations.html
deleted file mode 100644
index 7b5f933a8..000000000
--- a/r2/r2/templates/admintranslations.html
+++ /dev/null
@@ -1,113 +0,0 @@
-## The contents of this file are subject to the Common Public Attribution
-## License Version 1.0. (the "License"); you may not use this file except in
-## compliance with the License. You may obtain a copy of the License at
-## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
-## License Version 1.1, but Sections 14 and 15 have been added to cover use of
-## software over a computer network and provide for limited attribution for the
-## Original Developer. In addition, Exhibit A has been modified to be consistent
-## with Exhibit B.
-##
-## Software distributed under the License is distributed on an "AS IS" basis,
-## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
-## the specific language governing rights and limitations under the License.
-##
-## The Original Code is Reddit.
-##
-## The Original Developer is the Initial Developer. The Initial Developer of
-## the Original Code is CondeNet, Inc.
-##
-## All portions of the code written by CondeNet are Copyright (c) 2006-2010
-## CondeNet, Inc. All Rights Reserved.
-################################################################################
-
-<%namespace file="translation.html" import="statusbar"/>
-<%namespace file="printablebuttons.html" import="ynbutton, state_button" />
-<%
- from r2.lib.translation import Translator
-%>
-
-
-
-
- Edit base translation template
-
-
-
-
diff --git a/r2/r2/templates/translatedstring.html b/r2/r2/templates/translatedstring.html
deleted file mode 100644
index 83a3363d4..000000000
--- a/r2/r2/templates/translatedstring.html
+++ /dev/null
@@ -1,93 +0,0 @@
-## The contents of this file are subject to the Common Public Attribution
-## License Version 1.0. (the "License"); you may not use this file except in
-## compliance with the License. You may obtain a copy of the License at
-## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
-## License Version 1.1, but Sections 14 and 15 have been added to cover use of
-## software over a computer network and provide for limited attribution for the
-## Original Developer. In addition, Exhibit A has been modified to be consistent
-## with Exhibit B.
-##
-## Software distributed under the License is distributed on an "AS IS" basis,
-## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
-## the specific language governing rights and limitations under the License.
-##
-## The Original Code is Reddit.
-##
-## The Original Developer is the Initial Developer. The Initial Developer of
-## the Original Code is CondeNet, Inc.
-##
-## All portions of the code written by CondeNet are Copyright (c) 2006-2010
-## CondeNet, Inc. All Rights Reserved.
-################################################################################
-
-<%
- highlight = lambda x: ("" + x + " ")
- singular = thing._singular(highlight).replace("\\n", " ")
- plural = thing._plural(highlight).replace("\\n", " ")
- # downplay explanation strings
- singular = singular.replace(" {", " (").replace("}", ") ")
- plural = plural.replace(" {", " (").replace("}", ") ")
- singular = unsafe(singular)
- plural = unsafe(plural)
-
- %>
-
-%if (singular and thing.enabled) or c.user_is_admin:
-
-
- (${thing.index})
-
- %if plural:
-
- Singular: ${singular},
- Plural: ${plural}
-
- %else:
-
- ${singular}
-
- %endif
-
-
- %if c.user_is_admin and not thing.locale:
-
- %endif
-
- %if plural:
-
- %for i in xrange(thing.translator.nplurals):
- ${text_input(thing.translation(i), thing.is_valid(i),
- index=i)}
-
- (${thing.translator.plural_names[i] if len(thing.translator.plural_names) > i else "case #%d" % (i+1)})
-
- %endfor
- %else:
-
- ${text_input(thing.translation(), thing.is_valid(),
- len=len(singular))}
- %endif
- %if thing.tip:
- ${thing.tip}
- %endif
-
-
-%endif
-
-
-<%def name="text_input(text, valid, index = 0, len = 0)">
-
-
- ${text}
-
-
edit
- %if not valid:
-
- Invalid Translation
-
- %endif
-
-%def>
diff --git a/r2/r2/templates/translation.html b/r2/r2/templates/translation.html
deleted file mode 100644
index 0773217b0..000000000
--- a/r2/r2/templates/translation.html
+++ /dev/null
@@ -1,181 +0,0 @@
-## The contents of this file are subject to the Common Public Attribution
-## License Version 1.0. (the "License"); you may not use this file except in
-## compliance with the License. You may obtain a copy of the License at
-## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
-## License Version 1.1, but Sections 14 and 15 have been added to cover use of
-## software over a computer network and provide for limited attribution for the
-## Original Developer. In addition, Exhibit A has been modified to be consistent
-## with Exhibit B.
-##
-## Software distributed under the License is distributed on an "AS IS" basis,
-## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
-## the specific language governing rights and limitations under the License.
-##
-## The Original Code is Reddit.
-##
-## The Original Developer is the Initial Developer. The Initial Developer of
-## the Original Code is CondeNet, Inc.
-##
-## All portions of the code written by CondeNet are Copyright (c) 2006-2010
-## CondeNet, Inc. All Rights Reserved.
-################################################################################
-
-<%
- from md5 import md5
-%>
-
-
-
-
-%if thing.locale:
- Translation: en to ${thing.locale}
- %if thing.is_valid():
-
- %endif
-%else:
- Base Translation Template
-%endif
-
-
-
-
-
- Number complete: ${thing.num_completed}/${thing.num_total}
- ${statusbar(float(thing.num_completed)/max(thing.num_total,1))}
- ${"%d%%" % int(100 * thing.num_completed/max(thing.num_total,1))}
-
-
-
-
-
-%if not thing.is_valid():
-
-%for hash, string in thing.get_invalid():
-<%
- if len(string) > 50:
- s = string[:50] + '...'
- else:
- s = string
-%>
-Error: "${s}"
-%endfor
-
-%endif
-
-%if c.user_is_admin:
-%if thing.locale:
-
-
- « Edit base translation template
-
-
-%endif
-
-
- « Translation summary
-
-
-%endif
-
-