Improved admin translation interface.

This commit is contained in:
Martin Edenhofer 2015-06-28 02:16:47 +02:00
parent 423c7428a7
commit 436ddad323
7 changed files with 227 additions and 39 deletions

View file

@ -1,4 +1,9 @@
class Index extends App.ControllerContent class Index extends App.ControllerContent
events:
'click .js-pushChanges': 'pushChanges'
'click .js-resetChanges': 'resetChanges'
'click .js-syncChanges': 'syncChanges'
constructor: -> constructor: ->
super 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() }, { name: 'locale', display: '', tag: 'select', null: false, class: 'input', options: options, default: App.i18n.get() },
] ]
load = (params) => load = (params) =>
new TranslationToDo( @translationToDo = new TranslationToDo(
el: @$('.js-ToDo') el: @$('.js-ToDo')
locale: params.locale locale: params.locale
) )
new TranslationList( @translationList = new TranslationList(
el: @$('.js-List') el: @$('.js-List')
locale: params.locale locale: params.locale
) )
@ -38,12 +43,90 @@ class Index extends App.ControllerContent
release: => release: =>
rerender = -> rerender = ->
App.Event.trigger('ui: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 class TranslationToDo extends App.Controller
hasChanges: false
events: events:
'click .js-Create': 'create' 'click .js-create': 'create'
'click .js-TheSame': 'same' 'click .js-theSame': 'same'
constructor: -> constructor: ->
super super
@ -69,8 +152,16 @@ class TranslationToDo extends App.Controller
list: listNotTranslated list: listNotTranslated
) )
showAction: =>
@el.closest('.content').find('.js-changes').removeClass('hidden')
changes: =>
@hasChanges
create: (e) => create: (e) =>
e.preventDefault() e.preventDefault()
@hasChanges = true
@showAction()
field = $(e.target).closest('tr').find('.js-Item') field = $(e.target).closest('tr').find('.js-Item')
source = field.data('source') source = field.data('source')
target = field.val() target = field.val()
@ -104,6 +195,8 @@ class TranslationToDo extends App.Controller
same: (e) => same: (e) =>
e.preventDefault() e.preventDefault()
@hasChanges = true
@showAction()
field = $(e.target).closest('tr').find('.js-Item') field = $(e.target).closest('tr').find('.js-Item')
source = field.data('source') source = field.data('source')
@ -135,6 +228,7 @@ class TranslationToDo extends App.Controller
) )
class TranslationList extends App.Controller class TranslationList extends App.Controller
hasChanges: false
events: events:
'blur .js-translated input': 'update' 'blur .js-translated input': 'update'
'click .js-translated .js-Reset': 'reset' 'click .js-translated .js-Reset': 'reset'
@ -159,12 +253,8 @@ class TranslationList extends App.Controller
) )
render: (data = {}) => render: (data = {}) =>
#if !App.i18n.notTranslatedFeatureEnabled(@locale)
# return
@strings = [] @strings = []
@times = [] @times = []
for item in data.list for item in data.list
if item[4] is 'time' if item[4] is 'time'
@times.push item @times.push item
@ -176,18 +266,29 @@ class TranslationList extends App.Controller
strings: @strings strings: @strings
) )
ui = @ ui = @
changesAvailable = false
@$('.js-Item').each( (e) -> @$('.js-Item').each( (e) ->
id = $(this).data('id') id = $(this).data('id')
ui.updateRow(id) ui.updateRow(id)
changesAvailable = true
) )
if changesAvailable
@showAction()
showAction: =>
@el.closest('.content').find('.js-changes').removeClass('hidden')
changes: =>
@hasChanges
reset: (e) -> reset: (e) ->
e.preventDefault() e.preventDefault()
field = $(e.target).closest('tr').find('.js-Item') @hasChanges = true
id = field.data('id') field = $(e.target).closest('tr').find('.js-Item')
source = field.data('source') id = field.data('id')
initial = field.data('initial') source = field.data('source')
format = field.data('format') initial = field.data('initial')
format = field.data('format')
# if it's translated by user it self, delete it # if it's translated by user it self, delete it
if !initial || initial is '' if !initial || initial is ''
@ -238,10 +339,11 @@ class TranslationList extends App.Controller
update: (e) -> update: (e) ->
e.preventDefault() e.preventDefault()
id = $( e.target ).data('id') @hasChanges = true
source = $( e.target ).data('source') id = $( e.target ).data('id')
format = $( e.target ).data('format') source = $( e.target ).data('source')
target = $( e.target ).val() format = $( e.target ).data('format')
target = $( e.target ).val()
# local update # local update
@updateRow(id) @updateRow(id)

