diff options
| author | Fabien Meghazi <fme@openerp.com> | 2013-09-23 13:54:17 +0200 |
|---|---|---|
| committer | Fabien Meghazi <fme@openerp.com> | 2013-09-23 13:54:17 +0200 |
| commit | b783cbf4a628cbab5cc3bc7ec4a368970b6fcdd1 (patch) | |
| tree | 9a46f2a201f05ead0dfd770b1bf70781df6c32fd | |
| parent | 366843b0020ce4296b60d38ff3cc6a2344ce1bf8 (diff) | |
[ADD] Translator
bzr revid: fme@openerp.com-20130923115417-bq20piq7f11pyjve
| -rw-r--r-- | addons/website/static/src/css/editor.css | 21 | ||||
| -rw-r--r-- | addons/website/static/src/css/editor.sass | 16 | ||||
| -rw-r--r-- | addons/website/static/src/js/website.translator.js | 151 | ||||
| -rw-r--r-- | addons/website/static/src/xml/website.translator.xml | 53 |
4 files changed, 241 insertions, 0 deletions
diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 127939f6caf..b6cc8505fed 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -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; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 6eb47eab614..b5c0b21a197 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -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: diff --git a/addons/website/static/src/js/website.translator.js b/addons/website/static/src/js/website.translator.js new file mode 100644 index 00000000000..ac4c6d298d2 --- /dev/null +++ b/addons/website/static/src/js/website.translator.js @@ -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(); + }, + }); +})(); diff --git a/addons/website/static/src/xml/website.translator.xml b/addons/website/static/src/xml/website.translator.xml new file mode 100644 index 00000000000..f35cf583da9 --- /dev/null +++ b/addons/website/static/src/xml/website.translator.xml @@ -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> |
