mirror of
https://github.com/Hopiu/django-modeltranslation.git
synced 2026-05-04 03:24:52 +00:00
Added jquery-ui based admin support for tabbed translation fields. Resolves issue 39 (thanks to jaap and adamsc).
This commit is contained in:
parent
704a559c0d
commit
6438ef28e5
5 changed files with 213 additions and 5 deletions
|
|
@ -1,3 +1,5 @@
|
|||
ADDED: Jquery-ui based admin support for tabbed translation fields.
|
||||
(thanks to jaap and adamsc, resolves issue 39)
|
||||
ADDED: CSS class to identify a translation field and the default translation
|
||||
field in admin.
|
||||
(thanks to jaap)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ in detail in the following sections:
|
|||
synced to the database before. If they did - read further down what to do
|
||||
in that case.
|
||||
|
||||
|
||||
Configure the project's ``settings.py``
|
||||
---------------------------------------
|
||||
The following variables have to be added to or edited in the project's
|
||||
|
|
@ -250,6 +251,7 @@ The example above assumes that the default language is ``de``, therefore the
|
|||
fields*. If the default language is ``en``, the ``title_en`` and ``text_en``
|
||||
fields would be the *default translation fields*.
|
||||
|
||||
|
||||
Rules for translated field access
|
||||
---------------------------------
|
||||
So now when it comes to setting and getting the value of the original and the
|
||||
|
|
@ -301,6 +303,12 @@ the ``django.utils.i18n.get_language`` function to determine the current
|
|||
language.
|
||||
|
||||
|
||||
Set a default value
|
||||
-------------------
|
||||
*New in development version*
|
||||
TODO
|
||||
|
||||
|
||||
Django admin backend integration
|
||||
================================
|
||||
In order to be able to edit the translations via the admin backend you need to
|
||||
|
|
@ -316,9 +324,9 @@ patching on all your models registered for translation::
|
|||
|
||||
admin.site.register(News, NewsAdmin)
|
||||
|
||||
|
||||
Tweaks applied to the admin
|
||||
---------------------------
|
||||
|
||||
The ``TranslationAdmin`` class does only implement one special method which is
|
||||
``def formfield_for_dbfield(self, db_field, **kwargs)``. This method does the
|
||||
following:
|
||||
|
|
@ -329,6 +337,7 @@ following:
|
|||
3. Checks if the - now removed - original field was required and if so makes the
|
||||
default translation field required instead.
|
||||
|
||||
|
||||
TranslationAdmin in combination with other admin classes
|
||||
--------------------------------------------------------
|
||||
If there already exists a custom admin class for a translated model and you
|
||||
|
|
@ -433,10 +442,32 @@ class TranslatedNewsAdmin(NewsAdmin, TranslationAdmin):
|
|||
admin.site.register(News, NewsAdmin)
|
||||
}}}
|
||||
|
||||
Set a default value
|
||||
-------------------
|
||||
*New in developement version*
|
||||
TODO
|
||||
|
||||
Using tabbed translation fields
|
||||
-------------------------------
|
||||
*New in development version*
|
||||
Modeltranslation supports separation of translation fields via jquery-ui tabs.
|
||||
The proposed way to include it is through the inner `Media` class of a
|
||||
`TranslationAdmin` class like this:
|
||||
|
||||
{{{
|
||||
class NewsAdmin(TranslationAdmin):
|
||||
class Media:
|
||||
js = (
|
||||
'/static/modeltranslation/js/force_jquery.js',
|
||||
'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js',
|
||||
'/static/modeltranslation/js/tabbed_translation_fields.js',
|
||||
)
|
||||
css = {
|
||||
'screen': ('/static/modeltranslation/js/tabbed_translation_fields.css',),
|
||||
}
|
||||
}}}
|
||||
|
||||
The `force_jquery.js` script is necessary when using Django's built-in
|
||||
`django.jQuery` object. This and the static urls used are just an example and
|
||||
might have to be adopted to your setup of serving static files. Standard
|
||||
jquery-ui theming can be used to customize the look of tabs, the provided css
|
||||
file is supposed to work well with a default Django admin.
|
||||
|
||||
|
||||
The ``update_translation_fields`` command
|
||||
|
|
@ -488,6 +519,7 @@ field is updated::
|
|||
'foo'
|
||||
>>>
|
||||
|
||||
|
||||
Accessing translated fields outside views
|
||||
-----------------------------------------
|
||||
Since the ``modeltranslation`` mechanism relies on the current language as it
|
||||
|
|
@ -529,6 +561,7 @@ It is not possible to reuse existing models without modifying them.
|
|||
A much simpler version of the above `django-multilingual`.
|
||||
It works very similiar to the `django-multilingual` approach.
|
||||
|
||||
|
||||
`transdb`_
|
||||
----------
|
||||
|
||||
|
|
@ -537,6 +570,7 @@ It works very similiar to the `django-multilingual` approach.
|
|||
This approach uses a specialized ``Field`` class, which means one has to change
|
||||
existing models.
|
||||
|
||||
|
||||
`i18ndynamic`_
|
||||
--------------
|
||||
This approach is not developed any more.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* jQuery UI CSS Framework
|
||||
* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
||||
* http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/base/jquery.ui.core.css
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden { display: none; }
|
||||
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
|
||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
.ui-helper-clearfix { display: inline-block; }
|
||||
/* required comment for clearfix to work in Opera \*/
|
||||
* html .ui-helper-clearfix { height:1%; }
|
||||
.ui-helper-clearfix { display:block; }
|
||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
/* http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/base/jquery.ui.tabs.css */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
|
||||
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
|
||||
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
|
||||
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
|
||||
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
|
||||
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
|
||||
.ui-tabs .ui-tabs-hide {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
}
|
||||
|
||||
/* custom tabs theme */
|
||||
.ui-tabs { padding: 0; }
|
||||
.ui-tabs .ui-tabs-nav { padding: 5px 0 0 10px; border-bottom: 1px solid #EEEEEE; }
|
||||
.ui-tabs .ui-tabs-nav li { margin: 0; }
|
||||
.ui-tabs .ui-tabs-nav li.required { font-weight: bold; }
|
||||
.ui-tabs .ui-tabs-nav li a {
|
||||
border: 1px solid #CCCCCC;
|
||||
background: #eeeeee repeat-x;
|
||||
border-bottom-width: 0;
|
||||
color: #666666;
|
||||
padding: 4px 10px 4px 10px;
|
||||
margin-top: 2px;
|
||||
-moz-border-radius-topright: 4px;
|
||||
-webkit-border-top-right-radius: 4px;
|
||||
-moz-border-radius-topleft: 4px;
|
||||
-webkit-border-top-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a{
|
||||
background: #7CA0C7 repeat-x;
|
||||
color: #fff;
|
||||
padding: 6px 10px 4px 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ui-tabs .ui-tabs-panel{
|
||||
padding: 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
if (!jQuery) {
|
||||
jQuery = django.jQuery;
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true,
|
||||
plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
|
||||
var google, django, gettext;
|
||||
|
||||
(function () {
|
||||
var jQuery = django.jQuery || jQuery || $;
|
||||
jQuery(function ($) {
|
||||
function getGroupedTranslationFields() {
|
||||
/** Returns a grouped set of all text based model translation fields.
|
||||
* The returned datastructure will look something like this:
|
||||
* {
|
||||
* 'title': {
|
||||
* 'en': HTMLInputElement,
|
||||
* 'de': HTMLInputElement,
|
||||
* 'fr': HTMLInputElement
|
||||
* },
|
||||
* 'body': {
|
||||
* 'en': HTMLTextAreaElement,
|
||||
* 'de': HTMLTextAreaElement,
|
||||
* 'fr': HTMLTextAreaElement
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
var translation_fields = $('.modeltranslation').filter('input[type=text]:visible, textarea:visible'),
|
||||
grouped_translations = {};
|
||||
|
||||
translation_fields.each(function (i, el) {
|
||||
var name = $(el).attr('name').split('_'),
|
||||
lang = name.pop();
|
||||
name = name.join('_');
|
||||
if (!grouped_translations[name]) {
|
||||
grouped_translations[name] = {};
|
||||
}
|
||||
grouped_translations[name][lang] = el;
|
||||
});
|
||||
|
||||
return grouped_translations;
|
||||
}
|
||||
|
||||
function createTabs() {
|
||||
var grouped_translations = getGroupedTranslationFields();
|
||||
var tabs = [];
|
||||
$.each(grouped_translations, function (name, languages) {
|
||||
var tabs_container = $('<div></div>'),
|
||||
tabs_list = $('<ul></ul>'),
|
||||
insertion_point;
|
||||
tabs_container.append(tabs_list);
|
||||
$.each(languages, function (lang, el) {
|
||||
var container = $(el).closest('.form-row'),
|
||||
label = $('label', container),
|
||||
field_label = container.find('label'),
|
||||
id = 'tab_' + [name, lang].join('_'),
|
||||
panel, tab;
|
||||
// Remove language and brackets from field label, they are
|
||||
// displayed in the tab already.
|
||||
if (field_label.html()) {
|
||||
field_label.html(field_label.html().replace(/\ \[.+\]/, ''));
|
||||
}
|
||||
if (!insertion_point) {
|
||||
insertion_point = {
|
||||
'insert': container.prev().length ? 'after' : container.next().length ? 'prepend' : 'append',
|
||||
'el': container.prev().length ? container.prev() : container.parent()
|
||||
};
|
||||
}
|
||||
panel = $('<div id="' + id + '"></div>').append(container);
|
||||
tab = $('<li' + (label.hasClass('required') ? ' class="required"' : '') + '><a href="#' + id + '">' + lang + '</a></li>');
|
||||
tabs_list.append(tab);
|
||||
tabs_container.append(panel);
|
||||
});
|
||||
insertion_point.el[insertion_point.insert](tabs_container);
|
||||
tabs_container.tabs();
|
||||
tabs.push(tabs_container);
|
||||
});
|
||||
return tabs;
|
||||
}
|
||||
|
||||
function createMainSwitch(tabs) {
|
||||
var grouped_translations = getGroupedTranslationFields(),
|
||||
unique_languages = [],
|
||||
select = $('<select>');
|
||||
$.each(grouped_translations, function (name, languages) {
|
||||
$.each(languages, function (lang, el) {
|
||||
if ($.inArray(lang, unique_languages) < 0) {
|
||||
unique_languages.push(lang);
|
||||
}
|
||||
});
|
||||
});
|
||||
$.each(unique_languages, function (i, language) {
|
||||
select.append($('<option value="' + i + '">' + language + '</option>'));
|
||||
});
|
||||
select.change(function (e) {
|
||||
$.each(tabs, function (i, tab) {
|
||||
tab.tabs('select', parseInt(select.val()));
|
||||
});
|
||||
});
|
||||
$('#content h1').append(' ').append(select);
|
||||
}
|
||||
|
||||
if ($('body').hasClass('change-form')) {
|
||||
createMainSwitch(createTabs());
|
||||
}
|
||||
});
|
||||
}());
|
||||
Loading…
Reference in a new issue