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 "
-
- Edit base translation template
-
-
-
-
-
- %for t in sorted(thing.translations):
- <%
- name = Translator.get_name(t)
- en_name = Translator.get_en_name(t)
- authors = Translator.get_author(t)
- frac = Translator.get_complete_frac(t)
- %>
-
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", "
-
- %endfor
-
- ${en_name}/${name}
-
-
- [${t}]
-
-
-
- %if Translator.in_use(t):
- %if Translator.is_enabled(t):
- ${state_button("disable_lang", _("disable"), \
- "return change_state(this, 'disable_lang');", _("disabled"),
- hidden_data = dict(id=t))}
- %else:
- ${state_button("enable_lang", _("enable"), \
- "return change_state(this, 'enable_lang');", _("enabled"),
- hidden_data = dict(id=t))}
- %endif
- %if t in g.languages:
- [in use]
- %endif
- %endif
-
-
-
- %if Translator.in_use(t):
- [try]
- %endif
-
-
- ${statusbar(frac)}
-
-
- ${"%d%%" % int(100*frac)}
-
-
-
-
- [add author]
-
-
-
")
- 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:
-
-
-%endif
-
-
-<%def name="text_input(text, valid, index = 0, len = 0)">
-
- (${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
-
-
-%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))}
-
- - « Edit base translation template - -
-%endif -
-%endif - -