translation MultiWidgets for the new L10n UI (bug 607170)
This commit is contained in:
Родитель
22053ebd68
Коммит
0b5a5bdd4d
|
@ -116,7 +116,7 @@ class Addon(amo.models.ModelBase):
|
|||
homepage = TranslatedField()
|
||||
support_email = TranslatedField(db_column='supportemail')
|
||||
support_url = TranslatedField(db_column='supporturl')
|
||||
description = PurifiedField()
|
||||
description = PurifiedField(short=False)
|
||||
|
||||
summary = LinkifiedField()
|
||||
developer_comments = PurifiedField(db_column='developercomments')
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.utils import translation as translation_utils
|
|||
from django.utils.translation.trans_real import to_language
|
||||
|
||||
from .models import Translation, PurifiedTranslation, LinkifiedTranslation
|
||||
from .widgets import TranslationWidget
|
||||
from .widgets import TransInput, TransTextarea
|
||||
|
||||
|
||||
class TranslatedField(models.ForeignKey):
|
||||
|
@ -24,6 +24,7 @@ class TranslatedField(models.ForeignKey):
|
|||
# Django wants to default to translations.autoid, but we need id.
|
||||
options = dict(null=True, to_field='id', unique=True, blank=True)
|
||||
kwargs.update(options)
|
||||
self.short = kwargs.pop('short', True)
|
||||
self.require_locale = kwargs.pop('require_locale', True)
|
||||
super(TranslatedField, self).__init__(self.to, **kwargs)
|
||||
|
||||
|
@ -57,7 +58,8 @@ class TranslatedField(models.ForeignKey):
|
|||
setattr(cls, self.name, TranslationDescriptor(self))
|
||||
|
||||
def formfield(self, **kw):
|
||||
defaults = {'form_class': TranslationFormField}
|
||||
widget = TransInput if self.short else TransTextarea
|
||||
defaults = {'form_class': TranslationFormField, 'widget': widget}
|
||||
defaults.update(kw)
|
||||
return super(TranslatedField, self).formfield(**defaults)
|
||||
|
||||
|
@ -177,12 +179,11 @@ class TranslationDescriptor(related.ReverseSingleRelatedObjectDescriptor):
|
|||
|
||||
|
||||
class TranslationFormField(forms.Field):
|
||||
widget = TranslationWidget
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
for k in ('queryset', 'to_field_name'):
|
||||
if k in kwargs:
|
||||
del kwargs[k]
|
||||
self.widget = kwargs.pop('widget', TransInput)
|
||||
super(TranslationFormField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
|
|
|
@ -208,16 +208,6 @@ class TranslationTestCase(ExtraAppTestCase):
|
|||
eq_(sorted(ts.values_list('locale', flat=True)),
|
||||
['de', 'en-US', 'es-ES'])
|
||||
|
||||
def test_widget(self):
|
||||
strings = {'de': None, 'fr': 'oui'}
|
||||
o = TranslatedModel.objects.get(id=1)
|
||||
o.name = strings
|
||||
o.save()
|
||||
|
||||
# Shouldn't see de since that's NULL now.
|
||||
ws = widgets.trans_widgets(o.name_id, lambda *args: None)
|
||||
eq_(sorted(dict(ws).keys()), ['en-us', 'fr'])
|
||||
|
||||
def test_sorting(self):
|
||||
"""Test translation comparisons in Python code."""
|
||||
b = Translation.new('bbbb', 'de')
|
||||
|
@ -363,7 +353,7 @@ def test_translation_unicode():
|
|||
|
||||
def test_widget_value_from_datadict():
|
||||
data = {'f_en-US': 'woo', 'f_de': 'herr', 'f_fr_delete': ''}
|
||||
actual = widgets.TranslationWidget().value_from_datadict(data, [], 'f')
|
||||
actual = widgets.TransMulti().value_from_datadict(data, [], 'f')
|
||||
expected = {'en-US': 'woo', 'de': 'herr', 'fr': None}
|
||||
eq_(actual, expected)
|
||||
|
||||
|
|
|
@ -1,21 +1,10 @@
|
|||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.forms.util import flatatt
|
||||
from django.utils import translation
|
||||
from django.utils.translation.trans_real import to_language
|
||||
|
||||
import jinja2
|
||||
|
||||
import jingo
|
||||
|
||||
from .models import Translation
|
||||
|
||||
|
||||
attrs = 'name="{name}_{locale}" data-locale="{locale}" {attrs}'
|
||||
input = u'<input %s value="{value}">' % attrs
|
||||
textarea = u'<textarea %s>{value}</textarea>' % attrs
|
||||
|
||||
|
||||
def get_string(x):
|
||||
locale = translation.get_language()
|
||||
try:
|
||||
|
@ -43,32 +32,41 @@ class TranslationTextarea(forms.widgets.Textarea):
|
|||
return super(TranslationTextarea, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class TranslationWidget(forms.widgets.Textarea):
|
||||
class TransMulti(forms.widgets.MultiWidget):
|
||||
"""
|
||||
Builds the inputs for a translatable field.
|
||||
|
||||
# Django expects ForeignKey widgets to have a choices attribute.
|
||||
choices = None
|
||||
The backend dumps all the available translations into a set of widgets
|
||||
wrapped in div.trans and javascript handles the rest of the UI.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# We set up the widgets in render since every Translation needs a
|
||||
# different number of widgets.
|
||||
super(TransMulti, self).__init__(widgets=[])
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
self.name = name
|
||||
value = self.decompress(value)
|
||||
if value:
|
||||
self.widgets = [self.widget() for _ in value]
|
||||
else:
|
||||
# Give an empty widget in the current locale.
|
||||
self.widgets = [self.widget()]
|
||||
value = [Translation(locale=translation.get_language())]
|
||||
return super(TransMulti, self).render(name, value, attrs)
|
||||
|
||||
attrs = self.build_attrs(attrs)
|
||||
widget = widget_builder(name, attrs)
|
||||
id = attrs.pop('id')
|
||||
|
||||
lang = translation.get_language()
|
||||
widgets = {}
|
||||
widgets[lang] = widget(lang, value='')
|
||||
|
||||
try:
|
||||
trans_id = int(value)
|
||||
widgets.update(trans_widgets(trans_id, widget))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
languages = dict((i.lower(), j) for i, j in settings.LANGUAGES.items())
|
||||
|
||||
template = jingo.env.get_template('translations/transbox.html')
|
||||
return template.render(id=id, name=name, widgets=widgets,
|
||||
languages=languages)
|
||||
def decompress(self, value):
|
||||
if not value:
|
||||
return []
|
||||
elif isinstance(value, long):
|
||||
# We got a foreign key to the translation table.
|
||||
qs = Translation.objects.filter(id=value)
|
||||
return list(qs.filter(localized_string__isnull=False))
|
||||
elif isinstance(value, dict):
|
||||
# We're getting a datadict, there was a validation error.
|
||||
return [Translation(locale=k, localized_string=v)
|
||||
for k, v in value.items()]
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
# All the translations for this field are called {name}_{locale}, so
|
||||
|
@ -85,21 +83,31 @@ class TranslationWidget(forms.widgets.Textarea):
|
|||
rv[locale(key)] = data[key]
|
||||
return rv
|
||||
|
||||
|
||||
def trans_widgets(trans_id, widget):
|
||||
translations = (Translation.objects.filter(id=trans_id)
|
||||
.filter(localized_string__isnull=False)
|
||||
.values_list('locale', 'localized_string'))
|
||||
return [(to_language(locale), widget(locale, val))
|
||||
for locale, val in translations if val is not None]
|
||||
def format_output(self, widgets):
|
||||
s = super(TransMulti, self).format_output(widgets)
|
||||
return '<div data-name="%s">%s</div>' % (self.name, s)
|
||||
|
||||
|
||||
def widget_builder(name, attrs):
|
||||
class _TransWidget(object):
|
||||
"""
|
||||
Widget mixin that adds a Translation locale to the lang attribute and the
|
||||
input name.
|
||||
"""
|
||||
|
||||
def widget(locale, value):
|
||||
locale = to_language(locale)
|
||||
value = jinja2.escape(value)
|
||||
attrs_ = dict(id='trans_%s_%s' % (name, locale), **attrs)
|
||||
return textarea.format(name=name, locale=locale,
|
||||
attrs=flatatt(attrs_), value=value)
|
||||
return widget
|
||||
def render(self, name, value, attrs=None):
|
||||
attrs = attrs or {}
|
||||
lang = to_language(value.locale)
|
||||
attrs.update(lang=lang)
|
||||
# Use rsplit to drop django's name_idx numbering. (name_0 => name)
|
||||
name = '%s_%s' % (name.rsplit('_', 1)[0], lang)
|
||||
return super(_TransWidget, self).render(name, value, attrs)
|
||||
|
||||
|
||||
# TransInput and TransTextarea are MultiWidgets that know how to set up our
|
||||
# special translation attributes.
|
||||
class TransInput(TransMulti):
|
||||
widget = type('_TextInput', (_TransWidget, forms.widgets.TextInput), {})
|
||||
|
||||
|
||||
class TransTextarea(TransMulti):
|
||||
widget = type('_Textarea', (_TransWidget, forms.widgets.Textarea), {})
|
||||
|
|
Загрузка…
Ссылка в новой задаче