releaf-i18n_database 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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