Expanded localization coverage
This commit is contained in:
Родитель
041b1819be
Коммит
1a200f6146
|
@ -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();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче