releaf-i18n_database 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +24 -0
- data/app/assets/javascripts/releaf/controllers/releaf/i18n_database/translations.js +22 -0
- data/app/assets/stylesheets/releaf/controllers/releaf/i18n_database/translations.scss +95 -0
- data/app/builders/releaf/i18n_database/translations/builders_common.rb +13 -0
- data/app/builders/releaf/i18n_database/translations/edit_builder.rb +41 -0
- data/app/builders/releaf/i18n_database/translations/index_builder.rb +44 -0
- data/app/builders/releaf/i18n_database/translations/table_builder.rb +31 -0
- data/app/controllers/releaf/i18n_database/translations_controller.rb +171 -0
- data/app/lib/releaf/i18n_database/translations_importer.rb +72 -0
- data/app/lib/releaf/i18n_database/translations_utilities.rb +66 -0
- data/app/models/releaf/i18n_database/translation.rb +17 -0
- data/app/models/releaf/i18n_database/translation_data.rb +11 -0
- data/app/views/releaf/i18n_database/translations/_form_fields.haml +40 -0
- data/app/views/releaf/i18n_database/translations/export.xlsx.axlsx +24 -0
- data/lib/releaf-i18n_database.rb +5 -0
- data/lib/releaf/i18n_database/backend.rb +169 -0
- data/lib/releaf/i18n_database/builders_autoload.rb +10 -0
- data/lib/releaf/i18n_database/engine.rb +36 -0
- data/lib/releaf/i18n_database/humanize_missing_translations.rb +15 -0
- data/releaf-i18n_database.gemspec +21 -0
- data/spec/builders/translations/builder_common_spec.rb +39 -0
- data/spec/builders/translations/edit_builder_spec.rb +93 -0
- data/spec/builders/translations/index_builder_spec.rb +96 -0
- data/spec/builders/translations/table_builder_spec.rb +68 -0
- data/spec/controllers/i18n_backend/translations_controller_spec.rb +157 -0
- data/spec/features/translations_spec.rb +162 -0
- data/spec/fixtures/all_translations_exported.xlsx +0 -0
- data/spec/fixtures/time.formats.xlsx +0 -0
- data/spec/fixtures/translations_import.xlsx +0 -0
- data/spec/fixtures/unsupported_import_file.png +0 -0
- data/spec/lib/i18n_database/backend_spec.rb +337 -0
- data/spec/lib/i18n_database/humanize_missing_translations_spec.rb +18 -0
- data/spec/lib/i18n_database/translations_importer_spec.rb +17 -0
- data/spec/lib/i18n_database/translations_utilities_spec.rb +175 -0
- data/spec/models/i18n_database/translation_data_spec.rb +13 -0
- data/spec/models/i18n_database/translation_spec.rb +49 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1708060372539566d0f74c1d83d592b6052fab44
|
4
|
+
data.tar.gz: 26aa76b2f65753dddb9e0b85cfc3a2e4449daf30
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a1c4f3c879bdecf8b93032975e29b2b178121d51d705478bf1c3e72acf9d04eb091e001f603af442597fddef113d01e8cf71ca9ac884713096be8722ff6a39fa
|
7
|
+
data.tar.gz: 086aed43840796336776f6f23a09b472a90b1c0909290649bc26aa88f7a0c541e4a1a129201f3c5c4cfde52bc1cfe7bdf1a2eb04c4cb67e09dad8246c0e767d8
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2012, CubeSystems <info@cubesystems.lv>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
* Neither the name of the CubeSystems nor the names of its contributors may
|
12
|
+
be used to endorse or promote products derived from this software without
|
13
|
+
specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL CubeSystems BE LIABLE FOR ANY
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
jQuery(function()
|
2
|
+
{
|
3
|
+
var body = jQuery('body');
|
4
|
+
|
5
|
+
|
6
|
+
// import
|
7
|
+
var controller = jQuery( '.controller-releaf-i18n-database-translations' );
|
8
|
+
var import_form = controller.find( 'form.import' );
|
9
|
+
var import_file = import_form.find( 'input[type="file"]' );
|
10
|
+
var import_button = controller.find( 'button[name="import"]' );
|
11
|
+
|
12
|
+
import_button.click(function()
|
13
|
+
{
|
14
|
+
import_file.click();
|
15
|
+
});
|
16
|
+
|
17
|
+
import_file.change(function()
|
18
|
+
{
|
19
|
+
body.trigger('toolboxcloseall');
|
20
|
+
import_form.submit();
|
21
|
+
});
|
22
|
+
});
|
@@ -0,0 +1,95 @@
|
|
1
|
+
@import 'releaf/environment';
|
2
|
+
|
3
|
+
.controller-releaf-i18n-database-translations
|
4
|
+
{
|
5
|
+
&.view-edit,
|
6
|
+
&.view-import
|
7
|
+
{
|
8
|
+
section
|
9
|
+
{
|
10
|
+
padding: 0;
|
11
|
+
|
12
|
+
form
|
13
|
+
{
|
14
|
+
&,
|
15
|
+
> footer
|
16
|
+
{
|
17
|
+
max-width: none;
|
18
|
+
}
|
19
|
+
|
20
|
+
> .body
|
21
|
+
{
|
22
|
+
border: none;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
.table
|
28
|
+
{
|
29
|
+
.code
|
30
|
+
{
|
31
|
+
width: steps(24);
|
32
|
+
}
|
33
|
+
|
34
|
+
td
|
35
|
+
{
|
36
|
+
padding: 0;
|
37
|
+
background-color: $color-background-lightest;
|
38
|
+
border: 1px solid $color-border-normal;
|
39
|
+
|
40
|
+
&.translation-name
|
41
|
+
{
|
42
|
+
background: $color-background-normal;
|
43
|
+
|
44
|
+
input.text
|
45
|
+
{
|
46
|
+
padding-left: steps(2);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
input.text
|
52
|
+
{
|
53
|
+
border-width: 0;
|
54
|
+
font-weight: normal;
|
55
|
+
background: transparent none;
|
56
|
+
width: 100%;
|
57
|
+
|
58
|
+
&:focus
|
59
|
+
{
|
60
|
+
border-width: 1px;
|
61
|
+
margin-left: -1px;
|
62
|
+
margin-right: -1px;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
.button.danger:not(:hover)
|
67
|
+
{
|
68
|
+
background: inherit;
|
69
|
+
color: inherit;
|
70
|
+
}
|
71
|
+
|
72
|
+
.changed input
|
73
|
+
{
|
74
|
+
background-color: $color-highlight-lighter;
|
75
|
+
border-color: $color-highlight-normal;
|
76
|
+
}
|
77
|
+
|
78
|
+
.delete-column
|
79
|
+
{
|
80
|
+
border-right: none;
|
81
|
+
}
|
82
|
+
|
83
|
+
+ .tools
|
84
|
+
{
|
85
|
+
margin-top: steps(2);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
.controller-releaf-i18n-database-translations.view-index form.import
|
93
|
+
{
|
94
|
+
display: none;
|
95
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Releaf::I18nDatabase::Translations
|
2
|
+
module BuildersCommon
|
3
|
+
|
4
|
+
def action_url(action, params = {})
|
5
|
+
url_for(request.query_parameters.merge(action: action).merge(params))
|
6
|
+
end
|
7
|
+
|
8
|
+
def export_button
|
9
|
+
url = action_url(:export, format: :xlsx)
|
10
|
+
button(t("export"), "download", class: "secondary", href: url)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Releaf::I18nDatabase::Translations
|
2
|
+
class EditBuilder < Releaf::Builders::EditBuilder
|
3
|
+
include Releaf::I18nDatabase::Translations::BuildersCommon
|
4
|
+
|
5
|
+
def section
|
6
|
+
tag(:section) do
|
7
|
+
form_tag(action_url(:update)) do
|
8
|
+
safe_join{ section_blocks }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def section_body
|
14
|
+
tag(:div, class: "body") do
|
15
|
+
render partial: "form_fields", locals: {builder: self}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def import?
|
20
|
+
template_variable("import")
|
21
|
+
end
|
22
|
+
|
23
|
+
def save_button
|
24
|
+
button(save_button_text, "check", class: "primary", data: { type: 'ok' }, type: "submit")
|
25
|
+
end
|
26
|
+
|
27
|
+
def save_button_text
|
28
|
+
t(import? ? "Import" : "Save")
|
29
|
+
end
|
30
|
+
|
31
|
+
def footer_secondary_tools
|
32
|
+
[back_to_index_button, (export_button unless import?)].compact
|
33
|
+
end
|
34
|
+
|
35
|
+
def back_to_index_button
|
36
|
+
button(t("Back to list"), "caret-left", class: "secondary", href: action_url(:index))
|
37
|
+
end
|
38
|
+
|
39
|
+
def section_header; end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Releaf::I18nDatabase::Translations
|
2
|
+
class IndexBuilder < Releaf::Builders::IndexBuilder
|
3
|
+
include Releaf::I18nDatabase::Translations::BuildersCommon
|
4
|
+
|
5
|
+
def text_search_content
|
6
|
+
search_only_blank_ui + super
|
7
|
+
end
|
8
|
+
|
9
|
+
def search_only_blank_ui
|
10
|
+
search_field "only-blank" do
|
11
|
+
[
|
12
|
+
check_box_tag(:only_blank, 'true', params[:only_blank].present? ),
|
13
|
+
label_tag(:only_blank, t("Only blank"))
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def footer_primary_tools
|
19
|
+
[edit_button]
|
20
|
+
end
|
21
|
+
|
22
|
+
def footer_secondary_tools
|
23
|
+
[export_button, import_button, import_form]
|
24
|
+
end
|
25
|
+
|
26
|
+
def import_button
|
27
|
+
button(t("import"), "upload", name: "import", class: "secondary")
|
28
|
+
end
|
29
|
+
|
30
|
+
def import_form
|
31
|
+
form_tag(url_for(action: 'import'), multipart: true, class: 'import') do
|
32
|
+
file_field_tag :import_file
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def edit_button
|
37
|
+
button(t("Edit"), "edit", class: "primary", href: action_url(:edit))
|
38
|
+
end
|
39
|
+
|
40
|
+
def text_search_available?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Releaf::I18nDatabase::Translations
|
2
|
+
class TableBuilder < Releaf::Builders::TableBuilder
|
3
|
+
def column_names
|
4
|
+
[:key] + Releaf.application.config.all_locales
|
5
|
+
end
|
6
|
+
|
7
|
+
def head_cell_content(column)
|
8
|
+
if Releaf.application.config.all_locales.include? column.to_s
|
9
|
+
translate_locale(column)
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def cell_content(resource, column, options)
|
16
|
+
tag(:span, super)
|
17
|
+
end
|
18
|
+
|
19
|
+
def locale_value(resource, column)
|
20
|
+
resource.locale_value(column)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cell_format_method(column)
|
24
|
+
if Releaf.application.config.all_locales.include? column
|
25
|
+
:locale_value
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module Releaf::I18nDatabase
|
2
|
+
class TranslationsController < ::Releaf::BaseController
|
3
|
+
def self.resource_class
|
4
|
+
Releaf::I18nDatabase::Translation
|
5
|
+
end
|
6
|
+
|
7
|
+
def edit
|
8
|
+
@collection = resources
|
9
|
+
search(params[:search])
|
10
|
+
end
|
11
|
+
|
12
|
+
def export
|
13
|
+
@collection = resources
|
14
|
+
search(params[:search])
|
15
|
+
|
16
|
+
respond_to do |format|
|
17
|
+
format.xlsx do
|
18
|
+
response.headers['Content-Disposition'] = "attachment; filename=\"#{export_file_name}\""
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_breadcrumbs
|
24
|
+
super
|
25
|
+
|
26
|
+
if %w[edit update].include?(params[:action]) && !params.has_key?(:import)
|
27
|
+
@breadcrumbs << { name: I18n.t("edit translations", scope: controller_scope_name), url: url_for(action: :edit, search: params[:search]) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def update
|
32
|
+
@collection = []
|
33
|
+
@translations_to_save = []
|
34
|
+
|
35
|
+
valid = build_updatables(params[:translations])
|
36
|
+
import_view if params.has_key?(:import)
|
37
|
+
|
38
|
+
respond_to do |format|
|
39
|
+
format.html do
|
40
|
+
if valid
|
41
|
+
process_updatables
|
42
|
+
update_response_success
|
43
|
+
else
|
44
|
+
render_notification false, now: true
|
45
|
+
render action: :edit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def resources
|
52
|
+
Releaf::I18nDatabase::TranslationsUtilities.include_localizations(super).order(:key)
|
53
|
+
end
|
54
|
+
|
55
|
+
# overwrite search here
|
56
|
+
def search(lookup_string)
|
57
|
+
@collection = Releaf::I18nDatabase::TranslationsUtilities.search(@collection, lookup_string, params[:only_blank].present?)
|
58
|
+
end
|
59
|
+
|
60
|
+
def import
|
61
|
+
if File.exists?(import_file_path) && !import_file_extension.blank?
|
62
|
+
begin
|
63
|
+
@collection = Releaf::I18nDatabase::TranslationsImporter.new(import_file_path, import_file_extension).parsed_output
|
64
|
+
import_view
|
65
|
+
render :edit
|
66
|
+
rescue Releaf::I18nDatabase::TranslationsImporter::UnsupportedFileFormatError
|
67
|
+
flash["error"] = { "id" => "resource_status", "message" => I18n.t("Unsupported file format", scope: notice_scope_name) }
|
68
|
+
redirect_to action: :index
|
69
|
+
end
|
70
|
+
else
|
71
|
+
redirect_to action: :index
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def import_view
|
76
|
+
@import = true
|
77
|
+
@breadcrumbs << { name: I18n.t("import", scope: controller_scope_name) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup
|
81
|
+
super
|
82
|
+
@features = {
|
83
|
+
index: true
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def action_views
|
88
|
+
super.merge(import: :edit)
|
89
|
+
end
|
90
|
+
|
91
|
+
def action_features
|
92
|
+
{index: :index}.with_indifferent_access
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def process_updatables
|
98
|
+
resource_class.where(id: @translation_ids_to_destroy).destroy_all unless @translation_ids_to_destroy.empty?
|
99
|
+
@translations_to_save.map(&:save!)
|
100
|
+
Releaf::I18nDatabase::Backend.translations_updated_at = Time.now
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_updatables translations_params
|
104
|
+
valid = true
|
105
|
+
@translation_ids_to_destroy = params.fetch(:existing_translations, "").split(",")
|
106
|
+
|
107
|
+
translations_params ||= []
|
108
|
+
translations_params.each do |values|
|
109
|
+
translation = load_translation(values["key"], values["localizations"])
|
110
|
+
|
111
|
+
if translation.valid?
|
112
|
+
@translations_to_save << translation
|
113
|
+
@translation_ids_to_destroy.delete(translation.id.to_s)
|
114
|
+
else
|
115
|
+
valid = false
|
116
|
+
end
|
117
|
+
|
118
|
+
@collection << translation
|
119
|
+
end
|
120
|
+
|
121
|
+
valid
|
122
|
+
end
|
123
|
+
|
124
|
+
def load_translation(key, localizations)
|
125
|
+
translation = Releaf::I18nDatabase::Translation.where(key: key).first_or_initialize
|
126
|
+
translation.key = key
|
127
|
+
|
128
|
+
localizations.each_pair do |locale, localization|
|
129
|
+
load_translation_data(translation, locale, localization)
|
130
|
+
end
|
131
|
+
|
132
|
+
translation
|
133
|
+
end
|
134
|
+
|
135
|
+
def load_translation_data(translation, locale, localization)
|
136
|
+
translation_data = translation.translation_data.find{ |x| x.lang == locale }
|
137
|
+
# replace existing locale value only if new one is not blank
|
138
|
+
if translation_data
|
139
|
+
translation_data.localization = localization
|
140
|
+
# always assign value for new locale
|
141
|
+
elsif translation_data.nil?
|
142
|
+
translation_data = translation.translation_data.build(lang: locale, localization: localization)
|
143
|
+
end
|
144
|
+
|
145
|
+
translation_data
|
146
|
+
end
|
147
|
+
|
148
|
+
def update_response_success
|
149
|
+
if @import
|
150
|
+
msg = 'successfuly imported %{count} translations'
|
151
|
+
flash["success"] = { "id" => "resource_status", "message" => I18n.t(msg, default: msg, count: @translations_to_save.size , scope: notice_scope_name) }
|
152
|
+
redirect_to action: :index
|
153
|
+
else
|
154
|
+
render_notification true
|
155
|
+
redirect_to({action: :edit}.merge(request.query_parameters))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def export_file_name
|
160
|
+
"#{Rails.application.class.parent_name.underscore}_translations_#{Time.now.strftime('%Y_%m_%d_%H_%M_%S')}.xlsx"
|
161
|
+
end
|
162
|
+
|
163
|
+
def import_file_path
|
164
|
+
params[:import_file].try(:tempfile).try(:path).to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
def import_file_extension
|
168
|
+
File.extname(params[:import_file].original_filename).gsub(".", "")
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|