diff options
Diffstat (limited to '')
-rwxr-xr-x | clock | 302 |
1 files changed, 302 insertions, 0 deletions
@@ -0,0 +1,302 @@ +#!/usr/bin/env python +from __future__ import unicode_literals + +import glob +import json +import os + +from babel.core import get_global +from babel.dates import PATTERN_CHARS +from babel.dates import tokenize_pattern +from babel.localedata import LocaleDataDict +from babel.localedata import load +from babel.localedata import normalize_locale +from babel.plural import PluralRule +from babel.plural import _binary_compiler +from babel.plural import _GettextCompiler +from babel.plural import _unary_compiler +from babel.plural import compile_zero +from cleo import Application +from cleo import Command +from cleo import argument + +from pendulum import __version__ + + +class _LambdaCompiler(_GettextCompiler): + """Compiles the expression to lambda function.""" + + compile_v = compile_zero + compile_w = compile_zero + compile_f = compile_zero + compile_t = compile_zero + compile_and = _binary_compiler("(%s and %s)") + compile_or = _binary_compiler("(%s or %s)") + compile_not = _unary_compiler("(not %s)") + compile_mod = _binary_compiler("(%s %% %s)") + + def compile_relation(self, method, expr, range_list): + code = _GettextCompiler.compile_relation(self, method, expr, range_list) + code = code.replace("&&", "and") + code = code.replace("||", "or") + if method == "in": + expr = self.compile(expr) + code = "(%s == %s and %s)" % (expr, expr, code) + return code + + +class LocaleCreate(Command): + + name = "create" + description = "Creates locale translations." + + arguments = [argument("locales", "Locales to dump.", optional=False, multiple=True)] + + TEMPLATE = """# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from .custom import translations as custom_translations + + +\"\"\" +{locale} locale file. + +It has been generated automatically and must not be modified directly. +\"\"\" + + +locale = {{ + 'plural': {plural}, + 'ordinal': {ordinal}, + 'translations': {translations}, + 'custom': custom_translations +}} +""" + + CUSTOM_TEMPLATE = """# -*- coding: utf-8 -*- +from __future__ import unicode_literals + + +\"\"\" +{locale} custom locale file. +\"\"\" + +translations = {{}} +""" + + LOCALE_DIR = os.path.join("pendulum", "locales") + + def handle(self): + locales = self.argument("locales") + if not locales: + return + + for locale in locales: + data = {} + parts = locale.split("-") + if len(parts) > 1: + parts[1] = parts[1].upper() + + normalized = normalize_locale(locale.replace("-", "_")) + if not normalized: + self.line("<error>Locale [{}] does not exist.</error>".format(locale)) + continue + + self.line("<info>Generating <comment>{}</> locale.</>".format(locale)) + + content = LocaleDataDict(load(normalized)) + + # Pluralization rule + rule = content["plural_form"] + plural = self.plural_rule_to_lambda(rule) + + # Ordinal rule + rule = content["ordinal_form"] + ordinal = self.plural_rule_to_lambda(rule) + + # Getting days names + days = content["days"]["format"] + data["days"] = {} + for fmt, names in days.items(): + data["days"][fmt] = {} + for value, name in names.items(): + data["days"][fmt][(value + 1) % 7] = name + + # Getting months names + months = content["months"]["format"] + data["months"] = months + + # Units + patterns = content["unit_patterns"] + units = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "microsecond", + ] + data["units"] = {} + for unit in units: + pattern = patterns["duration-{}".format(unit)]["long"] + if "per" in pattern: + del pattern["per"] + + data["units"][unit] = pattern + + # Relative + data["relative"] = {} + for key in content["date_fields"]: + if key not in [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + ]: + continue + + data["relative"][key] = content["date_fields"][key] + + # Day periods + data["day_periods"] = content["day_periods"]["format"]["wide"] + + result = self.TEMPLATE.format( + locale=locale, + plural=plural, + ordinal=ordinal, + translations=self.format_dict(data, tab=2), + ) + + dest_dir = os.path.join(self.LOCALE_DIR, locale.replace("-", "_")) + if not os.path.exists(dest_dir): + os.mkdir(dest_dir) + + init = os.path.join(dest_dir, "__init__.py") + main = os.path.join(dest_dir, "locale.py") + custom = os.path.join(dest_dir, "custom.py") + + if not os.path.exists(init): + with open(init, "w"): + os.utime(init) + + with open(main, "w") as fw: + fw.write(result) + + if not os.path.exists(custom): + with open(custom, "w") as fw: + fw.write(self.CUSTOM_TEMPLATE.format(locale=locale)) + + def format_dict(self, d, tab=1): + s = ["{\n"] + for k, v in d.items(): + if isinstance(v, (dict, LocaleDataDict)): + v = self.format_dict(v, tab + 1) + else: + v = repr(v) + + s.append("%s%r: %s,\n" % (" " * tab, k, v)) + s.append("%s}" % (" " * (tab - 1))) + + return "".join(s) + + def plural_rule_to_lambda(self, rule): + to_py = _LambdaCompiler().compile + result = ["lambda n: "] + for tag, ast in PluralRule.parse(rule).abstract: + result.append("'%s' if %s else " % (tag, to_py(ast))) + result.append("'other'") + return "".join(result) + + def convert_ldml_format(self, fmt): + result = [] + + for tok_type, tok_value in tokenize_pattern(fmt): + if tok_type == "chars": + result.append(tok_value.replace("%", "%%")) + elif tok_type == "field": + fieldchar, fieldnum = tok_value + limit = PATTERN_CHARS[fieldchar] + if limit and fieldnum not in limit: + raise ValueError( + "Invalid length for field: %r" % (fieldchar * fieldnum) + ) + result.append( + self.TOKENS_MAP.get(fieldchar * fieldnum, fieldchar * fieldnum) + ) + else: + raise NotImplementedError("Unknown token type: %s" % tok_type) + + return "".join(result) + + +class LocaleRecreate(Command): + + name = "recreate" + description = "Recreate existing locales." + + def handle(self): + # Listing locales + + locales_dir = os.path.join("pendulum", "locales") + locales = glob.glob(os.path.join(locales_dir, "*", "locale.py")) + locales = [os.path.basename(os.path.dirname(l)) for l in locales] + + self.call("locale:create", [("locales", locales)]) + + +class LocaleCommand(Command): + + name = "locale" + description = "Locale related commands." + + commands = [LocaleCreate()] + + def handle(self): + self.call("help", self._config.name) + + +class WindowsTzDump(Command): + + name = "dump-timezones" + description = "Dumps the mapping of Windows timezones to IANA timezones." + + MAPPING_DIR = os.path.join("pendulum", "tz", "data") + + def handle(self): + raw_tznames = get_global("windows_zone_mapping") + sorted_names = sorted(list(raw_tznames.keys())) + + tznames = {} + for name in sorted_names: + tznames[name] = raw_tznames[name] + + mapping = json.dumps(tznames, indent=4) + mapping = "windows_timezones = " + mapping.replace('"', "'") + "\n" + + with open(os.path.join(self.MAPPING_DIR, "windows.py"), "w") as f: + f.write(mapping) + + +class WindowsCommand(Command): + + name = "windows" + description = "Windows related commands." + + commands = [WindowsTzDump()] + + def handle(self): + self.call("help", self._config.name) + + +app = Application("clock", __version__) +app.add(LocaleCommand()) +app.add(WindowsCommand()) + + +if __name__ == "__main__": + app.run() |