releaf-i18n_database 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/app/assets/javascripts/releaf/controllers/releaf/i18n_database/translations.js +22 -0
  4. data/app/assets/stylesheets/releaf/controllers/releaf/i18n_database/translations.scss +95 -0
  5. data/app/builders/releaf/i18n_database/translations/builders_common.rb +13 -0
  6. data/app/builders/releaf/i18n_database/translations/edit_builder.rb +41 -0
  7. data/app/builders/releaf/i18n_database/translations/index_builder.rb +44 -0
  8. data/app/builders/releaf/i18n_database/translations/table_builder.rb +31 -0
  9. data/app/controllers/releaf/i18n_database/translations_controller.rb +171 -0
  10. data/app/lib/releaf/i18n_database/translations_importer.rb +72 -0
  11. data/app/lib/releaf/i18n_database/translations_utilities.rb +66 -0
  12. data/app/models/releaf/i18n_database/translation.rb +17 -0
  13. data/app/models/releaf/i18n_database/translation_data.rb +11 -0
  14. data/app/views/releaf/i18n_database/translations/_form_fields.haml +40 -0
  15. data/app/views/releaf/i18n_database/translations/export.xlsx.axlsx +24 -0
  16. data/lib/releaf-i18n_database.rb +5 -0
  17. data/lib/releaf/i18n_database/backend.rb +169 -0
  18. data/lib/releaf/i18n_database/builders_autoload.rb +10 -0
  19. data/lib/releaf/i18n_database/engine.rb +36 -0
  20. data/lib/releaf/i18n_database/humanize_missing_translations.rb +15 -0
  21. data/releaf-i18n_database.gemspec +21 -0
  22. data/spec/builders/translations/builder_common_spec.rb +39 -0
  23. data/spec/builders/translations/edit_builder_spec.rb +93 -0
  24. data/spec/builders/translations/index_builder_spec.rb +96 -0
  25. data/spec/builders/translations/table_builder_spec.rb +68 -0
  26. data/spec/controllers/i18n_backend/translations_controller_spec.rb +157 -0
  27. data/spec/features/translations_spec.rb +162 -0
  28. data/spec/fixtures/all_translations_exported.xlsx +0 -0
  29. data/spec/fixtures/time.formats.xlsx +0 -0
  30. data/spec/fixtures/translations_import.xlsx +0 -0
  31. data/spec/fixtures/unsupported_import_file.png +0 -0
  32. data/spec/lib/i18n_database/backend_spec.rb +337 -0
  33. data/spec/lib/i18n_database/humanize_missing_translations_spec.rb +18 -0
  34. data/spec/lib/i18n_database/translations_importer_spec.rb +17 -0
  35. data/spec/lib/i18n_database/translations_utilities_spec.rb +175 -0
  36. data/spec/models/i18n_database/translation_data_spec.rb +13 -0
  37. data/spec/models/i18n_database/translation_spec.rb +49 -0
  38. 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