Просмотр исходного кода

use 3rd party libs for ember.js gettext

Rafał Pitoń 10 лет назад
Родитель
Сommit
c571aece11

+ 0 - 234
misago/core/management/commands/makemessages.py

@@ -1,234 +0,0 @@
-import codecs
-from hashlib import md5
-import os
-import re
-
-from path import Path
-
-from django.core.management.commands.makemessages import Command as BaseCommand
-from django.utils.text import smart_split
-
-
-I18N_HELPERS = {
-    # helper: min valid expression len
-    'gettext': 2,
-    'ngettext': 4,
-    'gettext_noop': 2,
-    'pgettext': 3,
-    'npgettext': 5
-}
-
-HBS_HELPERS = ('unbound', 'if')
-HBS_EXPRESSION = re.compile(r'({{{(.*?)}}})|({{(.*?)}})')
-
-
-class HandlebarsExpression(object):
-    def __init__(self, unparsed_expression):
-        cleaned_expression = self.clean_expression(unparsed_expression)
-        all_helpers = self.parse_expression(
-            unparsed_expression, cleaned_expression)
-
-        self.i18n_helpers = self.clean_helpers(all_helpers)
-
-    def get_i18n_helpers(self):
-        return self.i18n_helpers
-
-    def clean_expression(self, unparsed):
-        cleaned = u''
-
-        for piece in smart_split(unparsed):
-            if not cleaned and piece in HBS_HELPERS:
-                continue
-            if not piece.startswith('=') and not cleaned.endswith('='):
-                cleaned += ' '
-            cleaned += piece
-
-        return cleaned.strip()
-
-    def parse_expression(self, unparsed, cleaned):
-        helper = []
-        helpers = [helper]
-        stack = [helper]
-
-        for piece in smart_split(cleaned):
-            if piece.endswith(')'):
-                stack[-1].append(piece.rstrip(')').strip())
-                while piece.endswith(')'):
-                    piece = piece[:-1].strip()
-                    stack.pop()
-                continue
-
-            if not piece.startswith(('\'', '"')):
-                if piece.startswith('('):
-                    piece = piece[1:].strip()
-                    if piece.startswith('('):
-                        continue
-                    else:
-                        helper = [piece]
-                        helpers.append(helper)
-                        stack.append(helper)
-                else:
-                    is_kwarg = re.match(r'^[_a-zA-Z]+([_a-zA-Z0-9]+?)=', piece)
-                    if is_kwarg and not piece.endswith('='):
-                        piece = piece[len(is_kwarg.group(0)):]
-                        if piece.startswith('('):
-                            helper = [piece[1:].strip()]
-                            helpers.append(helper)
-                            stack.append(helper)
-                    else:
-                        stack[-1].append(piece)
-            else:
-                stack[-1].append(piece)
-
-        return helpers
-
-    def clean_helpers(self, all_helpers):
-        i18n_helpers = []
-        for helper in all_helpers:
-            i18n_helper_len = I18N_HELPERS.get(helper[0])
-            if i18n_helper_len and len(helper) >= i18n_helper_len:
-                i18n_helpers.append(helper[:i18n_helper_len])
-        return i18n_helpers
-
-
-class HandlebarsTemplate(object):
-    def __init__(self, content):
-        self.expressions = {}
-        self.content = content
-
-    def get_converted_content(self):
-        stripped_content = self.strip_expressions(self.content)
-        stripped_content = self.strip_non_expressions(stripped_content)
-        replaced_content = self.replace_expressions(stripped_content)
-        return replaced_content
-
-    def strip_expressions(self, content):
-        def replace_expression(matchobj):
-            trimmed_expression = matchobj.group(0).lstrip('{').rstrip('}')
-            parsed_expression = HandlebarsExpression(trimmed_expression)
-
-            expression_i18n_helpers = parsed_expression.get_i18n_helpers()
-
-            if expression_i18n_helpers:
-                self.expressions[matchobj.group(0)] = expression_i18n_helpers
-                return matchobj.group(0)
-            else:
-                return ''
-
-        return HBS_EXPRESSION.sub(replace_expression, self.content)
-
-    def strip_non_expressions(self, content):
-        stripped = u''
-
-        cursor = 0
-        for expression in HBS_EXPRESSION.finditer(content):
-            position = content.find(expression.group(0), cursor)
-
-            content_slice = content[cursor:position]
-            if content_slice:
-                slice_lines = len(content_slice.splitlines())
-                if slice_lines:
-                    stripped += '\n' * (slice_lines - 1)
-
-            stripped += expression.group(0)
-            cursor = position + len(expression.group(0))
-
-        return stripped
-
-    def replace_expressions(self, content):
-        def replace_expression(matchobj):
-            js_functions = []
-            for helper in self.expressions.get(matchobj.group(0)):
-                function, args = helper[0], helper[1:]
-                js_functions.append('%s(%s);' % (function, ', '.join(args)))
-            return ' '.join(js_functions)
-
-        return HBS_EXPRESSION.sub(replace_expression, content)
-
-
-class HandlebarsFile(object):
-    def __init__(self, hbs_path, make_js_file=True):
-        self.hbs_path = hbs_path
-        self.path_suffix = self.make_js_path_suffix(hbs_path)
-        self.js_path = self.make_js_path(hbs_path, self.path_suffix)
-
-        if make_js_file:
-            self.make_js_file(self.hbs_path, self.js_path)
-
-    def make_js_path_suffix(self, hbs_path):
-        return '%s.makemessages.js' % md5(hbs_path).hexdigest()[:8]
-
-    def make_js_path(self, hbs_path, path_suffix):
-        return Path('%s.%s' % (unicode(hbs_path), path_suffix))
-
-    def make_js_file(self, hbs_path, js_path):
-        file_content = u''
-        with codecs.open(hbs_path, encoding='utf-8', mode="r") as hbs_file:
-            file_content = hbs_file.read()
-
-        js_file = codecs.open(js_path, encoding='utf-8', mode='w')
-        js_file.write(HandlebarsTemplate(file_content).get_converted_content())
-        js_file.close()
-
-    def delete(self):
-        if self.js_path.exists() and self.js_path.isfile():
-            self.js_path.unlink()
-
-
-class Command(BaseCommand):
-    help = ("Runs over the entire source tree of the current directory and "
-"pulls out all strings marked for translation. It creates (or updates) a message "
-"file in the conf/locale (in the django tree) or locale (for projects and "
-"applications) directory.\n\nIf command is executed for JavaScript files, it "
-"also pulls strings from Misago Handlebars.js files.\n\nYou must run this "
-"command with one of either the --locale, --exclude or --all options.")
-
-    JS_TEMPLATES = ('.hbs', '.handlebars')
-
-    def handle(self, *args, **options):
-        locales = options.get('locale')
-        self.domain = options.get('domain')
-
-        subdirs = [unicode(d.basename()) for d in Path(os.getcwd()).dirs()]
-        use_subroutines = 'locale' in subdirs and self.domain == 'djangojs'
-
-        tmp_js_files = []
-        if use_subroutines:
-            # fake js files from templates
-            tmp_js_files = self.prepare_tmp_js_files();
-
-        super(Command, self).handle(*args, **options)
-
-        if use_subroutines:
-            # cleanup everything
-            self.cleanup_tmp_js_templates(tmp_js_files);
-            self.cleanup_po_files(locales, tmp_js_files);
-
-    def prepare_tmp_js_files(self):
-        files = []
-        for hbs_file in Path(os.getcwd()).walkfiles('*.hbs'):
-            files.append(HandlebarsFile(hbs_file))
-        return files
-
-    def cleanup_po_files(self, locales, tmp_js_files):
-        strip_tokens = [js_file.path_suffix for js_file in tmp_js_files]
-
-        for po_file in Path(os.getcwd()).walkfiles('djangojs.po'):
-            if not locales or po_file.splitall()[-3] in locales:
-                self.cleanup_po_file(po_file, strip_tokens)
-
-    def cleanup_po_file(self, po_path, strip_tokens):
-        file_content = u''
-        with codecs.open(po_path, encoding='utf-8', mode="r") as po_file:
-            file_content = po_file.read()
-
-        for token in strip_tokens:
-            file_content = file_content.replace(token, '')
-
-        po_file = codecs.open(po_path, encoding='utf-8', mode='w')
-        po_file.write(file_content)
-        po_file.close()
-
-    def cleanup_tmp_js_templates(self, tmp_js_files):
-        for js_file in tmp_js_files:
-            js_file.delete()