View file

@ -3,6 +3,9 @@
<h1><%- @T('Translations') %> <small></small></h1> <h1><%- @T('Translations') %> <small></small></h1>
</div> </div>
<div class="page-header-meta"> <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 class="language"></div>
</div> </div>
</div> </div>

View file

@ -13,7 +13,7 @@
<tr> <tr>
<td title="<%= item[1] %>"><%= item[1] %></td> <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 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> </tr>
<% end %> <% end %>
<% end %> <% end %>

View file

@ -8,6 +8,31 @@ class TranslationsController < ApplicationController
render json: Translation.list( params[:locale] ) render json: Translation.list( params[:locale] )
end 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 # GET /translations/admin/lang/:locale
def admin def admin
return if deny_if_not_role(Z_ROLENAME_ADMIN) return if deny_if_not_role(Z_ROLENAME_ADMIN)
@ -16,11 +41,13 @@ class TranslationsController < ApplicationController
# GET /translations # GET /translations
def index def index
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_index_render(Translation, params) model_index_render(Translation, params)
end end
# GET /translations/1 # GET /translations/1
def show def show
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_show_render(Translation, params) model_show_render(Translation, params)
end end

View file

@ -72,22 +72,54 @@ push translations to online
} }
return true if translations_to_push.empty? return true if translations_to_push.empty?
#return translations_to_push
url = 'https://i18n.zammad.com/api/v1/thanks_for_your_support' url = 'https://i18n.zammad.com/api/v1/thanks_for_your_support'
translator_key = Setting.get('translator_key')
result = UserAgent.post( result = UserAgent.post(
url, url,
{ {
locale: locale, locale: locale,
translations: translations_to_push, translations: translations_to_push,
fqdn: Setting.get('fqdn'), fqdn: Setting.get('fqdn'),
translator_key: '', translator_key: translator_key,
}, },
{ {
json: true, json: true,
} }
) )
fail "Can't push translations to #{url}: #{result.error}" if !result.success? 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 true
end end
@ -101,43 +133,47 @@ get list of translations
def self.list(locale, admin = false) def self.list(locale, admin = false)
# check cache # use cache if not admin page is requested
if !admin if !admin
list = cache_get( locale ) data = cache_get(locale)
end end
if !list if !data
# show total translations as reference count
data = {
'total' => Translation.where(locale: 'de-de').count,
}
list = [] list = []
translations = Translation.where( locale: locale.downcase ).order( :source ) translations = Translation.where(locale: locale.downcase).order(:source)
translations.each { |item| translations.each { |item|
if admin if admin
data = [ translation_item = [
item.id, item.id,
item.source, item.source,
item.target, item.target,
item.target_initial, item.target_initial,
item.format, item.format,
] ]
list.push data list.push translation_item
else else
data = [ translation_item = [
item.id, item.id,
item.source, item.source,
item.target, item.target,
item.format, item.format,
] ]
list.push data list.push translation_item
end end
data['list'] = list
} }
# set cache # set cache
if !admin if !admin
cache_set( locale, list ) cache_set(locale, data)
end end
end end
{ data
list: list,
}
end end
=begin =begin
@ -174,12 +210,12 @@ translate strings in ruby context, e. g. for notifications
end end
def cache_clear def cache_clear
Cache.delete( 'Translation::' + locale.downcase ) Cache.delete( 'TranslationMap::' + locale.downcase )
end end
def self.cache_set(locale, data) def self.cache_set(locale, data)
Cache.write( 'Translation::' + locale.downcase, data ) Cache.write( 'TranslationMap::' + locale.downcase, data )
end end
def self.cache_get(locale) def self.cache_get(locale)
Cache.get( 'Translation::' + locale.downcase ) Cache.get( 'TranslationMap::' + locale.downcase )
end end
end end

View file

@ -1,12 +1,16 @@
Zammad::Application.routes.draw do Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path 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', to: 'translations#index', via: :get
match api_path + '/translations/:id', to: 'translations#show', 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', to: 'translations#create', via: :post
match api_path + '/translations/:id', to: 'translations#update', via: :put match api_path + '/translations/:id', to: 'translations#update', via: :put
match api_path + '/translations/:id', to: 'translations#destroy', via: :delete 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 end

View file

@ -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