Improved admin translation interface.
This commit is contained in:
parent
423c7428a7
commit
436ddad323
7 changed files with 227 additions and 39 deletions
|
@ -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')
|
||||
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,10 +253,6 @@ class TranslationList extends App.Controller
|
|||
)
|
||||
|
||||
render: (data = {}) =>
|
||||
|
||||
#if !App.i18n.notTranslatedFeatureEnabled(@locale)
|
||||
# return
|
||||
|
||||
@strings = []
|
||||
@times = []
|
||||
for item in data.list
|
||||
|
@ -176,13 +266,24 @@ 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()
|
||||
@hasChanges = true
|
||||
field = $(e.target).closest('tr').find('.js-Item')
|
||||
id = field.data('id')
|
||||
source = field.data('source')
|
||||
|
@ -238,6 +339,7 @@ class TranslationList extends App.Controller
|
|||
|
||||
update: (e) ->
|
||||
e.preventDefault()
|
||||
@hasChanges = true
|
||||
id = $( e.target ).data('id')
|
||||
source = $( e.target ).data('source')
|
||||
format = $( e.target ).data('format')
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
db/migrate/20150628000001_translator_key_add.rb
Normal file
16
db/migrate/20150628000001_translator_key_add.rb
Normal 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
|
Loading…
Reference in a new issue