Expanded localization coverage

This commit is contained in:
Matt Claypotch 2010-11-04 13:22:03 -07:00
Родитель 041b1819be
Коммит 1a200f6146
14 изменённых файлов: 167 добавлений и 40 удалений

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

@ -11,17 +11,19 @@ from addons.models import Addon
from amo.utils import slug_validator
from tags.models import Tag
from tower import ugettext as _, ungettext as ngettext
from translations.widgets import (TranslationTextInput, TranslationTextarea,
TransTextarea)
from translations.fields import TranslatedField, PurifiedField, LinkifiedField
from translations.widgets import (TranslationTextInput, TranslationTextarea)
from translations.fields import (TransTextarea, TransInput, TransField)
class AddonFormBasic(happyforms.ModelForm):
name = forms.CharField(widget=TranslationTextInput, max_length=50)
slug = forms.CharField(max_length=30)
summary = forms.CharField(widget=TranslationTextarea, max_length=250)
summary = forms.CharField(widget=TransTextarea, max_length=250)
tags = forms.CharField(required=False)
class Meta:
model = Addon
fields = ('name', 'summary')
def __init__(self, *args, **kw):
self.request = kw.pop('request')
super(AddonFormBasic, self).__init__(*args, **kw)
@ -66,6 +68,7 @@ class AddonFormBasic(happyforms.ModelForm):
min_len).format(min_len))
return target
def clean_slug(self):
target = self.cleaned_data['slug']
slug_validator(target, lower=False)
@ -78,7 +81,6 @@ class AddonFormBasic(happyforms.ModelForm):
class AddonFormDetails(happyforms.ModelForm):
default_locale = forms.TypedChoiceField(choices=Addon.LOCALES)
homepage = forms.URLField(widget=TranslationTextInput)
def __init__(self, *args, **kw):
self.request = kw.pop('request')
@ -90,8 +92,8 @@ class AddonFormDetails(happyforms.ModelForm):
class AddonFormSupport(happyforms.ModelForm):
support_url = forms.URLField(widget=TranslationTextInput)
support_email = forms.EmailField(widget=TranslationTextInput)
support_url = forms.URLField(widget=TransInput)
support_email = forms.EmailField(widget=TransInput)
def __init__(self, *args, **kw):
self.request = kw.pop('request')
@ -121,7 +123,7 @@ class AddonFormSupport(happyforms.ModelForm):
class AddonFormTechnical(forms.ModelForm):
developer_comments = forms.CharField(widget=TranslationTextarea,
developer_comments = forms.CharField(widget=TransTextarea,
required=False)
def __init__(self, *args, **kw):
@ -136,12 +138,13 @@ class AddonFormTechnical(forms.ModelForm):
class AddonForm(happyforms.ModelForm):
name = forms.CharField(widget=TranslationTextInput,)
homepage = forms.CharField(widget=TranslationTextInput,)
eula = forms.CharField(widget=TranslationTextInput,)
description = forms.CharField(widget=TranslationTextInput,)
developer_comments = forms.CharField(widget=TranslationTextInput,)
privacy_policy = forms.CharField(widget=TranslationTextInput,)
the_future = forms.CharField(widget=TranslationTextInput,)
the_reason = forms.CharField(widget=TranslationTextInput,)
support_url = forms.CharField(widget=TranslationTextInput,)
summary = forms.CharField(widget=TranslationTextInput,)
support_email = forms.CharField(widget=TranslationTextInput,)
class Meta:

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

@ -11,6 +11,7 @@
<h2>{{ title }}</h2>
</header>
<section id="edit-addon" class="primary devhub-form" role="main">
{{ l10n_menu() }}
<form method="post" class="item" action="">
{{ csrf() }}
<div class="item_wrapper">

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

@ -1,4 +1,4 @@
{% from "devhub/includes/macros.html" import tip, empty_unless %}
{% from "devhub/includes/macros.html" import tip, empty_unless, trans_readonly %}
<form method="post" action="{{ url('devhub.addons.section', addon.id, 'basic', 'edit') }}">
<h3>
@ -15,13 +15,13 @@
<caption>{{ _('Basic Information for {0}')|f(addon.name) }}</caption>
<tbody>
<tr>
<th>{{ _("Name") }}</th>
<th><label for="name">{{ _("Name") }}</label></th>
<td>
{% if editable %}
{{ form.name|safe }}
{{ form.name.errors|safe }}
{% else %}
{{ addon.name }}
{{ trans_readonly('name', addon.name) }}
{% endif %}
</td>
</tr>
@ -44,7 +44,8 @@
</tr>
<tr>
<th>
{{ tip(_("Summary"),
<label for="summary">{{ _("Summary") }}</label>
{{ tip(None,
_("A short explanation of your add-on's basic functionality
that is displayed in search and browse listings, as well as
at the top of your add-on's details page.")) }}

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

@ -49,7 +49,8 @@
</tr>
<tr>
<th>
{{ tip(_("Homepage"),
<label for="homepage">{{ _("Homepage") }}</label>
{{ tip(None,
_("If your add-on has another homepage, enter its address here.
If your website is localized into other languages, multiple
translations of this field can be added.")) }}

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

@ -16,7 +16,8 @@
<tbody>
<tr>
<th>
{{ tip(_("Email"),
<label for="support_email">{{ _("Email") }}</label>
{{ tip(None,
_("If you wish to display an e-mail address for support inquiries,
enter it here. If you have different addresses for each language,
multiple translations of this field can be added.")) }}
@ -34,7 +35,8 @@
</tr>
<tr>
<th>
{{ tip(_("Website"),
<label for="support_url">{{ _("Website") }}</label>
{{ tip(None,
_("If your add-on has a support website or forum, enter its
address here. If your website is localized into other
languages, multiple translations of this field can be added. If

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

@ -16,7 +16,8 @@
<tbody>
<tr>
<th>
{{ tip(_("Developer Comments"),
<label for="developer_comments">{{ _("Developer Comments") }}</label>
{{ tip(None,
_("Any information end users may want to know that isn't
necessarily applicable to the add-on summary or description.
Common uses include listing known major bugs, information on

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

@ -13,6 +13,12 @@
{% endif %}
{% endmacro %}
{% macro trans_readonly(name, dict) %}
<div class="trans" data-name"{{ name }}">
{{ dict|pprint }}
</div>
{% endmacro %}
{% macro flags(text, element, editable, alt_text=False) %}
{% if editable %}
<div>

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

@ -59,7 +59,7 @@ class TranslatedField(models.ForeignKey):
def formfield(self, **kw):
widget = TransInput if self.short else TransTextarea
defaults = {'form_class': TranslationFormField, 'widget': widget}
defaults = {'form_class': TransField, 'widget': widget}
defaults.update(kw)
return super(TranslatedField, self).formfield(**defaults)
@ -178,13 +178,13 @@ class TranslationDescriptor(related.ReverseSingleRelatedObjectDescriptor):
return rv
class TranslationFormField(forms.Field):
class TransField(forms.Field):
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)
super(TransField, self).__init__(*args, **kwargs)
def clean(self, value):
return dict(value)

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

@ -39,7 +39,7 @@ def truncate(s, length=255, killwords=False, end='...'):
@register.inclusion_tag('translations/trans-menu.html')
@jinja2.contextfunction
def l10n_menu(context, default_locale='en-us'):
"""generates the locale menu for zamboni l10n"""
"""Generates the locale menu for zamboni l10n."""
languages = dict((i.lower(), j) for i, j in settings.LANGUAGES.items())
c = dict(context.items())
c.update(locals())

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

@ -1,13 +1,14 @@
<div class="transbox-new" data-default="{{ default_locale }}" id="l10n-menu">
{% set dl = languages[default_locale] %}
<p>
{# l10n: {0} is a language name, like 'French' #}
{% trans %}
Localize for: <a id="change-locale" href="#">{{ dl }}</a>
{% endtrans %}
</p>
<div id="locale-popup" class="popup hidden">
<section>
<div><a class="default_locale" href="#{{ default_locale }}">
<div><a class="default-locale" href="#{{ default_locale }}">
{{ dl }}
</a></div>
<ul id="existing_locales">
@ -23,4 +24,16 @@
</ul>
</section>
</div>
<div id="modal-l10n-unsaved" class="modal hidden">
<p class="msg">
{# l10n: {0} is a language name, like 'French' #}
{% trans %}
You have unsaved changes in the <b>{0}</b> locale.
Would you like to save your changes before switching locales?
{% endtrans %}
</p>
<button id="l10n-save-changes" class="button">{{ _('Save Changes') }}</button>
<button id="l10n-discard-changes" class="button">{{ _('Discard Changes') }}</button>
{{ _('or') }} <a id="l10n-cancel-changes" href="#">{{ _('Cancel') }}</a>
</div>
</div>

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

@ -89,7 +89,7 @@ class TransMulti(forms.widgets.MultiWidget):
def format_output(self, widgets):
s = super(TransMulti, self).format_output(widgets)
return '<div class="trans" data-name="%s">%s</div>' % (self.name, s)
return '<div id="trans-%s" class="trans" data-name="%s">%s</div>' % (self.name, self.name, s)
class _TransWidget(object):

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

@ -134,11 +134,15 @@ a.remove:hover,
padding-bottom: 0;
}
#id_tags, #id_name {
#trans-tags input,
#trans-name input {
width: 80%;
}
#id_homepage, #id_support_url, #id_support_email {
/*#id_homepage, #id_support_url, #id_support_email {*/
#trans-support_email input,
#trans-homepage input,
#trans-support_url input {
width: 60%;
}

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

@ -3080,6 +3080,9 @@ input.ui-autocomplete-loading {
#l10n-menu p {
margin: 0;
}
.html-rtl #l10n-menu {
float: left;
}
#change-locale {
padding-right: 16px;
}
@ -3108,7 +3111,18 @@ input.ui-autocomplete-loading {
display: inline-block;
float: right;
}
#locale-popup section > *:not(:last-child) {
border-bottom: 1px dotted #a4cfde;
#locale-popup section > div,
#locale-popup section > ul {
border-top: 1px dotted #a4cfde;
margin-bottom: 0;
}
#locale-popup section > div:first-child,
#locale-popup section > ul:first-child {
border-top: none;
}
.trans .cloned {
color: #ccc;
font-style: italic;
}
/* end l10n */

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

@ -1,8 +1,46 @@
$(document).ready(function () {
if (!$("#l10n-menu").length) return;
var locales = [];
dl = $('.default_locale').attr('href').substring(1);
currentLocale = dl;
dl = $('.default-locale').attr('href').substring(1);
currentLocale = dl,
unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(),
unsavedModal = $('#modal-l10n-unsaved').modal(),
translations = {}; //hold the initial values of the fields to check for changes
$(".primary").delegate(".trans input, .trans textarea", "change keyup paste blur", checkTranslation);
$("form").submit(function () {
$(this).find(".trans .cloned").remove();
})
function checkTranslation(e, t) {
var cloned_class="cloned";
var $input = e.originalEvent ? $(this) : $(format("[lang={0}]", [e]), t),
$trans = $input.closest(".trans"),
lang = e.originalEvent ? $input.attr("lang") : e;
$dl = $(format("[lang={0}]", [dl]), $trans),
transKey = $trans.attr("data-name")+'_'+lang;
if (lang == dl) {
cloned_class="";
$input.removeClass("cloned");
}
if (!(transKey in translations)) {
translations[transKey] = $input.val();
}
if (lang != dl && $input.val() == $dl.val() && $input.val().trim().length) {
$input.addClass(cloned_class).removeClass("unsaved");
} else if (!$input.val().trim().length) {
if (e.originalEvent && e.type == "focusout") {
$input.val($dl.val()).addClass(cloned_class).removeClass("unsaved");
} else {
$input.removeClass(cloned_class).removeClass("unsaved");
}
} else {
$input.removeClass(cloned_class);
if (translations[transKey] != $input.val()) {
$input.addClass("unsaved")
}
}
}
var localePopup = $("#locale-popup").popup("#change-locale", {
pointTo: "#change-locale",
@ -23,11 +61,49 @@ $(document).ready(function () {
e.preventDefault();
$tgt = $(this);
var new_locale = $tgt.attr("href").substring(1);
if (!_.include(locales,new_locale)) {
locales.push(new_locale);
var unsaved = $("form .trans .unsaved");
if (unsaved.length) {
unsavedModal.children(".msg")
.html(format(unsavedModalMsg,[$("#change-locale").text()]));
unsavedModal.render();
$("#l10n-save-changes").click(function () {
var unsavedForms = $('form:has(.trans .unsaved)');
var numFormsLeft = unsavedForms.length;
var erroredForms = 0;
unsavedForms.each(function() {
var $form = $(this);
$.post($form.attr('action'), $form.serialize(), function(d) {
var $resp = $(d);
numFormsLeft--;
if ($resp.find(".errorlist").length) { //display errors if they occur
$form.html($resp.html());
updateLocale();
erroredForms++;
} else { //clean up the errors we inserted
$form.find(".errorlist").remove();
}
if (numFormsLeft < 1) {
if (erroredForms) {
window.scrollTo(0,$(".l10n-error").offset().top);
} else {
updateLocale(new_locale);
}
}
});
});
unsavedModal.hideMe();
});
$("#l10n-discard-changes").click(function () {
$('.trans .unsaved').remove();
updateLocale(new_locale);
unsavedModal.hideMe();
});
$("#l10n-cancel-changes").click(function () {
unsavedModal.hideMe();
});
} else {
updateLocale(new_locale);
}
$("#change-locale").text($tgt.text());
updateLocale(new_locale);
localePopup.hideMe();
});
@ -37,25 +113,30 @@ $(document).ready(function () {
function updateLocale(lang) {
lang = lang || currentLocale;
if (currentLocale != lang) {
currentLocale = lang;
}
if (!_.include(locales,lang)) {
locales.push(lang);
}
$("#change-locale").text($(format("#locale-popup [href$={0}]", [lang])).first().text());
$(".trans").each(function () {
var $el = $(this),
field = $el.attr('data-name');
label = $(format("label[for={0}]",[field]));
if (!$el.children(format("[lang={0}]",[lang])).length) {
var $ni = $el.children(format("[lang={0}]",[dl])).clone();
$ni.attr('id',format('id_{0}_{1}',[field,lang]))
.val("").html("").attr("lang", lang).attr('name',[field,lang].join('_'));
$ni.attr('id',format('id_{0}_{1}',[field,lang])).addClass("cloned")
.attr("lang", lang).attr('name',[field,lang].join('_'));
$el.append($ni);
}
checkTranslation(lang, $el);
if (label.length) {
label.children(".locale").remove();
label.append(format("<span class='locale'>{0}</span>",[$("#change-locale").text()]));
}
});
if (currentLocale != lang) {
currentLocale = lang;
}
$(format(".trans [lang!={0}]:visible", [currentLocale])).hide();
$(format(".trans [lang={0}]", [lang])).show();
}