Skip to content

Commit

Permalink
Use flatten dict for store translations, remove pluralize api
Browse files Browse the repository at this point in the history
  • Loading branch information
maxpoletaev committed Oct 28, 2016
1 parent 3802d91 commit e30b04d
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 91 deletions.
2 changes: 1 addition & 1 deletion langpack/contrib/django/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .shortcuts import trans, trans_lazy, plural, localize # noqa
from .shortcuts import trans, trans_lazy, localize # noqa

default_app_config = 'langpack.contrib.django.apps.LangPackConfig'
5 changes: 0 additions & 5 deletions langpack/contrib/django/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,4 @@ def localize(*args, **kwargs):
return app_config.translator.localize(*args, **kwargs)


def plural(count, **variants):
app_config = apps.get_app_config('langpack')
return app_config.translator.pluralize(count, variants)


trans_lazy = lazy(trans, str)
15 changes: 0 additions & 15 deletions langpack/contrib/django/templatetags/langpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,3 @@ def trans(*args, **kwargs):
@register.simple_tag
def localize(*args, **kwargs):
return shortcuts.localize(*args, **kwargs)


@register.simple_tag(name='plural')
def plural_tag(*args, **kwargs):
return shortcuts.plural(*args, **kwargs)


@register.filter(name='plural')
def plural_filter(value, arg):
variants = {}
for part in arg.split(','):
k, v = part.split(':')
variants[k.strip()] = v.strip()

return shortcuts.plural(value, **variants)
37 changes: 10 additions & 27 deletions langpack/translators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,15 @@
import os

from .exceptions import TranslatorWarning, TranslatorError
from .utils import safe_format, deep_merge
from .utils import safe_format, flatten_dict
from .pluralizers import get_plural_form

NOT_RPOVIDED = object()


class TranslationStore(dict):
def __init__(self, initial={}):
super().__init__()
self.update(initial)

def get(self, *paths, default=NOT_RPOVIDED):
path = '.'.join(paths)

try:
return reduce(lambda d, k: d[k], path.split('.'), self)
except KeyError:
return path if default is NOT_RPOVIDED else default


class BaseTranslator:
def __init__(self):
self._translations = defaultdict(TranslationStore)
self._translations = defaultdict(dict)
self._formatters = {}
self._loaders = {}
self._hooks = {}
Expand All @@ -37,11 +23,11 @@ def set_lang(self, lang): # pragma: no cover
def get_lang(self): # pragma: no cover
raise NotImplementedError()

def get_template(self, str_path, values={}, default=NOT_RPOVIDED):
def get_template(self, str_path, values={}, default=None):
lang = self.get_lang()
# if str_path in self._hooks[lang]:
# str_path = self._hooks[lang][str_path](values)
return self._translations[lang].get(str_path, default=default)
return self._translations[lang].get(str_path, default)

def add_loader(self, loader, extensions):
assert type(loader) != type, 'Loader should be an instance, not a class.'
Expand All @@ -58,7 +44,7 @@ def add_formatter(self, formatter, type_names):
self._formatters[type_name] = formatter

def add_translations(self, lang, translations):
deep_merge(self._translations[lang], translations)
self._translations[lang].update(flatten_dict(translations))

def load_directory(self, directory, recursive=False):
if recursive:
Expand Down Expand Up @@ -87,14 +73,15 @@ def load_file(self, file_path):

def translate(self, str_path, **kwargs):
template = self.get_template(str_path, values=kwargs)
count = kwargs.get('count', None)

if not template and count is not None:
plural_form = get_plural_form(self.get_lang(), count)
template = self.get_template(str_path + '.' + plural_form)

if not template:
return str_path

if isinstance(template, dict):
count = kwargs.get('count', 0)
template = self.pluralize(count, template)

return safe_format(template, **kwargs)

def localize(self, value, format, formatter=None):
Expand All @@ -106,10 +93,6 @@ def localize(self, value, format, formatter=None):

return formatter(value, format, self)

def pluralize(self, count, variants):
plural_form = get_plural_form(self.get_lang(), count)
return variants.get(plural_form)


class Translator(BaseTranslator):
def __init__(self, initial_lang='en', *args, **kwargs):
Expand Down
19 changes: 19 additions & 0 deletions langpack/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ def __missing__(self, key):
return '{' + key + '}'


def flatten_dict(data, result=None, prevpath=None):
if result is None:
result = {}

if prevpath is None:
prevpath = []

for key, value in data.items():
nextpath = prevpath.copy()
nextpath.append(key)

if isinstance(value, dict):
flatten_dict(value, result, nextpath)
else:
result['.'.join(nextpath)] = value

return result


def safe_format(source, **kwargs):
return source.format_map(safedict(**kwargs))

Expand Down
4 changes: 1 addition & 3 deletions tests/django/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
from collections import defaultdict
from django.apps import apps

from langpack.translators import TranslationStore


class AppTestCase:
def get_translator(self):
translator = apps.get_app_config('langpack').translator
translator._translations = defaultdict(TranslationStore)
translator._translations = defaultdict(dict)
return translator


