getting lib module in sync with webifyme app
This commit is contained in:
Родитель
c3165532a9
Коммит
ff6381d58c
|
@ -10,3 +10,9 @@
|
|||
[submodule "src/tower"]
|
||||
path = src/tower
|
||||
url = git://github.com/clouserw/tower.git
|
||||
[submodule "src/django-celery"]
|
||||
path = src/django-celery
|
||||
url = git://github.com/ask/django-celery.git
|
||||
[submodule "src/celery"]
|
||||
path = src/celery
|
||||
url = git://github.com/ask/celery.git
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# based on django.contrib.admin.__init__.py
|
||||
|
||||
from django.utils.importlib import import_module
|
||||
LOADING = False
|
||||
|
||||
from django.conf import settings
|
||||
REGISTRATION_MODULE_NAME = getattr(
|
||||
settings, 'DBGETTEXT_REGISTRATION_MODULE_NAME', 'dbgettext_registration')
|
||||
|
||||
def autodiscover():
|
||||
"""
|
||||
Auto-discover INSTALLED_APPS dbgettext_registration.py modules and fail
|
||||
silently when not present. This forces an import on them to register any
|
||||
dbggettext bits they may want.
|
||||
"""
|
||||
global LOADING
|
||||
if LOADING:
|
||||
return
|
||||
LOADING = True
|
||||
|
||||
import imp
|
||||
for app in settings.INSTALLED_APPS:
|
||||
try:
|
||||
app_path = import_module(app).__path__
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
try:
|
||||
imp.find_module(REGISTRATION_MODULE_NAME, app_path)
|
||||
except ImportError:
|
||||
continue
|
||||
|
||||
import_module("%s.%s" % (app, REGISTRATION_MODULE_NAME))
|
||||
|
||||
# import project-level options
|
||||
if hasattr(settings, 'DBGETTEXT_PROJECT_OPTIONS'):
|
||||
import_module(settings.DBGETTEXT_PROJECT_OPTIONS)
|
||||
|
||||
LOADING = False
|
||||
|
||||
# go:
|
||||
autodiscover()
|
|
@ -1,94 +0,0 @@
|
|||
from dbgettext.parser import Token, SENTENCE_RE
|
||||
from django.conf import settings
|
||||
|
||||
class Tag(Token):
|
||||
""" An opening/closing/empty HTML tag """
|
||||
|
||||
gettext_inline_tags = getattr(settings, 'DBGETTEXT_INLINE_HTML_TAGS',
|
||||
('b','i','u','em','strong',))
|
||||
|
||||
def __init__(self, type, raw, name, attributes=None):
|
||||
super(Tag, self).__init__(type, raw)
|
||||
self.name = name
|
||||
self.attributes = attributes
|
||||
|
||||
def is_translatable(self):
|
||||
if self.name.lower() in Tag.gettext_inline_tags:
|
||||
return Token.MAYBE_TRANSLATE
|
||||
else:
|
||||
return Token.NEVER_TRANSLATE
|
||||
|
||||
|
||||
def lexicon(options):
|
||||
def ignore(scanner, token):
|
||||
return Token('ignore', token)
|
||||
|
||||
def open_tag(scanner, token):
|
||||
return Tag('open', token, scanner.match.groups()[0])
|
||||
|
||||
def close_tag(scanner, token):
|
||||
return Tag('close', token, scanner.match.groups()[0])
|
||||
|
||||
def empty_tag(scanner, token):
|
||||
return Tag('empty', token, scanner.match.groups()[0])
|
||||
|
||||
def open_tag_with_attributes(scanner, token):
|
||||
return Tag(*(('open', token,) + scanner.match.groups()[:2]))
|
||||
|
||||
def empty_tag_with_attributes(scanner, token):
|
||||
return Tag(*(('empty', token,) + scanner.match.groups()[:2]))
|
||||
|
||||
def text(scanner, token):
|
||||
if getattr(settings, 'DBGETTEXT_SPLIT_SENTENCES', True):
|
||||
text = token
|
||||
tokens = []
|
||||
while True:
|
||||
m = SENTENCE_RE.match(text)
|
||||
if m:
|
||||
tokens.append(Token('text',m.groups()[0]))
|
||||
tokens.append(Token('whitespace',m.groups()[1]))
|
||||
text = m.groups()[2]
|
||||
if text:
|
||||
tokens.append(Token('sentence_separator',''))
|
||||
else:
|
||||
break
|
||||
if text:
|
||||
tokens.append(Token('text', text))
|
||||
return tokens
|
||||
else:
|
||||
return Token('text', token)
|
||||
|
||||
def whitespace(scanner, token):
|
||||
return Token('whitespace', token)
|
||||
|
||||
ignored = [
|
||||
(r'<!--.*?-->', ignore),
|
||||
(r'<script.*?/script>', ignore),
|
||||
(r'\r', ignore), # forbidden in gettext, must split on these
|
||||
]
|
||||
|
||||
custom = getattr(options, 'custom_lexicon_rules', [])
|
||||
|
||||
tags = [
|
||||
(r'<\s*/\s*([^>]*?)\s*>', close_tag),
|
||||
(r'<\s*([^>]*?)\s*/\s*>', empty_tag),
|
||||
(r'<\s*([a-zA-Z]+)\s+([^\s>][^>]*?)\s*>',
|
||||
open_tag_with_attributes),
|
||||
(r'<\s*([a-zA-Z]+)\s+([^\s>][^>]*?)\s*/\s*>',
|
||||
empty_tag_with_attributes),
|
||||
(r'<\s*([^>]*?)\s*>', open_tag),
|
||||
]
|
||||
|
||||
whitespace = [
|
||||
(r'\s+', whitespace),
|
||||
(r' ', whitespace),
|
||||
]
|
||||
|
||||
text = [
|
||||
(r'[^\r<>]*[^\s<>]', text),
|
||||
]
|
||||
|
||||
lexicon = ignored + custom + tags + whitespace + text
|
||||
|
||||
return lexicon
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
from django.conf import settings
|
||||
from django.core.management.base import NoArgsCommand, CommandError
|
||||
from shutil import rmtree
|
||||
import os
|
||||
from dbgettext.registry import registry
|
||||
from dbgettext.parser import parsed_gettext
|
||||
|
||||
def recursive_getattr(obj, attr, default=None, separator='__'):
|
||||
""" Allows getattr(obj, 'related_class__property__subproperty__etc') """
|
||||
try:
|
||||
if attr.find(separator) > 0:
|
||||
bits = attr.split(separator)
|
||||
return recursive_getattr(getattr(obj, bits[0]),
|
||||
separator.join(bits[1:]), default)
|
||||
else:
|
||||
return getattr(obj, attr)
|
||||
except AttributeError:
|
||||
return default
|
||||
|
||||
|
||||
def get_field_or_callable_content(obj, attr_name):
|
||||
""" Returns value of obj.attr_name() or obj.attr_name """
|
||||
try:
|
||||
attr = getattr(obj, attr_name)
|
||||
except AttributeError:
|
||||
raise
|
||||
|
||||
if callable(attr):
|
||||
return attr()
|
||||
else:
|
||||
return attr
|
||||
|
||||
|
||||
def build_queryset(model, queryset=None, trail=[]):
|
||||
""" Recursively creates queryset for model using options """
|
||||
|
||||
try:
|
||||
options = registry._registry[model]
|
||||
except:
|
||||
raise Exception, "%s is not registered with dbgettext" % model
|
||||
|
||||
if queryset is None:
|
||||
queryset = model.objects.all()
|
||||
|
||||
recursive_criteria = {}
|
||||
for c in options.translate_if:
|
||||
recursive_criteria['__'.join(trail+[c])] = options.translate_if[c]
|
||||
queryset = queryset.filter(**recursive_criteria)
|
||||
|
||||
if options.parent:
|
||||
parent_model = \
|
||||
getattr(model,options.parent).field.related.parent_model
|
||||
queryset = build_queryset(parent_model, queryset,
|
||||
trail+[options.parent])
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
def build_path(obj):
|
||||
""" Recursively constructs path for object using options """
|
||||
|
||||
model = type(obj)
|
||||
options = registry._registry[model]
|
||||
if options.parent:
|
||||
path = build_path(getattr(obj, options.parent))
|
||||
else:
|
||||
path = os.path.join(model._meta.app_label,model._meta.module_name)
|
||||
return os.path.join(path, options.get_path_identifier(obj))
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
""" dbgettext_export management command """
|
||||
|
||||
# overridable path settings (default: project_root/locale/dbgettext/...)
|
||||
path = getattr(settings, 'DBGETTEXT_PATH', 'locale/')
|
||||
root = getattr(settings, 'DBGETTEXT_ROOT', 'dbgettext')
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
if not os.path.exists(self.path):
|
||||
raise CommandError('This command must be run from the project '
|
||||
'root directory, and the %s '
|
||||
'(settings.DBGETTEXT_PATH) directory must '
|
||||
'exist.' % self.path)
|
||||
self.gettext()
|
||||
|
||||
help = ('Extract translatable strings from models in database '
|
||||
'and store in static files for makemessages to pick up.')
|
||||
|
||||
def gettext(self):
|
||||
""" Export translatable strings from models into static files """
|
||||
|
||||
def write(file, string):
|
||||
string = string.replace('"','\\"') # prevent """"
|
||||
string = string.encode('utf8')
|
||||
file.write('# -*- coding: utf-8 -*-\ngettext("""%s""")\n' % string)
|
||||
|
||||
root = os.path.join(self.path, self.root)
|
||||
|
||||
# remove any old files
|
||||
if os.path.exists(root):
|
||||
rmtree(root)
|
||||
|
||||
# for each registered model:
|
||||
for model, options in registry._registry.items():
|
||||
for obj in build_queryset(model):
|
||||
path = os.path.join(root, build_path(obj))
|
||||
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
for attr_name in options.attributes:
|
||||
attr = get_field_or_callable_content(obj, attr_name)
|
||||
if attr:
|
||||
f = open(os.path.join(path, '%s.py' % attr_name), 'w')
|
||||
write(f, attr)
|
||||
f.close()
|
||||
|
||||
for attr_name in options.parsed_attributes:
|
||||
f = open(os.path.join(path, '%s.py' % attr_name), 'w')
|
||||
for s in parsed_gettext(obj, attr_name, export=True):
|
||||
write(f, s)
|
||||
f.close()
|
|
@ -1,139 +0,0 @@
|
|||
from django.conf import settings
|
||||
from registry import registry
|
||||
import re
|
||||
|
||||
SENTENCE_RE = getattr(settings, 'DBGETTEXT_SENTENCE_RE', re.compile(r'^(.*?\S[\!\?\.])(\s+)(\S+.*)$', re.DOTALL))
|
||||
|
||||
class Token(object):
|
||||
""" A categorised chunk of HTML content """
|
||||
|
||||
NEVER_TRANSLATE = 0 # e.g. comments, javascript, etc.
|
||||
MAYBE_TRANSLATE = 1 # e.g. whitespace -- surrounded by text vs on own
|
||||
ALWAYS_TRANSLATE = 2 # e.g. text
|
||||
|
||||
def __init__(self, type, raw):
|
||||
self.type = type
|
||||
self.raw = raw
|
||||
|
||||
def is_translatable(self):
|
||||
if self.type == 'text':
|
||||
return Token.ALWAYS_TRANSLATE
|
||||
elif self.type == 'whitespace':
|
||||
if self.raw.find('\r') >= 0: # carriage-returns forbidden
|
||||
return Token.NEVER_TRANSLATE
|
||||
else:
|
||||
return Token.MAYBE_TRANSLATE
|
||||
else:
|
||||
return Token.NEVER_TRANSLATE
|
||||
|
||||
def get_raw(self):
|
||||
""" Hook to allow subclasses to perform inner translation """
|
||||
return self.raw
|
||||
|
||||
def get_gettext(self):
|
||||
""" Return list of inner translatable strings """
|
||||
return []
|
||||
|
||||
|
||||
def flatten_token_list(token_list):
|
||||
""" Recursively flattens list of tokens.
|
||||
|
||||
Allows scanner callbacks to return lists of tokens.
|
||||
"""
|
||||
|
||||
flat_list = []
|
||||
for token in token_list:
|
||||
if isinstance(token, list):
|
||||
flat_list += flatten_token_list(token)
|
||||
else:
|
||||
flat_list.append(token)
|
||||
return flat_list
|
||||
|
||||
|
||||
def parsed_gettext(obj, attribute, export=False):
|
||||
""" Extracts translatable strings from parsable content
|
||||
|
||||
Returns original content with ugettext applied to translatable parts.
|
||||
|
||||
If export is True, returns a list of translatable strings only.
|
||||
|
||||
"""
|
||||
|
||||
options = registry._registry[type(obj)]
|
||||
content = getattr(obj, attribute)
|
||||
try:
|
||||
lexicon = options.parsed_attributes[attribute]
|
||||
except:
|
||||
raise Exception, "Invalid lexicon configuration in parsed_attributes"
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
# lazy / string_concat don't seem to work how I want...
|
||||
|
||||
scanner = re.Scanner(lexicon(options), re.DOTALL)
|
||||
tokens, remainder = scanner.scan(content)
|
||||
tokens = flatten_token_list(tokens)
|
||||
|
||||
gettext = []
|
||||
output = []
|
||||
current_string = []
|
||||
|
||||
def token_list_should_be_translated(token_list):
|
||||
""" True if any token is ALWAYS_TRANSLATE """
|
||||
for t in token_list:
|
||||
if t.is_translatable() == Token.ALWAYS_TRANSLATE:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gettext_from_token_list(token_list):
|
||||
""" Process token list into format string, parameters and remainder """
|
||||
format, params, remainder, inner_gettext = '', {}, '', []
|
||||
# remove any trailing whitespace
|
||||
while token_list[-1].type == 'whitespace':
|
||||
remainder = token_list.pop().raw + remainder
|
||||
for t in token_list:
|
||||
if hasattr(t, 'get_key'):
|
||||
format += '%%(%s)s' % t.get_key()
|
||||
params[t.get_key()] = t.get_raw()
|
||||
else:
|
||||
format += t.get_raw().replace('%', '%%')
|
||||
inner_gettext += t.get_gettext()
|
||||
return format, params, remainder, inner_gettext
|
||||
|
||||
for t in tokens + [Token('empty', '',)]:
|
||||
if current_string:
|
||||
# in the middle of building a translatable string
|
||||
if t.is_translatable():
|
||||
current_string.append(t)
|
||||
else:
|
||||
# end of translatable token sequence, check for text content
|
||||
if token_list_should_be_translated(current_string):
|
||||
format, params, trailing_whitespace, inner_gettext = \
|
||||
gettext_from_token_list(current_string)
|
||||
gettext.append(format)
|
||||
gettext += inner_gettext
|
||||
try:
|
||||
output.append(_(format) % params)
|
||||
except KeyError:
|
||||
# translator edited placeholder names? Fallback:
|
||||
output.append(format % params)
|
||||
output.append(trailing_whitespace)
|
||||
else:
|
||||
# should not be translated, raw output only
|
||||
output.append(''.join([x.raw for x in current_string]))
|
||||
# empty for next time:
|
||||
current_string = []
|
||||
# don't forget current token also:
|
||||
output.append(t.raw)
|
||||
else:
|
||||
# should we start a new translatable string?
|
||||
if t.is_translatable() and t.type != 'whitespace':
|
||||
current_string.append(t)
|
||||
else:
|
||||
output.append(t.raw)
|
||||
|
||||
if export:
|
||||
if remainder:
|
||||
raise Exception, 'scanner got stuck on: "%s"(...)' % remainder[:10]
|
||||
return gettext
|
||||
else:
|
||||
return ''.join(output)
|
|
@ -1,88 +0,0 @@
|
|||
from django.db.models.base import ModelBase
|
||||
|
||||
class Options(object):
|
||||
"""
|
||||
Encapsulates dbgettext options for a given model
|
||||
|
||||
- attributes:
|
||||
tuple of names of fields/callables to be translated
|
||||
- parsed_attributes:
|
||||
dictionary of names of fields/callables with HTML content which should
|
||||
have translatable content extracted (should not be listed in
|
||||
attributes), with their associated lexicons
|
||||
- translate_if:
|
||||
dictionary used to filter() queryset
|
||||
- get_path_identifier:
|
||||
function returning string used to identify object in path to exported
|
||||
content (given an object)
|
||||
- parent:
|
||||
name of foreign key to parent model, if registered. Affects:
|
||||
- path (path_identifier appended onto parent path)
|
||||
- queryset (object only translated if parent is)
|
||||
- custom_lexicon_rules
|
||||
list of extra custom rules ((regexp, function) tuples) to be applied
|
||||
when parsing HTML -- see html.py
|
||||
|
||||
"""
|
||||
|
||||
attributes = ()
|
||||
parsed_attributes = {}
|
||||
translate_if = {}
|
||||
parent = None
|
||||
|
||||
def get_path_identifier(self, obj):
|
||||
return '%s_%s' % (obj._meta.object_name, str(obj.pk))
|
||||
|
||||
|
||||
# Registration code based on django.contrib.admin.sites
|
||||
|
||||
class AlreadyRegistered(Exception):
|
||||
pass
|
||||
|
||||
class NotRegistered(Exception):
|
||||
pass
|
||||
|
||||
class Registry(object):
|
||||
"""
|
||||
A Registry object is used to register() models for dbgettext exporting,
|
||||
together with their associated options.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._registry = {} # model_class class -> Options subclass
|
||||
|
||||
def register(self, model_or_iterable, options_class, **options):
|
||||
"""
|
||||
Registers the given model(s) with the given admin class.
|
||||
|
||||
The model(s) should be Model classes, not instances.
|
||||
|
||||
If a model is already registered, this will raise AlreadyRegistered.
|
||||
"""
|
||||
|
||||
if isinstance(model_or_iterable, ModelBase):
|
||||
model_or_iterable = [model_or_iterable]
|
||||
for model in model_or_iterable:
|
||||
if model in self._registry:
|
||||
raise AlreadyRegistered(
|
||||
'The model %s is already registered' % model.__name__)
|
||||
|
||||
self._registry[model] = options_class() # instantiated
|
||||
|
||||
def unregister(self, model_or_iterable):
|
||||
"""
|
||||
Unregisters the given model(s).
|
||||
|
||||
If a model isn't already registered, this will raise NotRegistered.
|
||||
"""
|
||||
if isinstance(model_or_iterable, ModelBase):
|
||||
model_or_iterable = [model_or_iterable]
|
||||
for model in model_or_iterable:
|
||||
if model not in self._registry:
|
||||
raise NotRegistered(
|
||||
'The model %s is not registered' % model.__name__)
|
||||
del self._registry[model]
|
||||
|
||||
|
||||
# Global Registry object
|
||||
registry = Registry()
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
from dbgettext.parser import parsed_gettext as _parsed_gettext
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def parsed_gettext(obj, attribute):
|
||||
return _parsed_gettext(obj, attribute)
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3de4753768456ef59835f2e9adc9682d0dee6a35
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 747b493574b25c364fad6acb1dab8684901a60e4
|
Загрузка…
Ссылка в новой задаче