Browse Source

Improved admin translation interface.

Martin Edenhofer 9 years ago
parent
commit
436ddad323

+ 121 - 19
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)

+ 3 - 0
app/assets/javascripts/app/views/translation/index.jst.eco

@@ -3,6 +3,9 @@
     <h1><%- @T('Translations') %> <small></small></h1>
   </div>
   <div class="page-header-meta">
+    <a class="btn btn--success js-syncChanges"><%- @T('Sync with latest') %></a>
+    <a class="btn btn--danger hidden js-changes js-resetChanges"><%- @T('Reset Changes') %></a>
+    <a class="btn btn--primary hidden js-changes js-pushChanges"><%- @T('Push Changes') %></a>
     <div class="language"></div>
   </div>
 </div>

+ 1 - 1
app/assets/javascripts/app/views/translation/todo.jst.eco

@@ -13,7 +13,7 @@
       <tr>
         <td title="<%= item[1] %>"><%= item[1] %></td>
         <td class="translationOverview-itemContainer"><input class="js-Item translationOverview-item form-control" value="<%= item[2] %>" data-source="<%= item[1] %>"></td>
-        <td><a href="#" class="js-Create"><%- @T('Create') %></a> / <a href="#" class="js-TheSame"><%- @T('is the same') %></a></td>
+        <td><a href="#" class="js-create"><%- @T('Create') %></a> / <a href="#" class="js-theSame"><%- @T('is the same') %></a></td>
       </tr>
       <% end %>
     <% end %>

+ 27 - 0
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
 

+ 53 - 17
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

+ 6 - 2
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

+ 16 - 0
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