From 436ddad323d22e928c57b03e8bd43c0d7af80e7f Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sun, 28 Jun 2015 02:16:47 +0200 Subject: [PATCH] Improved admin translation interface. --- .../app/controllers/translation.js.coffee | 140 +++++++++++++++--- .../app/views/translation/index.jst.eco | 3 + .../app/views/translation/todo.jst.eco | 2 +- app/controllers/translations_controller.rb | 27 ++++ app/models/translation.rb | 70 ++++++--- config/routes/translation.rb | 8 +- .../20150628000001_translator_key_add.rb | 16 ++ 7 files changed, 227 insertions(+), 39 deletions(-) create mode 100644 db/migrate/20150628000001_translator_key_add.rb diff --git a/app/assets/javascripts/app/controllers/translation.js.coffee b/app/assets/javascripts/app/controllers/translation.js.coffee index 98e08f95e..626abf5ad 100644 --- a/app/assets/javascripts/app/controllers/translation.js.coffee +++ b/app/assets/javascripts/app/controllers/translation.js.coffee @@ -1,4 +1,9 @@ class Index extends App.ControllerContent + events: + 'click .js-pushChanges': 'pushChanges' + 'click .js-resetChanges': 'resetChanges' + 'click .js-syncChanges': 'syncChanges' + constructor: -> super @@ -19,11 +24,11 @@ class Index extends App.ControllerContent { name: 'locale', display: '', tag: 'select', null: false, class: 'input', options: options, default: App.i18n.get() }, ] load = (params) => - new TranslationToDo( + @translationToDo = new TranslationToDo( el: @$('.js-ToDo') locale: params.locale ) - new TranslationList( + @translationList = new TranslationList( el: @$('.js-List') locale: params.locale ) @@ -38,12 +43,90 @@ class Index extends App.ControllerContent release: => rerender = -> App.Event.trigger('ui:rerender') - App.Delay.set(rerender, 400) + if @translationToDo.changes() || @translationList.changes() + App.Delay.set(rerender, 400) + + pushChanges: => + locale = @$('[name="locale"]').val() + + @modal = new App.ControllerModal( + head: 'Pushing own translations...' + message: 'Pushing own translations to i18n.zammad.com, Thanks for contributing!' + cancel: false + close: false + shown: true + container: @el.closest('.content') + ) + + @ajax( + id: 'translations' + type: 'PUT' + url: @apiPath + '/translations/push' + data: JSON.stringify(locale: locale) + processData: false + success: (data, status, xhr) => + @modal.hide() + error: => + @modal.hide() + ) + + resetChanges: => + locale = @$('[name="locale"]').val() + + @modal = new App.ControllerModal( + head: 'Reseting changes...' + message: 'Reseting changes own translation changes...' + cancel: false + close: false + shown: true + container: @el.closest('.content') + ) + + @ajax( + id: 'translations' + type: 'POST' + url: @apiPath + '/translations/reset' + data: JSON.stringify(locale: locale) + processData: false + success: (data, status, xhr) => + App.Event.trigger('i18n:translation_todo_reload') + App.Event.trigger('i18n:translation_list_reload') + @modal.hide() + error: => + @modal.hide() + ) + + syncChanges: => + locale = @$('[name="locale"]').val() + + @modal = new App.ControllerModal( + head: 'Syncing with latest translations...' + message: 'Syncing with latest translations!' + cancel: false + close: false + shown: true + container: @el.closest('.content') + ) + + @ajax( + id: 'translations' + type: 'POST' + url: @apiPath + '/translations/sync' + data: JSON.stringify(locale: locale) + processData: false + success: (data, status, xhr) => + App.Event.trigger('i18n:translation_todo_reload') + App.Event.trigger('i18n:translation_list_reload') + @modal.hide() + error: => + @modal.hide() + ) class TranslationToDo extends App.Controller + hasChanges: false events: - 'click .js-Create': 'create' - 'click .js-TheSame': 'same' + 'click .js-create': 'create' + 'click .js-theSame': 'same' constructor: -> super @@ -69,8 +152,16 @@ class TranslationToDo extends App.Controller list: listNotTranslated ) + showAction: => + @el.closest('.content').find('.js-changes').removeClass('hidden') + + changes: => + @hasChanges + create: (e) => e.preventDefault() + @hasChanges = true + @showAction() field = $(e.target).closest('tr').find('.js-Item') source = field.data('source') target = field.val() @@ -104,6 +195,8 @@ class TranslationToDo extends App.Controller same: (e) => e.preventDefault() + @hasChanges = true + @showAction() field = $(e.target).closest('tr').find('.js-Item') source = field.data('source') @@ -135,6 +228,7 @@ class TranslationToDo extends App.Controller ) class TranslationList extends App.Controller + hasChanges: false events: 'blur .js-translated input': 'update' 'click .js-translated .js-Reset': 'reset' @@ -159,12 +253,8 @@ class TranslationList extends App.Controller ) render: (data = {}) => - - #if !App.i18n.notTranslatedFeatureEnabled(@locale) - # return - @strings = [] - @times = [] + @times = [] for item in data.list if item[4] is 'time' @times.push item @@ -176,18 +266,29 @@ class TranslationList extends App.Controller strings: @strings ) ui = @ + changesAvailable = false @$('.js-Item').each( (e) -> id = $(this).data('id') ui.updateRow(id) + changesAvailable = true ) + if changesAvailable + @showAction() + + showAction: => + @el.closest('.content').find('.js-changes').removeClass('hidden') + + changes: => + @hasChanges reset: (e) -> e.preventDefault() - field = $(e.target).closest('tr').find('.js-Item') - id = field.data('id') - source = field.data('source') - initial = field.data('initial') - format = field.data('format') + @hasChanges = true + field = $(e.target).closest('tr').find('.js-Item') + id = field.data('id') + source = field.data('source') + initial = field.data('initial') + format = field.data('format') # if it's translated by user it self, delete it if !initial || initial is '' @@ -238,10 +339,11 @@ class TranslationList extends App.Controller update: (e) -> e.preventDefault() - id = $( e.target ).data('id') - source = $( e.target ).data('source') - format = $( e.target ).data('format') - target = $( e.target ).val() + @hasChanges = true + id = $( e.target ).data('id') + source = $( e.target ).data('source') + format = $( e.target ).data('format') + target = $( e.target ).val() # local update @updateRow(id) diff --git a/app/assets/javascripts/app/views/translation/index.jst.eco b/app/assets/javascripts/app/views/translation/index.jst.eco index f2b5a89dc..953e02f94 100644 --- a/app/assets/javascripts/app/views/translation/index.jst.eco +++ b/app/assets/javascripts/app/views/translation/index.jst.eco @@ -3,6 +3,9 @@

