summaryrefslogtreecommitdiff
path: root/addons/website/static/src
diff options
context:
space:
mode:
authorFabien Meghazi <fme@openerp.com>2013-09-23 13:54:17 +0200
committerFabien Meghazi <fme@openerp.com>2013-09-23 13:54:17 +0200
commitb783cbf4a628cbab5cc3bc7ec4a368970b6fcdd1 (patch)
tree9a46f2a201f05ead0dfd770b1bf70781df6c32fd /addons/website/static/src
parent366843b0020ce4296b60d38ff3cc6a2344ce1bf8 (diff)
[ADD] Translator
bzr revid: fme@openerp.com-20130923115417-bq20piq7f11pyjve
Diffstat (limited to 'addons/website/static/src')
-rw-r--r--addons/website/static/src/css/editor.css21
-rw-r--r--addons/website/static/src/css/editor.sass16
-rw-r--r--addons/website/static/src/js/website.translator.js151
-rw-r--r--addons/website/static/src/xml/website.translator.xml53
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>