Expand Down
16 changes: 0 additions & 16 deletions tests/django/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,3 @@ def setup(self):
def test_trans(self):
result = self.render('{% trans "hello" name="John" %}')
test.assert_equal(result, 'Hello, John!')

def test_plural_tag(self):
result = self.render('{% plural 0 zero="no cats" one="cat" many="cats" %}')
test.assert_equal(result, 'no cats')
result = self.render('{% plural 1 zero="no cats" one="cat" many="cats" %}')
test.assert_equal(result, 'cat')
result = self.render('{% plural 10 zero="no cats" one="cat" many="cats" %}')
test.assert_equal(result, 'cats')

def test_plural_filter(self):
result = self.render('{{ 0|plural:"zero: no cats, one: cat, many: cats" }}')
test.assert_equal(result, 'no cats')
result = self.render('{{ 1|plural:"zero: no cats, one: cat, many: cats" }}')
test.assert_equal(result, 'cat')
result = self.render('{{ 10|plural:"zero: no cats, one: cat, many: cats" }}')
test.assert_equal(result, 'cats')
7 changes: 4 additions & 3 deletions tests/test_formatters.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from langpack.formatters import format_datetime
from langpack.translators import TranslationStore, NOT_RPOVIDED
from langpack.translators import NOT_RPOVIDED
from langpack.utils import flatten_dict
from datetime import datetime


class TranslatorMock:
def __init__(self, initial={}):
self._translations = TranslationStore(initial)
self._translations = flatten_dict(initial)

def get_template(self, str_path, default=NOT_RPOVIDED):
return self._translations.get(str_path, default=default)
return self._translations.get(str_path, default)


def test_format_datetime():
Expand Down
28 changes: 8 additions & 20 deletions tests/test_translators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from langpack.translators import Translator, TranslationStore
from langpack.translators import Translator
from langpack.utils import flatten_dict
from nose import tools as test
from datetime import datetime
from unittest import mock
Expand All @@ -18,14 +19,14 @@ def test_get_lang(self):
test.assert_equal(self.translator.get_lang(), 'en')

def test_get_template(self):
self.translator._translations['en'] = TranslationStore({
self.translator._translations['en'] = flatten_dict({
'hello': 'Hello, {name}!',
})
translation = self.translator.get_template('hello')
test.assert_equal(translation, 'Hello, {name}!')

def test_translate(self):
self.translator._translations['en'] = TranslationStore({
self.translator._translations['en'] = flatten_dict({
'hello': 'Hello, {name}!',
})
result = self.translator.translate('hello', name='John')
Expand All @@ -38,7 +39,7 @@ def test_translate(self):
test.assert_equal(result, 'unknown_str_id')

def test_translate_plural(self):
self.translator._translations['en'] = TranslationStore({
self.translator._translations['en'] = flatten_dict({
'i_have_apples': {
'zero': 'I have no apples',
'one': 'I have one apple',
Expand All @@ -64,8 +65,8 @@ def test_add_translations(self):
})

translations = self.translator._translations['en']
test.assert_equal(translations['blog']['comments'], 'Comments')
test.assert_equal(translations['blog']['post'], 'Post')
test.assert_equal(translations['blog.comments'], 'Comments')
test.assert_equal(translations['blog.post'], 'Post')

def test_load_directory(self):
loader = mock.Mock()
Expand All @@ -78,7 +79,7 @@ def test_load_directory(self):
test.assert_true(loader.load_file.called)

def test_localize(self):
self.translator._translations['en'] = TranslationStore({
self.translator._translations['en'] = flatten_dict({
'datetime': {
'formats': {
'short': '%d.%M.%Y',
Expand All @@ -95,16 +96,3 @@ def format_datetime(value, format, translator):

assert self.translator.localize(now, 'short') == now.strftime('%d.%M.%Y')
assert self.translator.localize(now, '%d.%M.%Y') == now.strftime('%d.%M.%Y')


class TestTranslationStore:
def setup(self):
self.store = TranslationStore()

def test_get(self):
self.store['en'] = {
'hello': 'Hello',
'inner': {'hello': 'Inner hello'}
}
assert self.store.get('en', 'hello') == 'Hello'
assert self.store.get('en', 'inner.hello') == 'Inner hello'
24 changes: 23 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
from langpack.utils import safe_format
from langpack.utils import safe_format, flatten_dict
from nose import tools as test


def test_safe_format():
test.assert_equal(safe_format('Hello {name}!', name='John'), 'Hello John!')
test.assert_equal(safe_format('Hello {name}!'), 'Hello {name}!')
test.assert_equal(safe_format('Hello', name='John'), 'Hello')


def test_flatten_dict():
data_dict = {
'a': {
'b': {
'c': 'value',
},
},
'd': {
'e': 'value',
},
'f': 'value',
}

expected = {
'a.b.c': 'value',
'd.e': 'value',
'f': 'value',
}

assert flatten_dict(data_dict) == expected

0 comments on commit e30b04d

Please sign in to comment.