Skip to content
This repository has been archived by the owner on Mar 15, 2018. It is now read-only.

Commit

Permalink
translation MultiWidgets for the new L10n UI (bug 607170)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Balogh authored and potch committed Nov 3, 2010
1 parent 22053eb commit 0b5a5bd
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 64 deletions.
2 changes: 1 addition & 1 deletion apps/addons/models.py
Expand Up @@ -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')
Expand Down
9 changes: 5 additions & 4 deletions apps/translations/fields.py
Expand Up @@ -6,7 +6,7 @@
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):
Expand All @@ -24,6 +24,7 @@ def __init__(self, **kwargs):
# 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)

Expand Down Expand Up @@ -57,7 +58,8 @@ def contribute_to_class(self, cls, name):
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)

Expand Down Expand Up @@ -177,12 +179,11 @@ def translation_from_dict(self, instance, lang, dict_):


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):
Expand Down
12 changes: 1 addition & 11 deletions apps/translations/tests/test_models.py
Expand Up @@ -208,16 +208,6 @@ def test_dict_bad_locale(self):
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')
Expand Down Expand Up @@ -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)

Expand Down
104 changes: 56 additions & 48 deletions apps/translations/widgets.py
@@ -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:
Expand Down Expand Up @@ -43,32 +32,41 @@ def render(self, name, value, attrs=None):
return super(TranslationTextarea, self).render(name, value, attrs)


class TranslationWidget(forms.widgets.Textarea):

# Django expects ForeignKey widgets to have a choices attribute.
choices = None

def render(self, name, value, attrs=None):

attrs = self.build_attrs(attrs)
widget = widget_builder(name, attrs)
id = attrs.pop('id')

lang = translation.get_language()
widgets = {}
widgets[lang] = widget(lang, value='')
class TransMulti(forms.widgets.MultiWidget):
"""
Builds the inputs for a translatable field.
try:
trans_id = int(value)
widgets.update(trans_widgets(trans_id, widget))
except (TypeError, ValueError):
pass
The backend dumps all the available translations into a set of widgets
wrapped in div.trans and javascript handles the rest of the UI.
"""

languages = dict((i.lower(), j) for i, j in settings.LANGUAGES.items())
def __init__(self):
# We set up the widgets in render since every Translation needs a
# different number of widgets.
super(TransMulti, self).__init__(widgets=[])

template = jingo.env.get_template('translations/transbox.html')
return template.render(id=id, name=name, widgets=widgets,
languages=languages)
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)

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
Expand All @@ -85,21 +83,31 @@ def value_from_datadict(self, data, files, name):
rv[locale(key)] = data[key]
return rv

def format_output(self, widgets):
s = super(TransMulti, self).format_output(widgets)
return '<div data-name="%s">%s</div>' % (self.name, s)


class _TransWidget(object):
"""
Widget mixin that adds a Translation locale to the lang attribute and the
input name.
"""

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)

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]

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

def widget_builder(name, attrs):

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
class TransTextarea(TransMulti):
widget = type('_Textarea', (_TransWidget, forms.widgets.Textarea), {})

0 comments on commit 0b5a5bd

Please sign in to comment.