[ADD] Translator
bzr revid: fme@openerp.com-20130923115417-bq20piq7f11pyjve
This commit is contained in:
@@ -107,6 +107,27 @@ table.editorbar-panel td.selected {
|
||||
background-color: #b1c9d9;
|
||||
}
|
||||
|
||||
.oe_translate_examples li {
|
||||
margin: 10px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.oe_translatable_text {
|
||||
outline: 1px solid black;
|
||||
}
|
||||
|
||||
.oe_translatable_field {
|
||||
outline: 1px dashed black;
|
||||
}
|
||||
|
||||
.oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty {
|
||||
outline-color: red;
|
||||
}
|
||||
|
||||
.oe_translatable_todo {
|
||||
background: #ffffb6;
|
||||
}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
.oe_editable .btn {
|
||||
-webkit-user-select: auto;
|
||||
|
||||
@@ -93,6 +93,20 @@ table.editorbar-panel
|
||||
td.selected
|
||||
background-color: #b1c9d9
|
||||
|
||||
// ---- TRANSLATIONS ---- {{{
|
||||
.oe_translate_examples li
|
||||
margin: 10px
|
||||
padding: 4px
|
||||
.oe_translatable_text
|
||||
outline: 1px solid black
|
||||
.oe_translatable_field
|
||||
outline: 1px dashed black
|
||||
.oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty
|
||||
outline-color: red
|
||||
.oe_translatable_todo
|
||||
background: rgb(255, 255, 182)
|
||||
// }}}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
|
||||
// bootstrap makes .btn elements unselectable -> RTE double-click can't know
|
||||
@@ -481,3 +495,5 @@ $navbar_height: 51px
|
||||
&.oe_ace_closed
|
||||
width: 0
|
||||
opacity: 0
|
||||
|
||||
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.translator.xml');
|
||||
|
||||
website.EditorBar.include({
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
'click a[data-action=js_website_translator]': function (ev) {
|
||||
var self = this;
|
||||
ev.preventDefault();
|
||||
if (!localStorage['website_translator_nodialog']) {
|
||||
var dialog = new website.TranslatorDialog();
|
||||
dialog.appendTo($(document.body));
|
||||
dialog.on('activate', this, function () {
|
||||
dialog.$el.modal('hide');
|
||||
this.translateMode();
|
||||
});
|
||||
} else {
|
||||
this.translateMode();
|
||||
}
|
||||
},
|
||||
}),
|
||||
temporaryHack: function() {
|
||||
// TODO: refactor once xmo's feature branch is merged
|
||||
debugger
|
||||
this.$buttons.edit.prop('disabled', true);
|
||||
this.$('#website-top-view').hide();
|
||||
this.$('#website-top-edit').show();
|
||||
$('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden");
|
||||
this.$el.find('[data-action=snippet]').hide();
|
||||
this.save = this.saveTranslations;
|
||||
},
|
||||
translateMode: function () {
|
||||
var self = this;
|
||||
this.temporaryHack();
|
||||
// this.edit(); -- Will need a translation mode
|
||||
this.translations = null;
|
||||
openerp.jsonRpc('/website/get_view_translations', 'call', {
|
||||
'xml_id': $(document.documentElement).data('view-xmlid'),
|
||||
'context': website.get_context(),
|
||||
}).then(function (translations) {
|
||||
self.translations = translations;
|
||||
self.processTranslatableNodes();
|
||||
});
|
||||
},
|
||||
processTranslatableNodes: function () {
|
||||
var self = this;
|
||||
var $editables = $('[data-oe-model]')
|
||||
.not('link, script')
|
||||
.not('.oe_snippets,.oe_snippet, .oe_snippet *')
|
||||
.not('[data-oe-type]');
|
||||
$editables.each(function () {
|
||||
self.transNode(this);
|
||||
});
|
||||
$('.oe_translatable_text').prop('contenteditable', true).on('paste', function () {
|
||||
var node = $(this);
|
||||
setTimeout(function () {
|
||||
self.sanitizeNode(node);
|
||||
}, 0);
|
||||
});
|
||||
$(document).on('blur keyup paste', '.oe_translatable_text[contenteditable]', function(ev) {
|
||||
var $node = $(this);
|
||||
setTimeout(function () {
|
||||
// Doing stuff next tick because past and keyup are
|
||||
// fired before the content is changed
|
||||
if (ev.type == 'paste') {
|
||||
self.sanitizeNode($node[0]);
|
||||
}
|
||||
if ($node.data('initial_content') !== $node.text()) {
|
||||
$node.addClass('oe_dirty').removeClass('oe_translatable_todo');
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
sanitizeNode: function (node) {
|
||||
node.text(node.text());
|
||||
},
|
||||
isTextNode: function (node) {
|
||||
return node.nodeType === 3 || node.nodeType === 4;
|
||||
},
|
||||
isTranslatable: function (text) {
|
||||
return text && _.str.trim(text) !== '';
|
||||
},
|
||||
markTranslatableNode: function (node) {
|
||||
// TODO: link nodes with same content
|
||||
node.className += ' oe_translatable_text oe_translatable_todo';
|
||||
node.contentEditable = true;
|
||||
$(node).data('initial_content', node.childNodes[0].data);
|
||||
},
|
||||
saveTranslations: function () {
|
||||
var trans = {};
|
||||
$('.oe_translatable_text.oe_dirty').each(function () {
|
||||
var $node = $(this);
|
||||
var text = $node.text();
|
||||
var view_id = $(this).parents('[data-oe-model="ir.ui.view"]')
|
||||
.not('[data-oe-type]').first().data().oeId;
|
||||
if (!trans[view_id]) {
|
||||
trans[view_id] = [];
|
||||
}
|
||||
trans[view_id].push({
|
||||
initial_content: $node.data('initial_content'),
|
||||
new_content: text
|
||||
});
|
||||
});
|
||||
openerp.jsonRpc('/website/set_translations', 'call', {
|
||||
'data': trans,
|
||||
'lang': website.get_context()['lang'],
|
||||
}).then(function () {
|
||||
website.reload();
|
||||
}).fail(function () {
|
||||
// TODO: bootstrap alert with error message
|
||||
alert("Could not save translation");
|
||||
});
|
||||
},
|
||||
transNode: function (node, context) {
|
||||
if (node.childNodes.length === 1
|
||||
&& this.isTextNode(node.childNodes[0])
|
||||
&& !node.getAttribute('data-oe-model')) {
|
||||
this.markTranslatableNode(node);
|
||||
} else {
|
||||
for (var i = 0, l = node.childNodes.length; i < l; i ++) {
|
||||
var n = node.childNodes[i];
|
||||
if (this.isTextNode(n)) {
|
||||
if (this.isTranslatable(n.data)) {
|
||||
var container = document.createElement('span');
|
||||
node.insertBefore(container, n);
|
||||
container.appendChild(n);
|
||||
this.markTranslatableNode(container);
|
||||
}
|
||||
} else {
|
||||
this.transNode(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
website.TranslatorDialog = openerp.Widget.extend({
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
'hidden.bs.modal': 'destroy',
|
||||
'click button[data-action=activate]': function (ev) {
|
||||
this.trigger('activate');
|
||||
},
|
||||
}),
|
||||
template: 'website.TranslatorDialog',
|
||||
start: function () {
|
||||
this.$el.modal();
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website.TranslatorDialog">
|
||||
<div class="modal fade oe_website_translator" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button title="Close" type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h2 class="modal-title">Translate this page</h2>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<section>
|
||||
<p>
|
||||
You are about to enter the translation mode.
|
||||
</p>
|
||||
<p>
|
||||
In this mode you can not change the structure of the document, you can only translate text.
|
||||
</p>
|
||||
<p>
|
||||
Here are the visuals used in order to help you in your translation flow:
|
||||
<ul class="oe_translate_examples">
|
||||
<li class="oe_translatable_text">
|
||||
Normal translatable content
|
||||
</li>
|
||||
<li class="oe_translatable_field">
|
||||
ERP translatable object data
|
||||
</li>
|
||||
<li class="oe_translatable_todo">
|
||||
Translation TODO
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</section>
|
||||
<hr/>
|
||||
<section class="row">
|
||||
<div class="col-md-6">
|
||||
<button type="button" data-action="activate" class="btn btn-primary">Ok</button>
|
||||
or
|
||||
<a data-action="discard" data-dismiss="modal" href="#">Cancel</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label>
|
||||
<input type="checkbox" name="do_not_show"/>
|
||||
Do not show this dialog later.
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
Reference in New Issue
Block a user