Improved admin interface for translations.

This commit is contained in:
Martin Edenhofer 2015-04-16 08:49:31 +02:00
parent c1d700c82e
commit 466bde7feb
6 changed files with 234 additions and 27 deletions

View file

@ -1,8 +1,4 @@
class Index extends App.ControllerContent class Index extends App.ControllerContent
events:
'blur input': 'update'
'click .js-Reset': 'reset'
constructor: -> constructor: ->
super super
@ -12,20 +8,141 @@ class Index extends App.ControllerContent
@title 'Translations' @title 'Translations'
@render() @render()
render: =>
@html App.view('translation/index')()
configure_attributes = [
{ name: 'locale', display: '', tag: 'select', null: false, class: 'input', options: { de: 'Deutsch', en: 'English (United States)', 'en-CA': 'English (Canada)', 'en-GB': 'English (United Kingdom)' }, default: App.i18n.get() },
]
load = (params) =>
new TranslationToDo(
el: @$('.js-ToDo')
locale: params.locale
)
new TranslationList(
el: @$('.js-List')
locale: params.locale
)
new App.ControllerForm(
el: @$('.language')
model: { configure_attributes: configure_attributes }
autofocus: false
handlers: [load]
)
release: =>
App.Event.trigger('ui:rerender')
class TranslationToDo extends App.Controller
events:
'click .js-Create': 'create'
'click .js-TheSame': 'same'
constructor: ->
super
@render()
@bind(
'i18n:translation_todo_reload',
=>
@render()
)
render: =>
listNotTranslated = []
for key, value of App.i18n.getNotTranslated(@locale)
item = [ '', key, '', '']
listNotTranslated.push item
@html App.view('translation/todo')(
list: listNotTranslated
)
create: (e) =>
e.preventDefault()
field = $(e.target).closest('tr').find('.js-Item')
source = field.data('source')
target = field.val()
# remove from not translated list
$(e.target).closest('tr').remove()
# local update
App.i18n.removeNotTranslated( @locale, source )
App.i18n.setMap( source, target )
# remote update
params =
locale: @locale
source: source
target: target
target_initial: ''
@ajax(
id: 'translations'
type: 'POST'
url: @apiPath + '/translations'
data: JSON.stringify(params)
processData: false
success: (data, status, xhr) =>
App.Event.trigger('i18n:translation_list_reload')
)
same: (e) =>
e.preventDefault()
field = $(e.target).closest('tr').find('.js-Item')
source = field.data('source')
# remove from not translated list
$(e.target).closest('tr').remove()
# local update
App.i18n.removeNotTranslated( @locale, source )
App.i18n.setMap( source, source )
# remote update
params =
locale: @locale
source: source
target: source
target_initial: ''
@ajax(
id: 'translations'
type: 'POST'
url: @apiPath + '/translations'
data: JSON.stringify(params)
processData: false
success: (data, status, xhr) =>
App.Event.trigger('i18n:translation_list_reload')
)
class TranslationList extends App.Controller
events:
'blur .js-translated input': 'update'
'click .js-translated .js-Reset': 'reset'
constructor: ->
super
@load() @load()
@bind(
'i18n:translation_list_reload',
=>
@load()
)
load: => load: =>
@ajax( @ajax(
id: 'translations_admin' id: 'translations_admin'
type: 'GET' type: 'GET'
url: @apiPath + '/translations/admin/lang/de' url: @apiPath + "/translations/admin/lang/#{@locale}"
processData: true processData: true
success: (data, status, xhr) => success: (data, status, xhr) =>
@render(data) @render(data)
) )
render: (data = {}) => render: (data = {}) =>
@html App.view('translation')( @html App.view('translation/list')(
list: data.list list: data.list
timestampFormat: data.timestampFormat timestampFormat: data.timestampFormat
dateFormat: data.dateFormat dateFormat: data.dateFormat
@ -40,7 +157,31 @@ class Index extends App.ControllerContent
e.preventDefault() e.preventDefault()
field = $(e.target).closest('tr').find('.js-Item') field = $(e.target).closest('tr').find('.js-Item')
id = field.data('id') id = field.data('id')
source = field.data('source')
initial = field.data('initial') initial = field.data('initial')
# if it's translated by user it self, delete it
if !initial || initial is ''
$(e.target).closest('tr').remove()
App.i18n.setMap( source, '' )
params =
id: id
@ajax(
id: 'translations'
type: 'DELETE'
url: @apiPath + '/translations/' + id
data: JSON.stringify(params)
processData: false
success: =>
console.log('aaa', @locale, source)
App.i18n.setNotTranslated( @locale, source )
App.Event.trigger('i18n:translation_todo_reload')
)
return
# update item
App.i18n.setMap( source, initial )
field.val( initial ) field.val( initial )
@updateRow(id) @updateRow(id)
params = params =
@ -58,8 +199,14 @@ class Index extends App.ControllerContent
update: (e) -> update: (e) ->
e.preventDefault() e.preventDefault()
id = $( e.target ).data('id') id = $( e.target ).data('id')
source = $( e.target ).data('source')
target = $( e.target ).val() target = $( e.target ).val()
@updateRow(id) @updateRow(id)
# local update
App.i18n.setMap( source, target )
# remote update
params = params =
id: id id: id
target: target target: target
@ -84,5 +231,4 @@ class Index extends App.ControllerContent
reset.hide() reset.hide()
reset.closest('tr').removeClass('warning') reset.closest('tr').removeClass('warning')
App.Config.set( 'Translation', { prio: 1800, parent: '#system', name: 'Translations', target: '#system/translation', controller: Index, role: ['Admin'] }, 'NavBarAdmin' ) App.Config.set( 'Translation', { prio: 1800, parent: '#system', name: 'Translations', target: '#system/translation', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )

View file

@ -6,44 +6,65 @@ class App.i18n
@translateContent: ( string, args... ) -> @translateContent: ( string, args... ) ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton _instance ?= new _i18nSingleton()
_instance.translateContent( string, args ) _instance.translateContent( string, args )
@translatePlain: ( string, args... ) -> @translatePlain: ( string, args... ) ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton _instance ?= new _i18nSingleton()
_instance.translatePlain( string, args ) _instance.translatePlain( string, args )
@translateInline: ( string, args... ) -> @translateInline: ( string, args... ) ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton _instance ?= new _i18nSingleton()
_instance.translateInline( string, args ) _instance.translateInline( string, args )
@translateTimestamp: ( args, offset = 0 ) -> @translateTimestamp: ( args, offset = 0 ) ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton _instance ?= new _i18nSingleton()
_instance.timestamp( args, offset ) _instance.timestamp( args, offset )
@translateDate: ( args, offset = 0 ) -> @translateDate: ( args, offset = 0 ) ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton _instance ?= new _i18nSingleton()
_instance.date( args, offset ) _instance.date( args, offset )
@get: -> @get: ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton _instance ?= new _i18nSingleton()
_instance.get() _instance.get()
@set: ( args ) -> @set: ( args ) ->
if _instance == undefined if _instance == undefined
_instance ?= new _i18nSingleton( args ) _instance ?= new _i18nSingleton()
_instance.set( args ) _instance.set( args )
@setMap: (source, target) ->
if _instance == undefined
_instance ?= new _i18nSingleton()
_instance.setMap( source, target )
@getNotTranslated: (locale) ->
if _instance == undefined
_instance ?= new _i18nSingleton()
_instance.getNotTranslated( locale )
@removeNotTranslated: (locale, key) ->
if _instance == undefined
_instance ?= new _i18nSingleton()
_instance.removeNotTranslated( locale, key )
@setNotTranslated: (locale, key) ->
if _instance == undefined
_instance ?= new _i18nSingleton()
_instance.setNotTranslated( locale, key )
class _i18nSingleton extends Spine.Module class _i18nSingleton extends Spine.Module
@include App.LogInclude @include App.LogInclude
constructor: ( locale ) -> constructor: ( locale ) ->
@map = {} @map = {}
@_notTranslated = {}
@dateFormat = 'yyyy-mm-dd' @dateFormat = 'yyyy-mm-dd'
@timestampFormat = 'yyyy-mm-dd HH:MM' @timestampFormat = 'yyyy-mm-dd HH:MM'
@ -165,6 +186,9 @@ class _i18nSingleton extends Spine.Module
@_translated = false @_translated = false
translated = string translated = string
if App.Config.get('developer_mode') is true if App.Config.get('developer_mode') is true
if !@_notTranslated[@locale]
@_notTranslated[@locale] = {}
@_notTranslated[@locale][string] = true
@log 'notice', "translation for '#{string}' in '#{@locale}' is missing" @log 'notice', "translation for '#{string}' in '#{@locale}' is missing"
# search %s # search %s
@ -176,6 +200,18 @@ class _i18nSingleton extends Spine.Module
# return translated string # return translated string
return translated return translated
setMap: ( source, target ) =>
@map[source] = target
getNotTranslated: ( locale ) =>
@_notTranslated[locale || @locale]
removeNotTranslated: ( locale, key ) =>
delete @_notTranslated[locale][key]
setNotTranslated: ( locale, key ) =>
@_notTranslated[locale][key] = true
date: ( time, offset ) => date: ( time, offset ) =>
@convert(time, offset, @dateFormat) @convert(time, offset, @dateFormat)

View file

@ -0,0 +1,10 @@
<div class="horizontal">
<div class="page-header-title">
<h1><%- @T('Translations') %> <small></small></h1>
</div>
<div class="page-header-meta">
<div class="language"></div>
</div>
</div>
<div class="js-ToDo"></div>
<div class="js-List"></div>

View file

@ -1,6 +1,3 @@
<div class="page-header-title">
<h1><%- @T('Translations') %><small></small></h1>
</div>
<h2><%- @T('Date & Datetime') %></h2> <h2><%- @T('Date & Datetime') %></h2>
<table class="translationOverview table table-striped table-hover"> <table class="translationOverview table table-striped table-hover">
<thead> <thead>
@ -28,7 +25,7 @@
</table> </table>
<h2><%- @T('Words') %></h2> <h2><%- @T('Words') %></h2>
<table class="translationOverview table table-striped table-hover"> <table class="translationOverview js-translated table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th class="translationOverview-source"><%- @T('Source') %></th> <th class="translationOverview-source"><%- @T('Source') %></th>
@ -42,7 +39,7 @@
<% for item in @list: %> <% for item in @list: %>
<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-initial="<%= item[3] %>" data-id="<%= item[0] %>"></td> <td class="translationOverview-itemContainer"><input class="js-Item translationOverview-item form-control" value="<%= item[2] %>" data-source="<%= item[1] %>" data-initial="<%= item[3] %>" data-id="<%= item[0] %>"></td>
<td title="<%= item[3] %>"><%= item[3]%></td> <td title="<%= item[3] %>"><%= item[3]%></td>
<td><a href="#" class="js-Reset"><%- @T('Reset') %></a></td> <td><a href="#" class="js-Reset"><%- @T('Reset') %></a></td>
</tr> </tr>

View file

@ -0,0 +1,21 @@
<h2><%- @T('Words') %> <small><%- @T('not translated') %></small></h2>
<table class="translationOverview table table-striped table-hover">
<thead>
<tr>
<th class="translationOverview-source"><%- @T('Source') %></th>
<th class="translationOverview-target"><%- @T('Target') %></th>
<th class="translationOverview-action"><%- @T('Action') %></th>
</tr>
</thead>
<tbody>
<% if @list: %>
<% for item in @list: %>
<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>
</tr>
<% end %>
<% end %>
</tbody>
</table>

View file

@ -5282,9 +5282,6 @@ label + .wizard-buttonList {
.translationOverview-initial { .translationOverview-initial {
width: 25%; width: 25%;
} }
.translationOverview-action {
width: 15%;
}
.translationOverview-item { .translationOverview-item {
width: 100%; width: 100%;
} }