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
@@ -0,0 +1,40 @@
|
|
1
|
+
module I18nAdmin
|
2
|
+
module ApplicationHelper
|
3
|
+
# Converts flash types to :success or :error to conform to what
|
4
|
+
# twitter bootstrap can handle
|
5
|
+
#
|
6
|
+
def homogenize_flash_type type
|
7
|
+
case type
|
8
|
+
when :notice then :success
|
9
|
+
when :alert then :warning
|
10
|
+
when :error then :danger
|
11
|
+
else type
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def display_flash
|
16
|
+
# Get devise errors if present
|
17
|
+
if respond_to?(:devise_error_messages!)
|
18
|
+
flash[:alert] = devise_error_messages! if defined?(resource) && devise_error_messages! != ""
|
19
|
+
end
|
20
|
+
|
21
|
+
# Render empty string if no flash
|
22
|
+
return "" if flash.empty?
|
23
|
+
|
24
|
+
# Make a div.alert for each message and join the whole
|
25
|
+
messages = flash.map do |type, message|
|
26
|
+
flash.delete(type)
|
27
|
+
content_tag :div, class: "alert alert-block alert-#{ homogenize_flash_type(type) } alert-dismissable" do
|
28
|
+
buffer = content_tag(:button, type: "button", class: "close", "data-dismiss" => "alert") do
|
29
|
+
content_tag(:span, '×'.html_safe)
|
30
|
+
end
|
31
|
+
|
32
|
+
buffer += content_tag(:p, message.html_safe)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Join the messages and make sure markup is correctly displayed
|
37
|
+
messages.join.html_safe
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
= modal id: 'job-queued-modal', data: { :'i18n-job-monitoring' => true, :'monitor-url' => job_monitor.job_progress_path(@job_id) } do |modal|
|
2
|
+
= modal.header do
|
3
|
+
= t('i18n_admin.exports.queued.title')
|
4
|
+
|
5
|
+
= modal.body do
|
6
|
+
%p= t('i18n_admin.exports.queued.please_wait')
|
7
|
+
|
8
|
+
%p.help-block= t('i18n_admin.exports.queued.help')
|
9
|
+
|
10
|
+
%hr
|
11
|
+
|
12
|
+
.progress
|
13
|
+
.progress-bar.progress-bar-info.progress-bar-striped.active{ style: 'width:100%;' }
|
14
|
+
|
15
|
+
= modal.footer do
|
16
|
+
= link_to '#', class: 'btn btn-default', data: { cancel: 'job' } do
|
17
|
+
%i.fa.fa-times
|
18
|
+
= t('i18n_admin.shared.cancel')
|
File without changes
|
File without changes
|
@@ -0,0 +1,61 @@
|
|
1
|
+
<div class="container-fluid main-container">
|
2
|
+
<div class="panel panel-default">
|
3
|
+
<div class="panel-heading">
|
4
|
+
<h1 class="panel-title">
|
5
|
+
<%= t('i18n_admin.exports.title') %>
|
6
|
+
</h1>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="panel-body">
|
10
|
+
<div class="list-group">
|
11
|
+
<% I18n.available_locales.each do |locale| %>
|
12
|
+
<%= link_to export_path(locale: locale, format: (:xls unless I18nAdmin.async_io)), class: 'list-group-item', remote: I18nAdmin.async_io, data: { :'export-link' => true } do %>
|
13
|
+
<i class="glyphicon glyphicon-download"></i>
|
14
|
+
<%= locale %>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<hr>
|
22
|
+
|
23
|
+
<div class="panel panel-default">
|
24
|
+
<div class="panel-heading">
|
25
|
+
<h1 class="panel-title">
|
26
|
+
<%= t('i18n_admin.imports.title') %>
|
27
|
+
</h1>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<% if @import && @import.errors.any? %>
|
31
|
+
<div class="panel-heading import-errors danger">
|
32
|
+
<% @import.errors.each do |type, errors| %>
|
33
|
+
<%= render partial: "i18n_admin/imports/errors/#{ type }", locals: { errors: errors } %>
|
34
|
+
<% end %>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<div class="panel-body">
|
39
|
+
<%= form_tag import_path, multipart: true do %>
|
40
|
+
<div class="form-group">
|
41
|
+
<%= label_tag :file, t('i18n_admin.imports.file.label') %>
|
42
|
+
<%= file_field_tag :file %>
|
43
|
+
<p class="help-block">
|
44
|
+
<%= t('i18n_admin.imports.file.hint') %>
|
45
|
+
</p>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<div class="form-group">
|
49
|
+
<%= label_tag :locale, t('i18n_admin.imports.locale.label') %>
|
50
|
+
<%= select_tag :locale, options_for_select(I18n.available_locales), include_blank: true %>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
<div class="form-actions">
|
54
|
+
<button type="submit" class="btn btn-primary">
|
55
|
+
<%= t('i18n_admin.imports.submit') %>
|
56
|
+
</button>
|
57
|
+
</div>
|
58
|
+
<% end %>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<div class="container-fluid main-container" data-processing-import data-check-url="<%= import_path(id: @job) %>">
|
2
|
+
<div class="panel panel-default">
|
3
|
+
<div class="panel-heading">
|
4
|
+
<h1 class="panel-title">
|
5
|
+
<%= t('i18n_admin.imports.processing.title') %>
|
6
|
+
</h1>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="panel-body" data-status="pending">
|
10
|
+
<p class="help-block text-center">
|
11
|
+
<%= t('i18n_admin.imports.processing.help_text') %>
|
12
|
+
<br><br>
|
13
|
+
<%= image_tag 'i18n_admin/spinner.gif' %>
|
14
|
+
</p>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="panel-body hidden" data-status="success">
|
18
|
+
<div class="alert alert-success">
|
19
|
+
<%= t('i18n_admin.imports.run.success') %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<%= link_to t('i18n_admin.shared.back'), new_import_path, class: 'btn btn-primary' %>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="panel-body hidden" data-status="error">
|
26
|
+
<div class="alert alert-error">
|
27
|
+
<%= t('i18n_admin.imports.run.errors') %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<%= link_to t('i18n_admin.shared.back'), new_import_path, class: 'btn btn-primary' %>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</div>
|
File without changes
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<tr class="translation-row" data-translation>
|
2
|
+
<td width="20%" class="translation-key-cell" title="<%= translation.key %>"><%= translation.key %></td>
|
3
|
+
<td width="40%"><%= translation.original %></td>
|
4
|
+
<td width="40%" class="input-cell">
|
5
|
+
<%= form_for(translation, url: translation_path(translation, locale: current_locale), remote: true, html: { data: { :'translation-form' => true } }) do |form| %>
|
6
|
+
<%= form.text_area :value, class: 'form-control translation-input', data: { :'translation-field' => true } %>
|
7
|
+
<% end %>
|
8
|
+
</td>
|
9
|
+
</tr>
|
@@ -0,0 +1,81 @@
|
|
1
|
+
<div class="container-fluid main-container">
|
2
|
+
<div class="panel panel-default">
|
3
|
+
<div class="panel-heading">
|
4
|
+
<div class="row">
|
5
|
+
<div class="col-md-6">
|
6
|
+
<h1 class="panel-title">
|
7
|
+
<%= t('i18n_admin.translations.title') %>
|
8
|
+
</h1>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="col-md-6">
|
12
|
+
<div class="pull-right">
|
13
|
+
<h2>
|
14
|
+
<%= t('i18n_admin.translations.choose_locale') %>
|
15
|
+
</h2>
|
16
|
+
|
17
|
+
<div class="btn-group">
|
18
|
+
<% @locales.each do |locale| %>
|
19
|
+
<%= link_to translations_path(locale: locale), class: "btn btn-primary#{ ' active' if current_locale == locale }" do %>
|
20
|
+
<%= locale %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<div class="panel-heading panel-subheading">
|
30
|
+
<div class="row">
|
31
|
+
<div class="col-md-6 pagination-column">
|
32
|
+
<%= render partial: 'pagination', locals: { translations: @translations } %>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div class="col-md-6">
|
36
|
+
<%= form_tag translations_path, method: :get do %>
|
37
|
+
<div class="input-group">
|
38
|
+
<span class="input-group-addon">
|
39
|
+
<i class="glyphicon glyphicon-search"></i>
|
40
|
+
</span>
|
41
|
+
|
42
|
+
<%= text_field_tag :q, params[:q], class: 'form-control' %>
|
43
|
+
<%= hidden_field_tag :locale, current_locale %>
|
44
|
+
|
45
|
+
<span class="input-group-btn">
|
46
|
+
<button class="btn btn-primary" type="submit">
|
47
|
+
<%= t('i18n_admin.translations.search.submit') %>
|
48
|
+
</button>
|
49
|
+
|
50
|
+
<% if params[:q].present? %>
|
51
|
+
<%= link_to translations_path(locale: current_locale), class: 'btn btn-danger' do %>
|
52
|
+
<%= t('i18n_admin.translations.search.cancel') %>
|
53
|
+
<% end %>
|
54
|
+
<% end %>
|
55
|
+
</span>
|
56
|
+
</div>
|
57
|
+
<% end %>
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div class="panel-body">
|
63
|
+
<table class="table table-bordered">
|
64
|
+
<thead>
|
65
|
+
<tr>
|
66
|
+
<th width="20%"><%= t('i18n_admin.translation_set.key') %></th>
|
67
|
+
<th width="40%"><%= t('i18n_admin.translation_set.original', default_locale: I18n.default_locale) %></th>
|
68
|
+
<th width="40%"><%= t('i18n_admin.translation_set.translation') %></th>
|
69
|
+
</tr>
|
70
|
+
</thead>
|
71
|
+
<tbody>
|
72
|
+
<%= render @translations %>
|
73
|
+
</tbody>
|
74
|
+
</table>
|
75
|
+
</div>
|
76
|
+
|
77
|
+
<div class="panel-footer">
|
78
|
+
<%= render partial: 'pagination', locals: { translations: @translations } %>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
</div>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
en:
|
2
|
+
i18n_admin:
|
3
|
+
translations:
|
4
|
+
title: 'Translations'
|
5
|
+
choose_locale: 'Choose locale to translate :'
|
6
|
+
search:
|
7
|
+
submit: 'Search'
|
8
|
+
cancel: 'Cancel search'
|
9
|
+
|
10
|
+
translation_set:
|
11
|
+
key: 'Key'
|
12
|
+
original: "Original (%{default_locale})"
|
13
|
+
translation: 'Translation'
|
14
|
+
|
15
|
+
exports:
|
16
|
+
title: "Export translations file"
|
17
|
+
|
18
|
+
imports:
|
19
|
+
title: "Import translations file"
|
20
|
+
file:
|
21
|
+
label: "Translations file"
|
22
|
+
hint: "File in .xls format, from the exported file template"
|
23
|
+
locale:
|
24
|
+
label: "Locale to update"
|
25
|
+
submit: "Update translations"
|
26
|
+
please_choose_file: "Please choose a file to upload"
|
27
|
+
processing:
|
28
|
+
title: "Importing"
|
29
|
+
help_text: |
|
30
|
+
The translations file is being imported.
|
31
|
+
This can take from a few seconds to several minutes, depending on
|
32
|
+
the size of the imported file.
|
33
|
+
run:
|
34
|
+
success: "The file has been successfully imported"
|
35
|
+
errors: "The file was imported, but some errors happened"
|
36
|
+
|
37
|
+
shared:
|
38
|
+
back: "Back"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
fr:
|
2
|
+
i18n_admin:
|
3
|
+
translations:
|
4
|
+
title: 'Traductions'
|
5
|
+
choose_locale: 'Choisissez la langue à traduire :'
|
6
|
+
search:
|
7
|
+
submit: 'Rechercher'
|
8
|
+
cancel: 'Annuler la recherche'
|
9
|
+
|
10
|
+
translation_set:
|
11
|
+
key: 'Clé'
|
12
|
+
original: 'Original (%{default_locale})'
|
13
|
+
translation: 'Traduction'
|
14
|
+
|
15
|
+
exports:
|
16
|
+
title: "Exporter les traductions"
|
17
|
+
queued:
|
18
|
+
title: "Export en cours ..."
|
19
|
+
please_wait: "Merci de patienter quelques instants."
|
20
|
+
help: |
|
21
|
+
L'export est en cours de traitement. Une fois terminé, le
|
22
|
+
téléchargement du fichier de traduction vous sera automatiquement
|
23
|
+
proposé.
|
24
|
+
file:
|
25
|
+
name: "traductions_%{lang}-%{time}.%{ext}"
|
26
|
+
columns:
|
27
|
+
key: "Clé"
|
28
|
+
original: "Original (%{lang})"
|
29
|
+
translated: "Traduction (%{lang})"
|
30
|
+
|
31
|
+
imports:
|
32
|
+
title: "Importer un fichier de traductions"
|
33
|
+
file:
|
34
|
+
label: "Fichier de traductions"
|
35
|
+
hint: "Fichier au format .xls, à partir du modèle du fichier exporté"
|
36
|
+
locale:
|
37
|
+
label: "Langue à mettre à jour"
|
38
|
+
submit: "Mettre à jour les traductions"
|
39
|
+
please_choose_file: "Merci de choisir un fichier à envoyer"
|
40
|
+
processing:
|
41
|
+
title: "Import en cours"
|
42
|
+
help_text: |
|
43
|
+
L'import des traductions est en cours.
|
44
|
+
Cette opération peut prendre de quelques secondes à plusieurs minutes,
|
45
|
+
en fonction de la taille du fichier importé.
|
46
|
+
run:
|
47
|
+
success: "Le fichier a bien été importé"
|
48
|
+
errors: "Le fichier a été importé, mais certaines erreurs ont eu lieu"
|
49
|
+
|
50
|
+
shared:
|
51
|
+
back: "Retour"
|
52
|
+
cancel: "Annuler"
|
data/config/routes.rb
ADDED