From ed2412ca526037848456b8e95abc784d0b8d1b0d Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Wed, 15 Jul 2015 14:16:01 -0500 Subject: [PATCH] i18n: Add translation to the field hint --- css/redactor.css | 5 + include/ajax.i18n.php | 27 +++ include/class.forms.php | 2 +- .../templates/dynamic-field-config.tmpl.php | 2 +- js/redactor-osticket.js | 2 + js/redactor-plugins.js | 160 ++++++++++++++++++ scp/ajax.php | 9 +- scp/js/scp.js | 2 +- 8 files changed, 205 insertions(+), 4 deletions(-) diff --git a/css/redactor.css b/css/redactor.css index a3dc97844..16d0d7c41 100644 --- a/css/redactor.css +++ b/css/redactor.css @@ -926,3 +926,8 @@ body .redactor-box-fullscreen { font-size: 12px; text-transform: uppercase; } +.redactor-editor[dir=rtl] { + direction: rtl; + text-align: right; + unicode-bidi: embed; +} diff --git a/include/ajax.i18n.php b/include/ajax.i18n.php index 9d3e76b60..4665f3ab2 100644 --- a/include/ajax.i18n.php +++ b/include/ajax.i18n.php @@ -100,6 +100,33 @@ class i18nAjaxAPI extends AjaxController { Http::response(500, sprintf("%s: Unable to commit language")); } + function getConfiguredLanguages() { + global $cfg; + + $primary = $cfg->getPrimaryLanguage(); + $info = Internationalization::getLanguageInfo($primary); + $langs = array( + $primary => array( + 'name' => Internationalization::getLanguageDescription($primary), + 'flag' => strtolower($info['flag']), + 'direction' => $info['direction'] ?: 'ltr', + ), + ); + + foreach ($cfg->getSecondaryLanguages() as $l) { + $info = Internationalization::getLanguageInfo($l); + $langs[$l] = array( + 'name' => Internationalization::getLanguageDescription($l), + 'flag' => strtolower($info['flag']), + 'direction' => $info['direction'] ?: 'ltr', + ); + } + $json = JsonDataEncoder::encode($langs); + Http::cacheable(md5($json), $cfg->lastModified()); + + return $json; + } + function getSecondaryLanguages() { global $cfg; diff --git a/include/class.forms.php b/include/class.forms.php index 135916269..d472d3de3 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -2016,7 +2016,7 @@ class ThreadEntryField extends FormField { } function getWidget($widgetClass=false) { - if ($hint = $this->get('hint')) + if ($hint = $this->getLocal('hint')) $this->set('placeholder', $hint); $this->set('hint', null); $widget = parent::getWidget($widgetClass); diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php index 24bf9a79f..732ec709b 100644 --- a/include/staff/templates/dynamic-field-config.tmpl.php +++ b/include/staff/templates/dynamic-field-config.tmpl.php @@ -181,7 +181,7 @@ <script type="text/javascript"> // Make translatable fields translatable - $('input[data-translate-tag], textarea[data-translate-tag]').translatable(); + $('input[data-translate-tag]').translatable(); </script> <style type="text/css"> diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index d9a26e932..c39352812 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -311,6 +311,8 @@ $(function() { } if (el.hasClass('fullscreen')) options['plugins'].push('fullscreen'); + if (el.data('translateTag')) + options['plugins'].push('translatable'); if ($('#ticket_thread[data-thread-id]').length) options['imageManagerJson'] += '?threadId=' + $('#ticket_thread').data('threadId'); getConfig().then(function(c) { diff --git a/js/redactor-plugins.js b/js/redactor-plugins.js index 8f01f5f6c..d20519f21 100644 --- a/js/redactor-plugins.js +++ b/js/redactor-plugins.js @@ -1842,3 +1842,163 @@ RedactorPlugins.contexttypeahead = function() { } }; }; + +RedactorPlugins.translatable = function() { + return { + langs: undefined, + config: undefined, + textareas: {}, + current: undefined, + primary: undefined, + button: undefined, + + init: function() { + $.ajax({ + url: 'ajax.php/i18n/langs/all', + success: this.translatable.setLangs.bind(this) + }); + getConfig().then(this.translatable.setConfig.bind(this)); + this.opts.keydownCallback = this.translatable.showCommit.bind(this); + this.translatable.translateTag = this.$textarea.data('translateTag'); + }, + + setLangs: function(langs) { + this.translatable.langs = langs; + this.translatable.buildDropdown(); + }, + + setConfig: function(config) { + this.translatable.config = config; + this.translatable.buildDropdown(); + }, + + buildDropdown: function() { + if (!this.translatable.config || !this.translatable.langs) + return; + + var plugin = this.translatable, + primary = this.$textarea, + primary_lang = plugin.config.primary_language.replace('-','_'), + primary_info = plugin.langs[primary_lang], + dropdown = {}, + items = {}; + + langs = plugin.langs; + plugin.textareas[primary_lang] = primary; + plugin.primary = plugin.current = primary_lang; + + dropdown[primary_lang] = { + title: '<i class="flag flag-'+primary_info.flag+'"></i> '+primary_info.name, + func: function() { plugin.switchTo(primary_lang); } + } + + $.each(langs, function(lang, info) { + if (lang == primary_lang) + return; + dropdown[lang] = { + title: '<i class="flag flag-'+info.flag+'"></i> '+info.name, + func: function() { plugin.switchTo(lang); } + }; + plugin.textareas[lang] = primary.clone(false).attr({ + lang: lang, + dir: info['direction'], + 'class': '', + }) + .removeAttr('name').removeAttr('data-translate-tag') + .text('') + .insertAfter(primary); + }); + + // Add the button to the toolbar + plugin.button = this.button.add('translate', __('Translate')), + this.button.setAwesome('translate', 'flag flag-' + plugin.config.primary_lang_flag); + plugin.button.parent().addClass('pull-right'); + this.button.addDropdown(plugin.button, dropdown); + + // Flip back to primary language before submitting + this.$textarea.closest('form').submit(function() { + plugin.switchTo(primary_lang); + }); + }, + + switchTo: function(lang) { + var that = this; + + if (lang == this.translatable.current) + return; + + if (this.translatable.translations === undefined) { + this.translatable.fetch('ajax.php/i18n/translate/' + this.translatable.translateTag) + .then(function(json) { + that.translatable.translations = json; + $.each(json, function(l, text) { + that.translatable.textareas[l].val(text); + }); + // Now switch to the language + that.translatable.switchTo(lang); + }); + return; + } + + var html = this.$editor.html(); + this.$textarea.val(this.clean.onSync(html)); + this.$textarea = this.translatable.textareas[lang]; + this.code.set(this.$textarea.val()); + this.translatable.current = lang; + + this.button.setAwesome('translate', 'flag flag-' + this.translatable.langs[lang].flag); + this.$editor.attr({lang: lang, dir: this.translatable.langs[lang].direction}); + }, + + showCommit: function() { + var plugin = this.translatable; + + if (this.translatable.current == this.translatable.primary) { + if (this.translatable.$commit) + this.translatable.$commit + .slideUp(function() { $(this).remove(); plugin.$commit = undefined; }); + return true; + } + + if (this.translatable.$commit) + return true; + + this.translatable.$commit = $('<div class="language-commit"></div>') + .hide() + .appendTo(this.$box) + .append($('<button type="button" class="white button commit"><i class="fa fa-save icon-save"></i> '+__('Save')+'</button>') + .on('click', $.proxy(this.translatable.commit, this)) + ) + .slideDown(); + }, + + commit: function() { + var changes = {}, self = this, + plugin = this.translatable, + $commit = plugin.$commit; + $commit.find('button').empty().text(' '+__('Saving')) + .prop('disabled', true) + .prepend($('<i>').addClass('fa icon-spin icon-spinner')); + changes[plugin.current] = this.code.get(); + $.ajax('ajax.php/i18n/translate/' + plugin.translateTag, { + type: 'post', + data: changes, + success: function() { + $commit.slideUp(function() { $(this).remove(); plugin.$commit = undefined; }); + } + }); + }, + + urlcache: {}, + fetch: function( url, data, callback ) { + var urlcache = this.translatable.urlcache; + if ( !urlcache[ url ] ) { + urlcache[ url ] = $.Deferred(function( defer ) { + $.ajax( url, { data: data, dataType: 'json' } ) + .then( defer.resolve, defer.reject ); + }).promise(); + } + return urlcache[ url ].done( callback ); + }, + }; +}; diff --git a/scp/ajax.php b/scp/ajax.php index 36d9a847a..6971008a6 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -222,6 +222,7 @@ $dispatcher = patterns('', url_get('^(?P<lang>[\w_]+)?/tips/(?P<namespace>[\w_.]+)$', 'getTipsJsonForLang') )), url('^/i18n/', patterns('ajax.i18n.php:i18nAjaxAPI', + url_get('^langs/all$', 'getConfiguredLanguages'), url_get('^langs$', 'getSecondaryLanguages'), url_get('^translate/(?P<tag>\w+)$', 'getTranslations'), url_post('^translate/(?P<tag>\w+)$', 'updateTranslations'), @@ -248,5 +249,11 @@ $dispatcher = patterns('', Signal::send('ajax.scp', $dispatcher); # Call the respective function -print $dispatcher->resolve($ost->get_path_info()); +$rv = $dispatcher->resolve($ost->get_path_info()); + +// Indicate JSON response content-type +if (is_string($rv) && $rv[0] == '{') + Http::response(200, $rv, 'application/json'); + +print $rv; ?> diff --git a/scp/js/scp.js b/scp/js/scp.js index a15b0e64f..19a0e1376 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -386,7 +386,7 @@ var scp_prep = function() { // Make translatable fields translatable - $('input[data-translate-tag], textarea[data-translate-tag]').translatable(); + $('input[data-translate-tag]').translatable(); if (window.location.hash) { $('ul.tabs li a[href="' + window.location.hash + '"]').trigger('click'); -- GitLab