<%- @T('Translations') %>

+ <%- @T('Sync with latest') %> + +
diff --git a/app/assets/javascripts/app/views/translation/todo.jst.eco b/app/assets/javascripts/app/views/translation/todo.jst.eco index 5885d63c8..b273441aa 100644 --- a/app/assets/javascripts/app/views/translation/todo.jst.eco +++ b/app/assets/javascripts/app/views/translation/todo.jst.eco @@ -13,7 +13,7 @@ <%= item[1] %> - <%- @T('Create') %> / <%- @T('is the same') %> + <%- @T('Create') %> / <%- @T('is the same') %> <% end %> <% end %> diff --git a/app/controllers/translations_controller.rb b/app/controllers/translations_controller.rb index cb6a27ed2..854e95aaa 100644 --- a/app/controllers/translations_controller.rb +++ b/app/controllers/translations_controller.rb @@ -8,6 +8,31 @@ class TranslationsController < ApplicationController render json: Translation.list( params[:locale] ) end + # PUT /translations/push + def push + return if deny_if_not_role(Z_ROLENAME_ADMIN) + start = Time.zone.now + Translation.push(params[:locale]) + if start > Time.zone.now - 5.seconds + sleep 4 + end + render json: { message: 'ok' }, status: :ok + end + + # POST /translations/sync + def sync + return if deny_if_not_role(Z_ROLENAME_ADMIN) + Translation.load + render json: { message: 'ok' }, status: :ok + end + + # POST /translations/reset + def reset + return if deny_if_not_role(Z_ROLENAME_ADMIN) + Translation.reset(params[:locale]) + render json: { message: 'ok' }, status: :ok + end + # GET /translations/admin/lang/:locale def admin return if deny_if_not_role(Z_ROLENAME_ADMIN) @@ -16,11 +41,13 @@ class TranslationsController < ApplicationController # GET /translations def index + return if deny_if_not_role(Z_ROLENAME_ADMIN) model_index_render(Translation, params) end # GET /translations/1 def show + return if deny_if_not_role(Z_ROLENAME_ADMIN) model_show_render(Translation, params) end diff --git a/app/models/translation.rb b/app/models/translation.rb index 501a9abed..dbd4dce86 100644 --- a/app/models/translation.rb +++ b/app/models/translation.rb @@ -72,22 +72,54 @@ push translations to online } return true if translations_to_push.empty? - #return translations_to_push + url = 'https://i18n.zammad.com/api/v1/thanks_for_your_support' + translator_key = Setting.get('translator_key') + result = UserAgent.post( url, { locale: locale, translations: translations_to_push, fqdn: Setting.get('fqdn'), - translator_key: '', + translator_key: translator_key, }, { json: true, } ) fail "Can't push translations to #{url}: #{result.error}" if !result.success? + + # set new translator_key if given + if result.data['translator_key'] + translator_key = Setting.set('translator_key', result.data['translator_key']) + end + + true + end + +=begin + +reset translations to origin + + Translation.reset(locale) + +=end + + def self.reset(locale) + + # only push changed translations + translations = Translation.where(locale: locale) + translations.each {|translation| + if !translation.target_initial || translation.target_initial.empty? + translation.destroy + elsif translation.target != translation.target_initial + translation.target = translation.target_initial + translation.save + end + } + true end @@ -101,43 +133,47 @@ get list of translations def self.list(locale, admin = false) - # check cache + # use cache if not admin page is requested if !admin - list = cache_get( locale ) + data = cache_get(locale) end - if !list + if !data + + # show total translations as reference count + data = { + 'total' => Translation.where(locale: 'de-de').count, + } list = [] - translations = Translation.where( locale: locale.downcase ).order( :source ) + translations = Translation.where(locale: locale.downcase).order(:source) translations.each { |item| if admin - data = [ + translation_item = [ item.id, item.source, item.target, item.target_initial, item.format, ] - list.push data + list.push translation_item else - data = [ + translation_item = [ item.id, item.source, item.target, item.format, ] - list.push data + list.push translation_item end + data['list'] = list } # set cache if !admin - cache_set( locale, list ) + cache_set(locale, data) end end - { - list: list, - } + data end =begin @@ -174,12 +210,12 @@ translate strings in ruby context, e. g. for notifications end def cache_clear - Cache.delete( 'Translation::' + locale.downcase ) + Cache.delete( 'TranslationMap::' + locale.downcase ) end def self.cache_set(locale, data) - Cache.write( 'Translation::' + locale.downcase, data ) + Cache.write( 'TranslationMap::' + locale.downcase, data ) end def self.cache_get(locale) - Cache.get( 'Translation::' + locale.downcase ) + Cache.get( 'TranslationMap::' + locale.downcase ) end end diff --git a/config/routes/translation.rb b/config/routes/translation.rb index 9293a85f9..c7281fbad 100644 --- a/config/routes/translation.rb +++ b/config/routes/translation.rb @@ -1,12 +1,16 @@ Zammad::Application.routes.draw do api_path = Rails.configuration.api_path + match api_path + '/translations/push', to: 'translations#push', via: :put + match api_path + '/translations/sync', to: 'translations#sync', via: :post + match api_path + '/translations/reset', to: 'translations#reset', via: :post + match api_path + '/translations/lang/:locale', to: 'translations#load', via: :get + match api_path + '/translations/admin/lang/:locale', to: 'translations#admin', via: :get + match api_path + '/translations', to: 'translations#index', via: :get match api_path + '/translations/:id', to: 'translations#show', via: :get match api_path + '/translations', to: 'translations#create', via: :post match api_path + '/translations/:id', to: 'translations#update', via: :put match api_path + '/translations/:id', to: 'translations#destroy', via: :delete - match api_path + '/translations/lang/:locale', to: 'translations#load', via: :get - match api_path + '/translations/admin/lang/:locale', to: 'translations#admin', via: :get end diff --git a/db/migrate/20150628000001_translator_key_add.rb b/db/migrate/20150628000001_translator_key_add.rb new file mode 100644 index 000000000..50e1457c9 --- /dev/null +++ b/db/migrate/20150628000001_translator_key_add.rb @@ -0,0 +1,16 @@ +class TranslatorKeyAdd < ActiveRecord::Migration + def up + Setting.create_if_not_exists( + title: 'Define translator identifier.', + name: 'translator_key', + area: 'i18n::translator_key', + description: 'Defines the translator identifier for contributions.', + options: {}, + state: '', + frontend: false + ) + end + + def down + end +end