Created
December 16, 2010 07:45
-
-
Save csytan/743166 to your computer and use it in GitHub Desktop.
db backed locale replacement for tornado on appengine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
import urllib | |
import re | |
import decimal | |
import uuid | |
import datetime | |
from google.appengine.ext import db | |
try: | |
import json | |
except: | |
from django.utils import simplejson as json | |
def google_translate(text, source='en', target='fr'): | |
subs = {} | |
for arg in re.findall('(\{.+?\})', text): | |
sub = str(uuid.uuid4()).replace('-', '') | |
subs[sub] = arg | |
text = text.replace(arg, sub) | |
params = urllib.urlencode({ | |
'v': '1.0', | |
'q': text, | |
'langpair': source + '|' + target, | |
'format': 'text' | |
}) | |
opener = urllib.FancyURLopener() | |
opener.addheader('Referer', 'http://www.caterpi.com/') | |
request = opener.open('http://ajax.googleapis.com/ajax/services/language/translate', params) | |
data = json.loads(request.read()) | |
response = data.get('responseData') | |
if response: | |
translation = response.get('translatedText') | |
for sub, arg in subs.items(): | |
translation = translation.replace(sub, arg) | |
return translation | |
def convert_currency(amount, fr='USD', to='CAD', rates={}): | |
if fr == to: | |
return amount | |
code = fr + to | |
if code not in rates: | |
csv = urllib.urlopen( | |
'http://download.finance.yahoo.com/d/quotes.csv?s=' + | |
code + '=X&f=sl1d1t1ba&e=.csv').read() | |
rates[code] = decimal.Decimal(csv.split(',')[1]) | |
return decimal.Decimal(str(amount)) * rates[code] | |
def word_count(text, lang, _non_word=re.compile(r'\W', re.UNICODE)): | |
# logosyllabic languages counted by character instead of by word | |
if lang in ['zh', 'zh-TW', 'zh-CN', 'ko', 'ja', 'vi']: | |
text = _non_word.sub('', text) | |
return len(text) | |
words = re.split(r'\s', text) | |
words = [_non_word.sub('', w) for w in words] | |
words = [w for w in words if w] | |
return len(words) | |
class Locale(object): | |
def __init__(self, code, currency=None): | |
self.code = code if code in LANGUAGES else 'en' | |
self.currency = currency if currency else 'USD' | |
# Initialize strings for date formatting | |
_ = self.translate | |
self._months = [ | |
_("January"), _("February"), _("March"), _("April"), | |
_("May"), _("June"), _("July"), _("August"), | |
_("September"), _("October"), _("November"), _("December")] | |
self._weekdays = [ | |
_("Monday"), _("Tuesday"), _("Wednesday"), _("Thursday"), | |
_("Friday"), _("Saturday"), _("Sunday")] | |
class unicode2(unicode): | |
"""Support for the >= Python 2.6 format method""" | |
def format(self, *args, **kwargs): | |
s = self | |
for key, val in kwargs.items(): | |
s = s.replace('{' + key + '}', unicode(val)) | |
return s | |
def translate(self, text, plural_text=None, count=None, key_name=None): | |
if count is not None and count != 1: | |
text = plural_text | |
if self.code == 'en': | |
return self.unicode2(text) | |
if not text: | |
return '' | |
t = SiteTranslation.get_translation(text, self.code, key_name=key_name) | |
if t: return self.unicode2(t) | |
return self.unicode2(text) | |
def format_money(self, amount, symbol=True): | |
amount = convert_currency(amount, to=self.currency) | |
prefix = CURRENCIES[self.currency] if symbol else '' | |
if self.currency == 'JPY': | |
return prefix + str(int(amount)) | |
elif self.currency == 'RUB': | |
return '%.2f' % amount + ' RUB' | |
return prefix + '%.2f' % amount | |
def format_date(self, date, gmt_offset=0): | |
"""Adapted from tornado.locale | |
Formats the given date (which should be GMT). | |
""" | |
if type(date) in (int, long, float): | |
date = datetime.datetime.utcfromtimestamp(date) | |
now = datetime.datetime.utcnow() | |
# Round down to now. Due to click skew, things are somethings | |
# slightly in the future. | |
if date > now: date = now | |
local_date = date - datetime.timedelta(minutes=gmt_offset) | |
local_now = now - datetime.timedelta(minutes=gmt_offset) | |
local_yesterday = local_now - datetime.timedelta(hours=24) | |
difference = now - date | |
seconds = difference.seconds | |
days = difference.days | |
_ = self.translate | |
format = None | |
if days == 0: | |
if seconds < 50: | |
return _("Seconds ago") | |
if seconds < 50 * 60: | |
minutes = round(seconds / 60.0) | |
return _("1 minute ago", "{minutes} minutes ago", | |
minutes).format(minutes=int(minutes)) | |
hours = round(seconds / (60.0 * 60)) | |
return _("1 hour ago", "{hours} hours ago", | |
hours).format(hours=int(hours)) | |
if days == 0: | |
format = _("{time}") | |
elif days == 1 and local_date.day == local_yesterday.day: | |
format = _("Yesterday") | |
elif days < 5: | |
format = _("{weekday}") | |
elif days < 334: # 11mo, since confusing for same month last year | |
format = _("{month_name} {day}") | |
if format is None: | |
format = _("{month_name} {day}, {year}") | |
return format.format( | |
month_name=self._months[local_date.month - 1], | |
weekday=self._weekdays[local_date.weekday()], | |
day=str(local_date.day), | |
year=str(local_date.year) | |
) | |
class SiteTranslation(db.Expando): | |
text = db.TextProperty() | |
@classmethod | |
def init_lang(cls, target): | |
assert target in LANGUAGES | |
keys = ['January', 'February', 'March', 'April', | |
'May', 'June', 'July', 'August', | |
'September', 'October', 'November', 'December', | |
'Monday', 'Tuesday', 'Wednesday', 'Thursday', | |
'Friday', 'Saturday', 'Sunday'] | |
keys += LANGUAGES.values() | |
for t in cls.get_by_key_name(keys): | |
if not getattr(t, 'translation_' + target, None): | |
translation = google_translate(t.text, target=target) | |
t.set(translation, target) | |
@staticmethod | |
def str_to_key(s): | |
# key_name is limited to the first 500 bytes of the source string | |
return s.encode('utf-8')[:500].decode('utf-8') | |
@classmethod | |
def get_translation(cls, text, locale, key_name=None, cache={}): | |
if not key_name: | |
key_name = cls.str_to_key(text) | |
if not cache: | |
# preload translations | |
for t in cls.all().fetch(1000): | |
cache[t.key().name()] = t | |
if key_name in cache: | |
t = cache[key_name] | |
else: | |
t = cls.get_or_insert(key_name=key_name, text=text) | |
cache[key_name] = t | |
return getattr(t, 'translation_' + locale, None) | |
def set(self, translation, target): | |
assert target in LANGUAGES | |
setattr(self, 'translation_' + target, db.Text(translation)) | |
self.put() | |
class SiteTranslationChange(db.Model): | |
created = db.DateTimeProperty(auto_now_add=True) | |
user = db.ReferenceProperty() | |
site_translation = db.ReferenceProperty(SiteTranslation) | |
lang = db.StringProperty() | |
translation = db.TextProperty() | |
SITE_LANGUAGES = ['en', 'zh', 'es', 'ja', 'ru', 'fr'] | |
SITE_CURRENCIES = ['USD', 'CNY', 'JPY', 'EUR', 'CAD', 'GBP', 'RUB'] | |
LANGUAGES = { | |
'af': 'Afrikaans', | |
'am': 'Amharic', | |
'ar': 'Arabic', | |
'az': 'Azerbaijani', | |
'be': 'Belarusian', | |
'bg': 'Bulgarian', | |
'bh': 'Bihari', | |
'bn': 'Bengali', | |
'bo': 'Tibetan', | |
'ca': 'Catalan', | |
'chr': 'Cherokee', | |
'cs': 'Czech', | |
'da': 'Danish', | |
'de': 'German', | |
'dv': 'Dhivehi', | |
'el': 'Greek', | |
'en': 'English', | |
'eo': 'Esperanto', | |
'es': 'Spanish', | |
'es-ES': 'Spanish (Spain)', | |
'es-LA': 'Spanish (Latin America)', | |
'et': 'Estonian', | |
'eu': 'Basque', | |
'fa': 'Persian', | |
'fi': 'Finnish', | |
'fr': 'French', | |
'gl': 'Galician', | |
'gn': 'Guarani', | |
'gu': 'Gujarati', | |
'hi': 'Hindi', | |
'hr': 'Croatian', | |
'hu': 'Hungarian', | |
'hy': 'Armenian', | |
'id': 'Indonesian', | |
'is': 'Icelandic', | |
'it': 'Italian', | |
'iu': 'Inuktitut', | |
'ga': 'Irish', | |
'iw': 'Hebrew', | |
'ja': 'Japanese', | |
'ka': 'Georgian', | |
'kk': 'Kazakh', | |
'km': 'Khmer', | |
'kn': 'Kannada', | |
'ko': 'Korean', | |
'ku': 'Kurdish', | |
'ky': 'Kyrgyz', | |
'lo': 'Laothian', | |
'lt': 'Lithuanian', | |
'lv': 'Latvian', | |
'mk': 'Macedonian', | |
'ml': 'Malayalam', | |
'mn': 'Mongolian', | |
'mr': 'Marathi', | |
'ms': 'Malay', | |
'mt': 'Maltese', | |
'my': 'Burmese', | |
'ne': 'Nepali', | |
'nl': 'Dutch', | |
'no': 'Norwegian', | |
'or': 'Oriya', | |
'pa': 'Punjabi', | |
'pl': 'Polish', | |
'ps': 'Pashto', | |
'pt': 'Portuguese', | |
'ro': 'Romanian', | |
'ru': 'Russian', | |
'sa': 'Sanskrit', | |
'sd': 'Sindhi', | |
'si': 'Sinhalese', | |
'sk': 'Slovak', | |
'sl': 'Slovenian', | |
'sq': 'Albanian', | |
'sr': 'Serbian', | |
'sv': 'Swedish', | |
'sw': 'Swahili', | |
'ta': 'Tamil', | |
'te': 'Telugu', | |
'tg': 'Tajik', | |
'th': 'Thai', | |
'tl': 'Filipino', | |
'tr': 'Turkish', | |
'ug': 'Uighur', | |
'uk': 'Ukrainian', | |
'ur': 'Urdu', | |
'uz': 'Uzbek', | |
'vi': 'Vietnamese', | |
'zh': 'Chinese', | |
'zh-CN': 'Chinese (simplified)', | |
'zh-TW': 'Chinese (traditional)' | |
} | |
LANGUAGE_CODES = LANGUAGES.keys() | |
NATIVE_LANGUAGES = { | |
'af': u'Afrikaans', | |
'ar': u'العربية', | |
'az': u'Azərbaycan dili', | |
'bg': u'Български', | |
'bn': u'বাংলা', | |
'bs': u'Bosanski', | |
'ca': u'Català', | |
'cs': u'Čeština', | |
'cy': u'Cymraeg', | |
'da': u'Dansk', | |
'de': u'Deutsch', | |
'el': u'Ελληνικά', | |
'en': u'English', | |
'eo': u'Esperanto', | |
'es': u'Español', | |
'et': u'Eesti', | |
'eu': u'Euskara', | |
'fa': u'فارسی', | |
'fi': u'Suomi', | |
'fo': u'Føroyskt', | |
'fr': u'Français', | |
'ga': u'Gaeilge', | |
'gl': u'Galego', | |
'he': u'עברית', | |
'hi': u'हिन्दी', | |
'hr': u'Hrvatski', | |
'hu': u'Magyar', | |
'id': u'Bahasa Indonesia', | |
'is': u'Íslenska', | |
'it': u'Italiano', | |
'ja': u'日本語', | |
'ka': u'ქართული', | |
'ko': u'한국어', | |
'la': u'lingua latina', | |
'lt': u'Lietuvių', | |
'lv': u'Latviešu', | |
'mk': u'Македонски', | |
'ml': u'മലയാളം', | |
'ms': u'Bahasa Melayu', | |
'nb': u'Norsk (bokmål)', | |
'ne': u'नेपाली', | |
'nl': u'Nederlands', | |
'nn': u'Norsk (nynorsk)', | |
'pa': u'ਪੰਜਾਬੀ', | |
'pl': u'Polski', | |
'pt': u'Português', | |
'ro': u'Română', | |
'ru': u'Русский', | |
'sk': u'Slovenčina', | |
'sl': u'Slovenščina', | |
'sq': u'Shqip', | |
'sr': u'Српски', | |
'sv': u'Svenska', | |
'sw': u'Kiswahili', | |
'ta': u'தமிழ்', | |
'te': u'తెలుగు', | |
'th': u'ภาษาไทย', | |
'tl': u'Filipino', | |
'tr': u'Türkçe', | |
'uk': u'Українська', | |
'vi': u'Tiếng Việt', | |
'zh': u'中文' | |
} | |
CURRENCIES = { | |
'ALL': u'Lek', | |
'USD': u'$', | |
'AFN': u'؋', | |
'ARS': u'$', | |
'AWG': u'ƒ', | |
'AUD': u'$', | |
'AZN': u'ман', | |
'BSD': u'$', | |
'BBD': u'$', | |
'BYR': u'p.', | |
'EUR': u'€', | |
'BZD': u'BZ$', | |
'BMD': u'$', | |
'BOB': u'$b', | |
'BAM': u'KM', | |
'BWP': u'P', | |
'BGN': u'лв', | |
'BRL': u'R$', | |
'GBP': u'£', | |
'BND': u'$', | |
'KHR': u'៛', | |
'CAD': u'$', | |
'KYD': u'$', | |
'CLP': u'$', | |
'CNY': u'元', | |
'COP': u'$', | |
'CRC': u'₡', | |
'HRK': u'kn', | |
'CUP': u'₱', | |
'EUR': u'€', | |
'CZK': u'Kč', | |
'DKK': u'kr', | |
'DOP': u'RD$', | |
'XCD': u'$', | |
'EGP': u'£', | |
'SVC': u'$', | |
'GBP': u'£', | |
'EEK': u'kr', | |
'EUR': u'€', | |
'FKP': u'£', | |
'FJD': u'$', | |
'EUR': u'€', | |
'GHC': u'¢', | |
'GIP': u'£', | |
'EUR': u'€', | |
'GTQ': u'Q', | |
'GGP': u'£', | |
'GYD': u'$', | |
'EUR': u'€', | |
'HNL': u'L', | |
'HKD': u'$', | |
'HUF': u'Ft', | |
'ISK': u'kr', | |
'INR': u'₨', | |
'IDR': u'Rp', | |
'IRR': u'﷼', | |
'EUR': u'€', | |
'IMP': u'£', | |
'ILS': u'₪', | |
'EUR': u'€', | |
'JMD': u'J$', | |
'JPY': u'¥', | |
'JEP': u'£', | |
'KZT': u'лв', | |
'KPW': u'₩', | |
'KRW': u'₩', | |
'KGS': u'лв', | |
'LAK': u'₭', | |
'LVL': u'Ls', | |
'LBP': u'£', | |
'LRD': u'$', | |
'CHF': u'CHF', | |
'LTL': u'Lt', | |
'EUR': u'€', | |
'MKD': u'ден', | |
'MYR': u'RM', | |
'EUR': u'€', | |
'MUR': u'₨', | |
'MXN': u'$', | |
'MNT': u'₮', | |
'MZN': u'MT', | |
'NAD': u'$', | |
'NPR': u'₨', | |
'ANG': u'ƒ', | |
'EUR': u'€', | |
'NZD': u'$', | |
'NIO': u'C$', | |
'NGN': u'₦', | |
'KPW': u'₩', | |
'NOK': u'kr', | |
'OMR': u'﷼', | |
'PKR': u'₨', | |
'PAB': u'B/.', | |
'PYG': u'Gs', | |
'PEN': u'S/.', | |
'PHP': u'Php', | |
'PLN': u'zł', | |
'QAR': u'﷼', | |
'RON': u'lei', | |
'RUB': u'руб', | |
'SHP': u'£', | |
'SAR': u'﷼', | |
'RSD': u'Дин.', | |
'SCR': u'₨', | |
'SGD': u'$', | |
'EUR': u'€', | |
'SBD': u'$', | |
'SOS': u'S', | |
'ZAR': u'R', | |
'KRW': u'₩', | |
'EUR': u'€', | |
'LKR': u'₨', | |
'SEK': u'kr', | |
'CHF': u'CHF', | |
'SRD': u'$', | |
'SYP': u'£', | |
'TWD': u'NT$', | |
'THB': u'฿', | |
'TTD': u'TT$', | |
'TRY': u'TL', | |
'TRL': u'₤', | |
'TVD': u'$', | |
'UAH': u'₴', | |
'GBP': u'£', | |
'USD': u'$', | |
'UYU': u'$U', | |
'UZS': u'лв', | |
'EUR': u'€', | |
'VEF': u'Bs', | |
'VND': u'₫', | |
'YER': u'﷼', | |
'ZWD': u'Z$' | |
} | |
Locale.SITE_LANGUAGES = SITE_LANGUAGES | |
Locale.SITE_CURRENCIES = SITE_CURRENCIES | |
Locale.LANGUAGES = LANGUAGES | |
Locale.NATIVE_LANGUAGES = NATIVE_LANGUAGES | |
Locale.CURRENCIES = CURRENCIES |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment