i18n_admin 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +23 -0
- data/app/assets/images/i18n_admin/spinner.gif +0 -0
- data/app/assets/javascripts/i18n_admin/application.js +21 -0
- data/app/assets/javascripts/i18n_admin/src/i18n-admin-job-monitor.coffee +37 -0
- data/app/assets/javascripts/i18n_admin/src/import.coffee +23 -0
- data/app/assets/javascripts/i18n_admin/src/translation.coffee +45 -0
- data/app/assets/stylesheets/i18n_admin/application.css.scss +88 -0
- data/app/controllers/i18n_admin/application_controller.rb +21 -0
- data/app/controllers/i18n_admin/exports_controller.rb +19 -0
- data/app/controllers/i18n_admin/imports_controller.rb +28 -0
- data/app/controllers/i18n_admin/translations_controller.rb +30 -0
- data/app/helpers/i18n_admin/application_helper.rb +40 -0
- data/app/models/i18n_admin/export_file.rb +4 -0
- data/app/models/i18n_admin/import_file.rb +4 -0
- data/app/models/i18n_admin/import_job.rb +4 -0
- data/app/models/i18n_admin/resource_file.rb +8 -0
- data/app/models/i18n_admin/translations_set.rb +5 -0
- data/app/models/i18n_admin/whitelisted_resource.rb +9 -0
- data/app/views/i18n_admin/exports/queued.html.haml +18 -0
- data/app/views/i18n_admin/imports/errors/_resource_invalid.html.erb +0 -0
- data/app/views/i18n_admin/imports/errors/_resource_not_found.html.erb +0 -0
- data/app/views/i18n_admin/imports/new.html.erb +61 -0
- data/app/views/i18n_admin/imports/processing.html.erb +33 -0
- data/app/views/i18n_admin/imports/queued.html.haml +0 -0
- data/app/views/i18n_admin/translations/_pagination.html.erb +3 -0
- data/app/views/i18n_admin/translations/_translation.html.erb +9 -0
- data/app/views/i18n_admin/translations/index.html.erb +81 -0
- data/app/views/layouts/i18n_admin/application.html.haml +11 -0
- data/config/locales/i18n_admin.en.yml +38 -0
- data/config/locales/i18n_admin.fr.yml +52 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20140829094415_create_i18n_admin_translations_sets.rb +12 -0
- data/db/migrate/20150825133437_create_i18n_admin_import_jobs.rb +11 -0
- data/db/migrate/20150828074645_create_i18n_admin_whitelisted_resources.rb +12 -0
- data/db/migrate/20160404161658_create_i18n_admin_resource_files.rb +11 -0
- data/lib/ext/paperclip.rb +5 -0
- data/lib/generators/i18n_admin/install/install_generator.rb +31 -0
- data/lib/generators/i18n_admin/install/templates/initializer.rb +28 -0
- data/lib/i18n_admin.rb +51 -0
- data/lib/i18n_admin/engine.rb +28 -0
- data/lib/i18n_admin/errors.rb +10 -0
- data/lib/i18n_admin/errors/base.rb +23 -0
- data/lib/i18n_admin/errors/collection.rb +24 -0
- data/lib/i18n_admin/errors/resource_invalid.rb +7 -0
- data/lib/i18n_admin/errors/resource_not_found.rb +7 -0
- data/lib/i18n_admin/export.rb +14 -0
- data/lib/i18n_admin/export/base.rb +129 -0
- data/lib/i18n_admin/export/xls.rb +110 -0
- data/lib/i18n_admin/hstore_backend.rb +68 -0
- data/lib/i18n_admin/import.rb +16 -0
- data/lib/i18n_admin/import/base.rb +64 -0
- data/lib/i18n_admin/import/job.rb +17 -0
- data/lib/i18n_admin/import/xls.rb +58 -0
- data/lib/i18n_admin/request_store.rb +11 -0
- data/lib/i18n_admin/translation.rb +27 -0
- data/lib/i18n_admin/translation_collection.rb +34 -0
- data/lib/i18n_admin/translations.rb +96 -0
- data/lib/i18n_admin/version.rb +3 -0
- data/lib/tasks/i18n_admin_tasks.rake +35 -0
- metadata +307 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 02ae7e131829c4fa8de58cc8442a66c4469849ef
|
4
|
+
data.tar.gz: 00483d502876735d4062205d0c671255cbb2216c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f6f60e39ca4eb851df17d9a38fcac50df910c9c1b071739a29a65ae067abc1b026ceeb123124c7157d82642433ddc1ca9fa9843f405f9a604aec9b7a99d7c056
|
7
|
+
data.tar.gz: 52c1b20386afbcd82178981210e088ea07a4b08476f935f063a9c082fdc929915c5bbc05847be3778249fca79164a9d2458715403ef842d12a54920747e7bbd6
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# I18nAdmin
|
2
|
+
|
3
|
+
Admin panel to translate Rails apps with `I18n::Backend::KeyValue` stores.
|
4
|
+
|
5
|
+
**Warning : Only PostgreSQL is supported due to HSTORE field type usage to store translations**
|
6
|
+
|
7
|
+
It works by creating a I18n backend chain and plugging a custom backend before
|
8
|
+
the original YAML backend.
|
9
|
+
|
10
|
+
When you translate a key, it is stored in a `HSTORE` field, with one database
|
11
|
+
row per locale. This allows for fast translations fetching on request start.
|
12
|
+
|
13
|
+
The translations are then merged with the ones existing in the YAML, and cached
|
14
|
+
for the request duration using the
|
15
|
+
[RequestStore](https://github.com/steveklabnik/request_store) gem.
|
16
|
+
|
17
|
+
## Install
|
18
|
+
|
19
|
+
Add to your Gemfile and bundle :
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'i18n_admin', github: 'glyph-fr/i18n_admin'
|
23
|
+
```
|
24
|
+
|
25
|
+
Run the install generator which will mount the engine and create an initializer
|
26
|
+
template to configure the gem, and create necessary migration :
|
27
|
+
|
28
|
+
```bash
|
29
|
+
rails generate i18n_admin:install
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
I18nAdmin gives you an admin panel to translate your static contents.
|
35
|
+
It's accessible at the Engine mount point that you choosed when running
|
36
|
+
the install generator.
|
37
|
+
|
38
|
+
The default URL is `/i18n-admin`
|
39
|
+
|
40
|
+
### Authentication
|
41
|
+
|
42
|
+
To plug the engine with your authentication system, you can pass a method
|
43
|
+
name to execute in a `before_action` filter in the I18nAdmin controllers.
|
44
|
+
|
45
|
+
This can be done in the initializer file generated at
|
46
|
+
`config/initializers/i18n_admin.rb` :
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
I18nAdmin.config do |config|
|
50
|
+
config.authentication_method = :authenticate_user!
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
### Excluding keys from the admin
|
55
|
+
|
56
|
+
To avoid some keys to be displayed in the translation admin, you can use a
|
57
|
+
Regex in the initializer file. The regex is matched against the whole key path
|
58
|
+
(e.g. "time.formats.short")
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
I18nAdmin.config do |config|
|
62
|
+
# Exclude all date and time formats from translations
|
63
|
+
config.excluded_keys_pattern = /^(date|time)/
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
## UI locale
|
68
|
+
|
69
|
+
The locale is defined from the rails' `I18n.locale`, which is automatically
|
70
|
+
added in the `<html>` tag `lang` attribute.
|
71
|
+
|
72
|
+
The JS app then taps into this locale to set its default locale and allow
|
73
|
+
displaying side-by-side the content with the default locale and the current
|
74
|
+
locale's translation for the key
|
75
|
+
|
76
|
+
## Licence
|
77
|
+
|
78
|
+
This project rocks and uses MIT-LICENSE.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'I18nAdmin'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
Binary file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require jquery.autoGrow
|
16
|
+
//= require turbolinks
|
17
|
+
//= require bootstrap
|
18
|
+
//= require nprogress
|
19
|
+
//= require nprogress-turbolinks
|
20
|
+
//= require sidekiq-job_monitor
|
21
|
+
//= require_tree ./src
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class I18nAdminJobMonitor
|
2
|
+
constructor: (@$el) ->
|
3
|
+
@initializeSidekiqJobMonitor()
|
4
|
+
|
5
|
+
initializeSidekiqJobMonitor: ->
|
6
|
+
@$el.sidekiqJobMonitor(onStart: @onMonitoringStart)
|
7
|
+
|
8
|
+
onMonitoringStart: (monitor) =>
|
9
|
+
@monitor = monitor
|
10
|
+
@$modal = monitor.$el
|
11
|
+
@$modal.appendTo('body').modal()
|
12
|
+
@$modal.on('complete', @onMonitoringComplete)
|
13
|
+
@$modal.on('hide.bs.modal', @onModalHide)
|
14
|
+
@$modal.on('failed', @hideModal)
|
15
|
+
@$modal.on('click', '[data-cancel="job"]', @cancelClicked)
|
16
|
+
|
17
|
+
onMonitoringComplete: (e, monitor, data) =>
|
18
|
+
@$modal.modal('hide')
|
19
|
+
window.location.href = data.url
|
20
|
+
|
21
|
+
onModalHide: (e) =>
|
22
|
+
@cancel()
|
23
|
+
|
24
|
+
cancelClicked: =>
|
25
|
+
@hideModal()
|
26
|
+
|
27
|
+
hideModal: =>
|
28
|
+
@$modal?.modal('hide')
|
29
|
+
|
30
|
+
cancel: ->
|
31
|
+
@monitor.stopMonitoring()
|
32
|
+
@monitor.cancelJob()
|
33
|
+
|
34
|
+
|
35
|
+
$(document).on 'page:change', ->
|
36
|
+
$('[data-export-link][data-remote]').each (i, el) ->
|
37
|
+
new I18nAdminJobMonitor($(el))
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class ImportProgress
|
2
|
+
constructor: (@$el) ->
|
3
|
+
@checkURL = @$el.data('check-url')
|
4
|
+
@checkProgress()
|
5
|
+
|
6
|
+
checkProgress: ->
|
7
|
+
$.getJSON(@checkURL).then (job) =>
|
8
|
+
switch job.state
|
9
|
+
when 'pending' then setTimeout($.proxy(@checkProgress, this), 1000)
|
10
|
+
when 'success' then @handleSuccess(job)
|
11
|
+
when 'error' then @handleError(job)
|
12
|
+
|
13
|
+
handleSuccess: (job) ->
|
14
|
+
$('[data-status="pending"]').addClass('hidden')
|
15
|
+
$('[data-status="success"]').removeClass('hidden')
|
16
|
+
|
17
|
+
handleError: (job) ->
|
18
|
+
$('[data-status="pending"]').addClass('hidden')
|
19
|
+
$('[data-status="error"]').removeClass('hidden')
|
20
|
+
|
21
|
+
$(document).on 'page:change', ->
|
22
|
+
if ($importProgress = $('[data-processing-import]')).length
|
23
|
+
new ImportProgress($importProgress)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Translation
|
2
|
+
constructor: (@$el) ->
|
3
|
+
@initialize()
|
4
|
+
|
5
|
+
initialize: ->
|
6
|
+
@$form = @$el.find('[data-translation-form]')
|
7
|
+
@$field = @$el.find('[data-translation-field]')
|
8
|
+
@currentValue = @$field.val()
|
9
|
+
|
10
|
+
@$field.on('blur', $.proxy(@updateTranslation, this))
|
11
|
+
@$form.on('ajax:beforeSend', $.proxy(@onBeforeSend, this))
|
12
|
+
@$form.on('ajax:complete', $.proxy(@onSaveComplete, this))
|
13
|
+
@$form.on('ajax:success', $.proxy(@translationSaved, this))
|
14
|
+
|
15
|
+
@$field.autoGrow()
|
16
|
+
|
17
|
+
updateTranslation: ->
|
18
|
+
if (newValue = @$field.val()) != @currentValue
|
19
|
+
@currentValue = newValue
|
20
|
+
@$form.submit()
|
21
|
+
|
22
|
+
translationSaved: (e, response) ->
|
23
|
+
@onSaveComplete()
|
24
|
+
|
25
|
+
$newMarkup = $(response)
|
26
|
+
@$el.html($newMarkup.html())
|
27
|
+
|
28
|
+
@initialize()
|
29
|
+
@showSuccess()
|
30
|
+
|
31
|
+
onBeforeSend: ->
|
32
|
+
NProgress.start()
|
33
|
+
@$el.addClass('loading')
|
34
|
+
|
35
|
+
onSaveComplete: ->
|
36
|
+
NProgress.done()
|
37
|
+
@$el.removeClass('loading')
|
38
|
+
|
39
|
+
showSuccess: ->
|
40
|
+
@$field.addClass('success')
|
41
|
+
setTimeout (=> @$field.removeClass('success')), 1000
|
42
|
+
|
43
|
+
$(document).on 'page:change', ->
|
44
|
+
if ($translations = $('[data-translation]')).length
|
45
|
+
$translations.each (i, el) -> new Translation($(el))
|
@@ -0,0 +1,88 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require nprogress
|
15
|
+
*= require nprogress-bootstrap
|
16
|
+
*= require_self
|
17
|
+
*/
|
18
|
+
|
19
|
+
@import "bootstrap";
|
20
|
+
|
21
|
+
body {
|
22
|
+
overflow: scroll;
|
23
|
+
}
|
24
|
+
|
25
|
+
.pagination {
|
26
|
+
margin-top: 0;
|
27
|
+
}
|
28
|
+
|
29
|
+
.panel-title {
|
30
|
+
font-size: 2em;
|
31
|
+
}
|
32
|
+
|
33
|
+
.panel-heading h2 {
|
34
|
+
font-size: 1em;
|
35
|
+
margin-top: 0;
|
36
|
+
}
|
37
|
+
|
38
|
+
.panel-default > .panel-heading.panel-subheading,
|
39
|
+
.panel-default > .panel-footer {
|
40
|
+
background: #fcfcfc;
|
41
|
+
}
|
42
|
+
|
43
|
+
.panel-subheading .pagination-container .text-center {
|
44
|
+
text-align: left;
|
45
|
+
}
|
46
|
+
|
47
|
+
.pagination-container .pagination {
|
48
|
+
margin-bottom: 0px;
|
49
|
+
}
|
50
|
+
|
51
|
+
.pagination-column {
|
52
|
+
border-right: 1px solid #ccc;
|
53
|
+
}
|
54
|
+
|
55
|
+
.table > tbody > tr > td.input-cell {
|
56
|
+
padding: 0;
|
57
|
+
}
|
58
|
+
|
59
|
+
.translation-input {
|
60
|
+
min-width: 400px;
|
61
|
+
|
62
|
+
&.success {
|
63
|
+
background: #DFF0D8;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
td.input-cell textarea {
|
68
|
+
border-radius: 0;
|
69
|
+
border: 0;
|
70
|
+
box-shadow: 1px 2px 5px #f0f0f0 inset;
|
71
|
+
}
|
72
|
+
|
73
|
+
td.input-cell textarea:focus {
|
74
|
+
box-shadow: 1px 2px 5px #ccc inset;
|
75
|
+
}
|
76
|
+
|
77
|
+
.translation-row {
|
78
|
+
&.loading {
|
79
|
+
opacity: 0.5;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
.translation-key-cell {
|
84
|
+
max-width: 100px;
|
85
|
+
text-overflow: ellipsis;
|
86
|
+
overflow: hidden;
|
87
|
+
}
|
88
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module I18nAdmin
|
2
|
+
class ApplicationController < I18nAdmin.root_controller_parent.constantize
|
3
|
+
include FontAwesome::Rails::IconHelper
|
4
|
+
|
5
|
+
helper_method :current_locale
|
6
|
+
|
7
|
+
if (authentication_method = I18nAdmin.authentication_method)
|
8
|
+
before_filter authentication_method
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_user
|
12
|
+
if (current_user_method = I18nAdmin.current_user_method)
|
13
|
+
send(current_user_method)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_locale
|
18
|
+
@current_locale ||= (params[:locale] || I18n.locale).to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module I18nAdmin
|
2
|
+
class ExportsController < I18nAdmin::ApplicationController
|
3
|
+
def show
|
4
|
+
respond_to do |format|
|
5
|
+
format.html do
|
6
|
+
@job_id = Export::XLS.perform_async(current_locale.to_s)
|
7
|
+
render 'queued', layout: false
|
8
|
+
end
|
9
|
+
|
10
|
+
format.xls do
|
11
|
+
job = Export::XLS.new
|
12
|
+
job.perform(current_locale.to_s)
|
13
|
+
|
14
|
+
redirect_to job.export_file.file.url
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module I18nAdmin
|
2
|
+
class ImportsController < I18nAdmin::ApplicationController
|
3
|
+
def show
|
4
|
+
respond_to do |format|
|
5
|
+
format.json do
|
6
|
+
job = ImportJob.find(params[:id])
|
7
|
+
render json: job.as_json(only: [:id, :filename, :state])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def new
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
unless params[:file].present?
|
17
|
+
flash[:error] = t('i18n_admin.imports.please_choose_file')
|
18
|
+
render 'new'
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
@job = ImportJob.create!(locale: current_locale, filename: params[:file].original_filename)
|
23
|
+
Import::Job.perform_async(current_locale, params[:file], @job.id)
|
24
|
+
|
25
|
+
render 'processing'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module I18nAdmin
|
2
|
+
class TranslationsController < I18nAdmin::ApplicationController
|
3
|
+
before_filter :fetch_translations
|
4
|
+
|
5
|
+
def index
|
6
|
+
@translations = @translations.search(params[:q]) if params[:q]
|
7
|
+
@translations = @translations.page(params[:page]).per(60)
|
8
|
+
|
9
|
+
@locales = I18n.available_locales
|
10
|
+
end
|
11
|
+
|
12
|
+
def update
|
13
|
+
translation = @translations.find(params[:id])
|
14
|
+
translation.value = translation_params[:value]
|
15
|
+
|
16
|
+
I18nAdmin::Translations.update(translation)
|
17
|
+
render partial: 'translation', locals: { translation: translation }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def translation_params
|
23
|
+
params.require(:translation).permit(:value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_translations
|
27
|
+
@translations = I18nAdmin::Translations.translations_for(current_locale)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|