translation MultiWidgets for the new L10n UI (bug 607170)

This commit is contained in:
Jeff Balogh 2010-10-28 12:26:56 -07:00 коммит произвёл Matt Claypotch
Родитель 22053ebd68
Коммит 0b5a5bdd4d
4 изменённых файлов: 63 добавлений и 64 удалений

Просмотреть файл

@ -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), {})