+ 0 - 252
misago/core/tests/test_makemessages.py

@@ -1,252 +0,0 @@
-from django.test import TestCase
-
-from misago.core.management.commands.makemessages import (
-    HandlebarsExpression, HandlebarsTemplate, HandlebarsFile)
-
-
-class HandlebarsExpressionTests(TestCase):
-    def test_get_i18n_helpers(self):
-        """expression parser finds i18n helpers"""
-        expression = HandlebarsExpression("some.expression")
-        self.assertFalse(expression.get_i18n_helpers())
-
-        expression = HandlebarsExpression("bind-attr src=user.avatar")
-        self.assertFalse(expression.get_i18n_helpers())
-
-        expression = HandlebarsExpression("gettext 'misiek'")
-        self.assertTrue(expression.get_i18n_helpers())
-
-        expression = HandlebarsExpression("gettext '%(user)s has %(trait)s' user=user.username trait=(gettext user.trait)")
-        helpers = expression.get_i18n_helpers()
-        self.assertEqual(len(helpers), 2)
-        self.assertEqual(helpers[0], ['gettext', "'%(user)s has %(trait)s'"])
-        self.assertEqual(helpers[1], ['gettext', "user.trait"])
-
-        expression = HandlebarsExpression('gettext "%(param)s!" param = (gettext "nested once" param = (gettext "nested twice")) otherparam= (gettext "nested once again")')
-        helpers = expression.get_i18n_helpers()
-        self.assertEqual(len(helpers), 4)
-        self.assertEqual(helpers[0], ['gettext', '"%(param)s!"'])
-        self.assertEqual(helpers[1], ['gettext', '"nested once"'])
-        self.assertEqual(helpers[2], ['gettext', '"nested twice"'])
-        self.assertEqual(helpers[3], ['gettext', '"nested once again"'])
-
-
-class HandlebarsTemplateTests(TestCase):
-    def test_empty_file(self):
-        """empty file causes no errors"""
-        template = HandlebarsTemplate("")
-        self.assertEqual(template.get_converted_content(), "")
-
-    def test_stripped_expression(self):
-        """non-i18n expression is stripped"""
-        template = HandlebarsTemplate("{{ some.expression }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-    def test_invalid_expression_stripping(self):
-        """invalid i18n expressions are stripped"""
-        template = HandlebarsTemplate("{{gettext }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{ngettext }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{ngettext 'apple' }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{ngettext 'apple' 'apples' }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{gettext_noop }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{pgettext }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{pgettext 'apple' }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-        template = HandlebarsTemplate("{{npgettext 'fruit' 'apple' }}")
-        self.assertEqual(template.get_converted_content(), "")
-
-    def test_valid_expression_replace(self):
-        """valid i18n expressions are replaced"""
-        VALID_CASES = (
-            '%s',
-            'unbound %s',
-            'something (%s)',
-            'unbound something (%s)',
-            'if condition (%s)',
-            'something "lorem ipsum" some.var kwarg=(%s) otherkwarg=(helper something)'
-        )
-
-        for case in VALID_CASES:
-            self.subtest("{{%s}}" % case)
-            self.subtest("{{ %s }}" % case)
-            self.subtest("{{{%s}}}" % case)
-            self.subtest("{{{ %s }}}" % case)
-
-    def subtest(self, case_template):
-        CASES = (
-            (
-                "gettext 'Lorem ipsum'",
-                "gettext('Lorem ipsum');"
-            ),
-            (
-                "gettext 'Lorem %(vis)s' vis=name",
-                "gettext('Lorem %(vis)s');"
-            ),
-            (
-                "gettext 'Lorem %(vis)s' vis=(gettext user.vis)",
-                "gettext('Lorem %(vis)s'); gettext(user.vis);"
-            ),
-            (
-                "gettext some_variable",
-                "gettext(some_variable);"
-            ),
-            (
-                "gettext 'Lorem ipsum'",
-                "gettext('Lorem ipsum');"
-            ),
-            (
-                "gettext 'Lorem %(vis)s' vis=name",
-                "gettext('Lorem %(vis)s');"
-            ),
-            (
-                "gettext some_variable",
-                "gettext(some_variable);"
-            ),
-            (
-                "gettext some_variable user=user.username",
-                "gettext(some_variable);"
-            ),
-            (
-                "ngettext '%(count)s apple' '%(count)s apples' apples_count",
-                "ngettext('%(count)s apple', '%(count)s apples', apples_count);"
-            ),
-            (
-                "ngettext '%(user)s has %(count)s apple' '%(user)s has %(count)s apples' apples_count user=user.username",
-                "ngettext('%(user)s has %(count)s apple', '%(user)s has %(count)s apples', apples_count);"
-            ),
-            (
-                "ngettext apple apples apples_count",
-                "ngettext(apple, apples, apples_count);"
-            ),
-            (
-                "ngettext '%(count)s apple' apples apples_count",
-                "ngettext('%(count)s apple', apples, apples_count);"
-            ),
-            (
-                "ngettext '%(user)s has %(count)s apple' apples apples_count user=user.username",
-                "ngettext('%(user)s has %(count)s apple', apples, apples_count);"
-            ),
-            (
-                "gettext_noop 'Lorem ipsum'",
-                "gettext_noop('Lorem ipsum');"
-            ),
-            (
-                "gettext_noop 'Lorem %(vis)s' vis=name",
-                "gettext_noop('Lorem %(vis)s');"
-            ),
-            (
-                "gettext_noop some_variable",
-                "gettext_noop(some_variable);"
-            ),
-            (
-                "gettext_noop 'Lorem ipsum'",
-                "gettext_noop('Lorem ipsum');"
-            ),
-            (
-                "gettext_noop 'Lorem %(vis)s' vis=name",
-                "gettext_noop('Lorem %(vis)s');"
-            ),
-            (
-                "gettext_noop some_variable",
-                "gettext_noop(some_variable);"
-            ),
-            (
-                "pgettext 'month' 'may'",
-                "pgettext('month', 'may');"
-            ),
-            (
-                "pgettext 'month' month_name",
-                "pgettext('month', month_name);"
-            ),
-            (
-                "pgettext 'day of month' 'May, %(day)s' day=calendar.day",
-                "pgettext('day of month', 'May, %(day)s');"
-            ),
-            (
-                "pgettext context value day=calendar.day",
-                "pgettext(context, value);"
-            ),
-            (
-                "npgettext 'fruits' '%(count)s apple' '%(count)s apples' apples_count",
-                "npgettext('fruits', '%(count)s apple', '%(count)s apples', apples_count);"
-            ),
-            (
-                "npgettext 'fruits' '%(user)s has %(count)s apple' '%(user)s has %(count)s apples' apples_count user=user.username",
-                "npgettext('fruits', '%(user)s has %(count)s apple', '%(user)s has %(count)s apples', apples_count);"
-            ),
-            (
-                "npgettext context apple apples apples_count",
-                "npgettext(context, apple, apples, apples_count);"
-            ),
-            (
-                "npgettext context '%(count)s apple' apples apples_count",
-                "npgettext(context, '%(count)s apple', apples, apples_count);"
-            ),
-            (
-                "npgettext 'fruits' '%(user)s has %(count)s apple' apples apples_count user=user.username",
-                "npgettext('fruits', '%(user)s has %(count)s apple', apples, apples_count);"
-            ),
-        )
-
-        assertion_msg = """
-HBS template was parsed incorrectly:
-
-input:      %s
-output:     %s
-expected:   %s
-"""
-
-        for test, expected_output in CASES:
-            test_input = case_template % test
-
-            template = HandlebarsTemplate(case_template % test)
-            test_output = template.get_converted_content()
-
-            self.assertEqual(
-                test_output, expected_output,
-                assertion_msg % (test_input, test_output, expected_output))
-
-    def test_multiple_expressions(self):
-        """multiple expressions are handled"""
-        template = HandlebarsTemplate("{{gettext 'Posted by:'}} <strong>{{gettext user.rank.title}}</strong>; <em>{{ user.city }}</em>")
-        self.assertEqual(template.get_converted_content(),
-                         "gettext('Posted by:');gettext(user.rank.title);")
-
-        template = HandlebarsTemplate("""{{gettext 'Posted by:'}}<br>
-                                         {{gettext user.rank.title}}""")
-        self.assertEqual(template.get_converted_content(),
-                         "gettext('Posted by:');\ngettext(user.rank.title);")
-
-        template = HandlebarsTemplate("""<h1>{{ thread.title }}</h1>
-            {{gettext 'Posted by:'}}<br>
-
-            {{gettext user.rank.title}}""")
-        self.assertEqual(template.get_converted_content(),
-                         "\ngettext('Posted by:');\n\ngettext(user.rank.title);")
-
-
-class HandlebarsFileTests(TestCase):
-    def test_make_js_path(self):
-        """Object correctly translates hbs path to temp js path"""
-        hbs_path = "templates/application.hbs"
-        test_file = HandlebarsFile(hbs_path, False)
-
-        suffix = test_file.make_js_path_suffix(hbs_path)
-        self.assertTrue(suffix.endswith(".makemessages.js"))
-
-        js_path = test_file.make_js_path(hbs_path, suffix)
-        self.assertTrue(js_path.startswith(hbs_path))
-        self.assertTrue(js_path.endswith(suffix))

+ 2 - 1
misago/emberapp/app/app.js

@@ -1,8 +1,8 @@
 import Ember from 'ember';
 import Resolver from 'ember/resolver';
 import loadInitializers from 'ember/load-initializers';
+import registerGettextHelpers from 'django-ember-gettext/helpers/gettext';
 import config from './config/environment';
-import 'misago/helpers/gettext';
 
 Ember.MODEL_FACTORY_INJECTIONS = true;
 
@@ -13,6 +13,7 @@ var App = Ember.Application.extend({
   Resolver: Resolver
 });
 
+registerGettextHelpers();
 loadInitializers(App, config.modulePrefix);
 
 export default App;

+ 0 - 62
misago/emberapp/app/helpers/gettext.js

@@ -1,62 +0,0 @@
-import Ember from 'ember';
-
-var registerHelper = Ember.HTMLBars.registerHelper;
-var makeBoundHelper = Ember.HTMLBars.makeBoundHelper;
-
-registerHelper('gettext', makeBoundHelper(function(args, kwargs) {
-
-  var msgid = args[0];
-
-  if (Object.getOwnPropertyNames(kwargs).length > 0) {
-    return interpolate(gettext(msgid), kwargs, true);
-  } else {
-    return gettext(msgid);
-  }
-
-}));
-
-registerHelper('ngettext', makeBoundHelper(function(args, kwargs) {
-
-  var singular = args[0];
-  var plural = args[1];
-  var count = args[2];
-
-  kwargs.count = count;
-
-  return interpolate(ngettext(singular, plural, count), kwargs, true);
-}));
-
-registerHelper('gettext_noop', makeBoundHelper(function(args, kwargs) {
-
-  var msgid = args[0];
-
-  if (Object.getOwnPropertyNames(kwargs).length > 0) {
-    return interpolate(gettext_noop(msgid), kwargs, true);
-  } else {
-    return gettext_noop(msgid);
-  }
-}));
-
-registerHelper('pgettext', makeBoundHelper(function(args, kwargs) {
-
-  var context = args[0];
-  var msgid = args[1];
-
-  if (Object.getOwnPropertyNames(kwargs).length > 0) {
-    return interpolate(pgettext(context, msgid), kwargs, true);
-  } else {
-    return pgettext(context, msgid);
-  }
-}));
-
-registerHelper('npgettext', makeBoundHelper(function(args, kwargs) {
-
-  var context = args[0];
-  var singular = args[1];
-  var plural = args[2];
-  var count = args[3];
-
-  kwargs.count = count;
-
-  return interpolate(npgettext(context, singular, plural, count), kwargs, true);
-}));

+ 3 - 2
misago/emberapp/package.json

@@ -20,7 +20,8 @@
   "license": "GNU GPL v2",
   "devDependencies": {
     "broccoli-asset-rev": "^2.0.0",
-    "ember-cli": "0.1.7",
+    "django-ember-gettext": "0.9.7",
+    "ember-cli": "0.1.9",
     "ember-cli-6to5": "0.2.1",
     "ember-cli-content-security-policy": "0.3.0",
     "ember-cli-dependency-checker": "0.0.7",
@@ -30,7 +31,7 @@
     "ember-cli-less": "1.1.0",
     "ember-cli-qunit": "0.1.2",
     "ember-data": "1.0.0-beta.14",
-    "ember-django-adapter": "^0.5.1",
+    "ember-django-adapter": "0.5.2",
     "ember-export-application-global": "^1.0.0",
     "express": "^4.8.5",
     "glob": "^4.0.5"

+ 1 - 0
misago/project_template/requirements.txt

@@ -5,6 +5,7 @@ bleach==1.4.1
 django-debug-toolbar==1.2.1
 django-cors-headers==1.0.0
 django-crispy-forms==1.4.0
+django-hbs-makemessages==0.9.6
 django-htmlmin==0.7
 django-mptt==0.6.1
 django-pipeline==1.4.2