weeler 0.0.4 → 1.0.0

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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rspec +1 -1
  4. data/.travis.yml +7 -2
  5. data/CHANGELOG.md +149 -1
  6. data/LICENSE.txt +2 -2
  7. data/README.md +83 -1
  8. data/app/.DS_Store +0 -0
  9. data/app/assets/javascripts/weeler/init.js.coffee +13 -2
  10. data/app/assets/javascripts/weeler/lib/notify.js +18 -0
  11. data/app/assets/javascripts/weeler/lib/sortable.js +9 -0
  12. data/app/assets/stylesheets/weeler/init.css.erb +2 -1
  13. data/app/assets/stylesheets/weeler/style.css.scss +22 -0
  14. data/app/controllers/concerns/weeler/section_seo.rb +16 -0
  15. data/app/controllers/weeler/administration_controller.rb +11 -0
  16. data/app/controllers/weeler/application_controller.rb +5 -0
  17. data/app/controllers/weeler/base_controller.rb +16 -12
  18. data/app/controllers/weeler/configuration_controller.rb +9 -0
  19. data/app/controllers/weeler/content_controller.rb +12 -0
  20. data/app/controllers/weeler/home_controller.rb +5 -2
  21. data/app/controllers/weeler/seo_items_controller.rb +24 -0
  22. data/app/controllers/weeler/settings_controller.rb +21 -0
  23. data/app/controllers/weeler/static_sections_controller.rb +19 -0
  24. data/app/controllers/weeler/translations_controller.rb +36 -18
  25. data/app/models/concerns/weeler/seoable.rb +61 -0
  26. data/app/models/concerns/weeler/sluger.rb +32 -0
  27. data/app/models/settings.rb +2 -0
  28. data/app/models/weeler/seo.rb +9 -0
  29. data/app/views/kaminari/{_first_page.html.erb → weeler/_first_page.html.erb} +1 -1
  30. data/app/views/kaminari/{_gap.html.erb → weeler/_gap.html.erb} +1 -1
  31. data/app/views/kaminari/{_last_page.html.erb → weeler/_last_page.html.erb} +1 -1
  32. data/app/views/kaminari/{_next_page.html.erb → weeler/_next_page.html.erb} +1 -1
  33. data/app/views/kaminari/{_page.html.erb → weeler/_page.html.erb} +0 -0
  34. data/app/views/kaminari/{_paginator.html.erb → weeler/_paginator.html.erb} +1 -1
  35. data/app/views/kaminari/{_prev_page.html.erb → weeler/_prev_page.html.erb} +1 -1
  36. data/app/views/layouts/weeler/base.html.haml +22 -0
  37. data/{lib/generators/weeler/templates/views/weeler/example/index.html.erb → app/views/weeler/administration/_filter.html.haml} +0 -0
  38. data/app/views/weeler/administration/_header_row.html.haml +5 -0
  39. data/app/views/weeler/administration/_row.html.haml +5 -0
  40. data/app/views/weeler/administration/_side_navbar.html.haml +8 -0
  41. data/app/views/weeler/administration/index.html.haml +17 -0
  42. data/app/views/weeler/administration/welcome.html.haml +5 -0
  43. data/app/views/weeler/base/_flash.html.haml +3 -0
  44. data/app/views/weeler/base/_footer.html.haml +2 -0
  45. data/app/views/weeler/base/_header_navbar.html.haml +19 -0
  46. data/app/views/weeler/base/_side_navbar.html.haml +2 -0
  47. data/app/views/weeler/configuration/_side_navbar.html.haml +12 -0
  48. data/app/views/weeler/configuration/index.html.haml +3 -0
  49. data/app/views/weeler/content/_error_messages.html.haml +5 -0
  50. data/app/views/weeler/content/_form.html.haml +6 -0
  51. data/app/views/weeler/content/_list.html.haml +23 -0
  52. data/app/views/weeler/content/_object_seo.html.haml +31 -0
  53. data/app/views/weeler/content/_side_navbar.html.haml +17 -0
  54. data/app/views/weeler/content/_sortable_list.html.haml +24 -0
  55. data/app/views/weeler/content/edit.html.haml +2 -0
  56. data/app/views/weeler/content/index.html.haml +10 -0
  57. data/app/views/weeler/content/new.html.haml +2 -0
  58. data/app/views/weeler/content/welcome.html.haml +3 -0
  59. data/app/views/weeler/home/_side_navbar.html.haml +8 -0
  60. data/app/views/weeler/home/about.html.haml +15 -0
  61. data/app/views/weeler/home/index.html.haml +3 -0
  62. data/app/views/weeler/seo_items/edit.html.haml +28 -0
  63. data/app/views/weeler/seo_items/index.html.haml +15 -0
  64. data/app/views/weeler/settings/index.html.haml +19 -0
  65. data/app/views/weeler/static_sections/show.html.haml +26 -0
  66. data/app/views/weeler/translations/_form.html.haml +27 -0
  67. data/app/views/weeler/translations/_translation.html.haml +11 -0
  68. data/app/views/weeler/translations/edit.html.haml +14 -0
  69. data/app/views/weeler/translations/index.html.haml +76 -0
  70. data/app/views/weeler/translations/new.html.haml +13 -0
  71. data/lib/.DS_Store +0 -0
  72. data/lib/generators/weeler/.DS_Store +0 -0
  73. data/lib/generators/weeler/install_generator.rb +30 -26
  74. data/lib/generators/weeler/templates/assets/javascripts/weeler/app/index.js +2 -0
  75. data/lib/generators/weeler/templates/assets/stylesheets/weeler/app/index.css +4 -0
  76. data/lib/generators/weeler/templates/controllers/weeler/application_controller.rb +6 -0
  77. data/lib/generators/weeler/templates/initializers/weeler.rb +30 -2
  78. data/lib/generators/weeler/templates/migrations/create_weeler_seos.rb +14 -0
  79. data/lib/generators/weeler/templates/migrations/create_weeler_settings.rb +17 -0
  80. data/lib/generators/weeler/templates/migrations/translate_weeler_seos.rb +15 -0
  81. data/lib/generators/weeler/templates/views/weeler/.keep +0 -0
  82. data/lib/i18n/backend/weeler.rb +102 -22
  83. data/lib/i18n/backend/weeler/dedupe.rb +31 -0
  84. data/lib/i18n/backend/weeler/exporter.rb +67 -5
  85. data/lib/i18n/backend/weeler/html_checker.rb +41 -0
  86. data/lib/i18n/backend/weeler/importer.rb +30 -13
  87. data/lib/i18n/backend/weeler/translation.rb +12 -7
  88. data/lib/i18n/humanize_missing_translations.rb +1 -0
  89. data/lib/i18n/weeler.rb +8 -2
  90. data/lib/tasks/weeler.rake +8 -0
  91. data/lib/weeler.rb +80 -7
  92. data/lib/weeler/.DS_Store +0 -0
  93. data/lib/weeler/action_controller/acts/restful.rb +185 -0
  94. data/lib/weeler/action_dispatch/routing/mapper.rb +87 -0
  95. data/lib/weeler/action_view/helpers/form_helper.rb +13 -0
  96. data/lib/weeler/action_view/helpers/image_form_helper.rb +84 -0
  97. data/lib/weeler/action_view/helpers/translation_helper.rb +15 -0
  98. data/lib/weeler/deprecator.rb +9 -0
  99. data/lib/weeler/engine.rb +18 -4
  100. data/lib/weeler/version.rb +7 -1
  101. data/rspec.html +426 -147
  102. data/spec/.DS_Store +0 -0
  103. data/spec/controllers/translations_controller_spec.rb +76 -5
  104. data/spec/dummy/app/controllers/weeler/posts_controller.rb +3 -7
  105. data/spec/dummy/app/models/post.rb +17 -0
  106. data/spec/dummy/app/models/translation.rb +2 -0
  107. data/spec/dummy/config/locales/en.yml +4 -1
  108. data/spec/dummy/config/routes.rb +3 -3
  109. data/spec/dummy/db/development.sqlite3 +0 -0
  110. data/spec/dummy/db/migrate/20140123083704_create_weeler_seos.rb +14 -0
  111. data/spec/dummy/db/migrate/20140123083705_create_weeler_settings.rb +17 -0
  112. data/spec/dummy/db/migrate/{20131015144900_create_weeler_translations.rb → 20140123083706_create_weeler_translations.rb} +0 -0
  113. data/spec/dummy/db/migrate/20140123083707_translate_weeler_seos.rb +15 -0
  114. data/spec/dummy/db/migrate/20140718103237_create_posts.rb +12 -0
  115. data/spec/dummy/db/migrate/20140726151210_create_translations.rb +12 -0
  116. data/spec/dummy/db/schema.rb +59 -1
  117. data/spec/dummy/db/test.sqlite3 +0 -0
  118. data/spec/dummy/log/development.log +221 -0
  119. data/spec/factories/dummy_posts.rb +6 -0
  120. data/spec/fixtures/.DS_Store +0 -0
  121. data/spec/routing/routes_spec.rb +4 -4
  122. data/spec/spec_helper.rb +14 -3
  123. data/spec/weeler/action_controller/acts/restful_spec.rb +155 -0
  124. data/spec/weeler/action_view/helpers/form_helper_spec.rb +26 -0
  125. data/spec/weeler/action_view/helpers/translation_helper_spec.rb +16 -0
  126. data/spec/weeler/i18n/backend/weeler/dedupe_spec.rb +21 -0
  127. data/spec/weeler/i18n/backend/weeler/exporter_spec.rb +2 -4
  128. data/spec/weeler/i18n/backend/weeler/importer_spec.rb +5 -2
  129. data/spec/weeler/i18n/backend/weeler_spec.rb +121 -23
  130. data/weeler.gemspec +15 -7
  131. metadata +207 -85
  132. data/.DS_Store +0 -0
  133. data/app/assets/stylesheets/weeler/vendor/bootstrap.css +0 -6086
  134. data/app/views/layouts/weeler/base.html.erb +0 -40
  135. data/app/views/weeler/home/about.html.erb +0 -16
  136. data/app/views/weeler/home/index.html.erb +0 -4
  137. data/app/views/weeler/shared/_flash.html.erb +0 -17
  138. data/app/views/weeler/shared/_footer.html.erb +0 -3
  139. data/app/views/weeler/shared/_header_navbar.html.erb +0 -18
  140. data/app/views/weeler/shared/_side_navbar.html.erb +0 -14
  141. data/app/views/weeler/translations/_form.html.erb +0 -43
  142. data/app/views/weeler/translations/_translation.html.erb +0 -11
  143. data/app/views/weeler/translations/edit.html.erb +0 -19
  144. data/app/views/weeler/translations/index.html.erb +0 -103
  145. data/app/views/weeler/translations/new.html.erb +0 -19
  146. data/lib/generators/weeler/templates/controllers/weeler/example_controller.rb.example +0 -7
  147. data/lib/i18n/backend/weeler/exporter/active_record.rb +0 -37
  148. data/lib/i18n/backend/weeler/exporter/active_record_relation.rb +0 -38
  149. data/lib/i18n/backend/weeler/store_procs.rb +0 -39
  150. data/lib/weeler/route_mapper.rb +0 -41
  151. data/spec/dummy/db/production.sqlite3 +0 -0
  152. data/spec/dummy/log/test.log +0 -88454
@@ -0,0 +1,13 @@
1
+ .header.container
2
+ .row
3
+ .col-lg-12.col-md-12
4
+ .page-header
5
+ %h1 New translation
6
+ .edit_user.container
7
+ .row
8
+ .col-lg-12.col-md-12
9
+ = form_for @translation, {url: weeler_translations_path, html: {class: "form-horizontal", role: "form"}} do |f|
10
+ = render partial: "form", locals: {translation: @translation, f: f}
11
+ %p.text-center
12
+ %button.btn.btn-primary{type: "submit"} Save
13
+ %a.btn.btn-link{href: weeler_translations_path} Cancel
data/lib/.DS_Store CHANGED
Binary file
Binary file
@@ -29,14 +29,18 @@ module Weeler
29
29
  end
30
30
  end
31
31
 
32
- #def install_models
33
- # copy_files 'models', 'app/models'
34
- #end
35
-
36
32
  def install_views
37
33
  copy_files 'views', 'app/views'
38
34
  end
39
35
 
36
+ def install_javascripts_assets
37
+ copy_files 'assets/javascripts', 'lib/assets/javascripts'
38
+ end
39
+
40
+ def install_stylesheets_assets
41
+ copy_files 'assets/stylesheets', 'lib/assets/stylesheets'
42
+ end
43
+
40
44
  def install_controllers
41
45
  copy_files 'controllers', 'app/controllers'
42
46
  end
@@ -45,37 +49,37 @@ module Weeler
45
49
  def add_weeler_routes
46
50
  weeler_routes = "mount_weeler_at \"weeler\" do \n"
47
51
  weeler_routes << " # weeler_resources :example, include_in_weeler_menu: true \n"
52
+ weeler_routes << " # Also you orderable and imageable concerns \n"
48
53
  weeler_routes << " end"
49
54
  route weeler_routes
50
55
  end
51
56
 
57
+ private
52
58
 
53
- private
54
-
55
- def copy_files subdir, dest_dir
56
- raise ArgumEnterror unless subdir.is_a? String
57
- raise ArgumEnterror unless dest_dir.is_a? String
58
- raise ArgumetnError if subdir.blank?
59
- raise ArgumetnError if dest_dir.blank?
59
+ def copy_files subdir, dest_dir
60
+ raise ArgumEnterror unless subdir.is_a? String
61
+ raise ArgumEnterror unless dest_dir.is_a? String
62
+ raise ArgumetnError if subdir.blank?
63
+ raise ArgumetnError if dest_dir.blank?
60
64
 
61
- get_file_list(subdir).each do |image|
62
- copy_file [subdir, image].join('/'), [dest_dir, image].join('/')
63
- end
65
+ get_file_list(subdir).each do |image|
66
+ copy_file [subdir, image].join('/'), [dest_dir, image].join('/')
64
67
  end
68
+ end
65
69
 
66
- def get_file_list subdir
67
- raise ArgumentError unless subdir.is_a? String
68
- raise ArgumetnError if subdir.blank?
69
- dir = get_current_dir
70
- search_path = [dir, 'templates', subdir].join('/') + '/'
71
- file_list = Dir.glob(search_path + '**/*').map { |filename| File.directory?(filename) ? nil : filename.sub(search_path, '') }
72
- file_list.delete nil
73
- return file_list
74
- end
70
+ def get_file_list subdir
71
+ raise ArgumentError unless subdir.is_a? String
72
+ raise ArgumetnError if subdir.blank?
73
+ dir = get_current_dir
74
+ search_path = [dir, 'templates', subdir].join('/') + '/'
75
+ file_list = Dir.glob(search_path + '**/*').map { |filename| File.directory?(filename) ? nil : filename.sub(search_path, '') }
76
+ file_list.delete nil
77
+ return file_list
78
+ end
75
79
 
76
- def get_current_dir
77
- File.dirname(__FILE__)
78
- end
80
+ def get_current_dir
81
+ File.dirname(__FILE__)
82
+ end
79
83
 
80
84
  end
81
85
  end
@@ -0,0 +1,2 @@
1
+ // Here you can implement your JS code
2
+ // For example //= require redactor-rails
@@ -0,0 +1,4 @@
1
+ /*
2
+ Here you can implement your css code
3
+ // For example *= require redactor-rails
4
+ */
@@ -0,0 +1,6 @@
1
+ module Weeler
2
+ class ApplicationController < ::ApplicationController
3
+ # Here you can define controller methods for using in weeler.
4
+ # Also you can set before_filter 's and define those methods.
5
+ end
6
+ end
@@ -1,7 +1,35 @@
1
1
  Weeler.setup do |config|
2
2
 
3
+ # Use Weeler I18n transaltion engine (ActiveRecord store).
4
+ #
3
5
  # config.use_weeler_i18n = true
4
- # config.create_missing_translations
5
- # config.required_user_method = nil
6
+ # config.create_missing_translations = true # Create translation in database if it missing
7
+
8
+ # Require user method weeler use in before filter. (e.g. require_user)
9
+ #
10
+ # config.required_user_method = nil
11
+
12
+ # You can define logout path (e.g. "/logout")
13
+ #
14
+ # config.logout_path = nil
15
+
16
+ # Menu items in Content section. There you must create resources in app.
17
+ #
18
+ # config.content_menu_items = []
19
+
20
+ # If you don't want to create model in your app and use only single page CMS, then you should use this property.
21
+ # e.g. {about: ['about_us.title', 'about_us.about_text_html', 'about_us.our_company', 'about_us.our_company_text_html']}
22
+ #
23
+ #
24
+ # config.static_sections = [] # Menu items in Content section under Static. Resourses uses I18n translaitons
25
+
26
+ # Menu items in Administration section. There you must create resources in app.
27
+ #
28
+ # config.administration_menu_items = []
29
+
30
+ # You can exclude translation groups from weeler administration
31
+ # By default theres already excluded [:activerecord, :attributes, :helpers, :views, :i18n, :weeler]
32
+ #
33
+ # config.excluded_i18n_groups += ["number", "date", "scopes", "named_routes", "named_routes_path", "path_names", "errors", "routes"]
6
34
 
7
35
  end
@@ -0,0 +1,14 @@
1
+ class CreateWeelerSeos < ActiveRecord::Migration
2
+ def change
3
+ create_table :weeler_seos do |t|
4
+ t.string :title
5
+ t.text :description
6
+ t.text :keywords
7
+ t.string :section
8
+ t.references :seoable, index: true, polymorphic: true
9
+
10
+ t.timestamps
11
+ end
12
+ add_index :weeler_seos, :section
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ class CreateWeelerSettings < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :settings do |t|
4
+ t.string :var, :null => false
5
+ t.text :value, :null => true
6
+ t.integer :thing_id, :null => true
7
+ t.string :thing_type, :limit => 30, :null => true
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :settings, [ :thing_type, :thing_id, :var ], :unique => true
12
+ end
13
+
14
+ def self.down
15
+ drop_table :settings
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ class TranslateWeelerSeos < ActiveRecord::Migration
2
+ def self.up
3
+ Weeler::Seo.create_translation_table!({
4
+ title: :string,
5
+ description: :text,
6
+ keywords: :text
7
+ }, {
8
+ :migrate_data => true
9
+ })
10
+ end
11
+
12
+ def self.down
13
+ Weeler::Seo.drop_translation_table! :migrate_data => true
14
+ end
15
+ end
@@ -1,14 +1,30 @@
1
1
  require 'i18n/backend/base'
2
2
  require 'i18n/backend/weeler/translation'
3
+ require 'i18n/backend/weeler/dedupe'
4
+ require 'i18n/backend/weeler/html_checker'
3
5
  require 'i18n/backend/weeler/exporter'
4
6
  require 'i18n/backend/weeler/importer'
5
7
 
6
8
  module I18n
7
9
  module Backend
10
+ # I18n backend by weeler for storing translations in database and caching.
11
+ #
12
+ # It implements storing all translation in <tt>Translation</tt> active record moduel table.
13
+ # When application is running, it stores translation in cache for better performance.
14
+ #
15
+ # Also this backend provides extra moduls:
16
+ # * HTML checher, for storing html translations.
17
+ # * exporter - for export all translations in xlsx
18
+ # * importer - for importing all translations
19
+ # * Dedupe - you should use if you find duplicated keys
20
+ #
8
21
  class Weeler
22
+ attr_accessor :i18n_cache
23
+
9
24
  PLURAL_KEYS = ["zero", "one", "other"]
10
25
 
11
- autoload :StoreProcs, 'i18n/backend/weeler/store_procs'
26
+ autoload :HtmlChecker, 'i18n/backend/weeler/html_checker'
27
+ autoload :Translation, 'i18n/backend/weeler/dedupe'
12
28
  autoload :Translation, 'i18n/backend/weeler/translation'
13
29
  autoload :Exporter, 'i18n/backend/weeler/exporter'
14
30
  autoload :Importer, 'i18n/backend/weeler/importer'
@@ -16,59 +32,123 @@ module I18n
16
32
  module Implementation
17
33
  include Base, Flatten
18
34
 
19
- def available_locales
20
- begin
21
- Translation.available_locales
22
- rescue ::ActiveRecord::StatementInvalid
23
- []
24
- end
25
- end
26
-
27
35
  def store_translations(locale, data, options = {})
28
36
  escape = options.fetch(:escape, true)
29
37
  flatten_translations(locale, data, escape, false).each do |key, value|
30
38
  Translation.locale(locale).lookup(expand_keys(key)).delete_all
31
- Translation.create(:locale => locale.to_s, :key => key.to_s, :value => value)
39
+ Translation.create(locale: locale.to_s, key: key.to_s, value: value)
40
+ end
41
+ end
42
+
43
+ def reload_cache
44
+ i18n_cache.clear
45
+
46
+ Translation.all.each do |translation|
47
+ i18n_cache.write [translation.locale, translation.key], translation if translation.value.present?
32
48
  end
49
+
50
+ i18n_cache.write('UPDATED_AT', Settings.i18n_updated_at) if ActiveRecord::Base.connection.table_exists?('settings')
33
51
  end
34
52
 
35
53
  protected
36
54
 
37
55
  def lookup(locale, key, scope = [], options = {})
38
56
  key = normalize_flat_keys(locale, key, scope, options[:separator])
57
+ return lookup_in_cache(locale, key, scope, options)
58
+ end
59
+
60
+ private
61
+
62
+ def lookup_in_cache locale, key, scope = [], options = {}
63
+ # reload cache if cache timestamp differs from last translations update
64
+ reload_cache if ((!ActiveRecord::Base.connection.table_exists?('settings')) || i18n_cache.read('UPDATED_AT') != Settings.i18n_updated_at)
65
+
66
+ return nil if i18n_cache.read([:missing, [locale, key]])
67
+
68
+ keys = expand_keys key
69
+
70
+ keys.reverse.each do |check_key|
71
+
72
+ result = i18n_cache.read([locale, check_key])
73
+
74
+ return result.value unless result.blank?
75
+ end
76
+
77
+ # mark translation as missing
78
+ i18n_cache.write([:missing, [locale, key]], true)
79
+
80
+ if ::Weeler.create_missing_translations
81
+ return store_empty_translation locale, key, options
82
+ else
83
+ return nil
84
+ end
85
+ end
86
+
87
+ def lookup_in_database locale, key, scope = [], options = {}
39
88
  result = Translation.locale(locale).lookup(key).load
40
89
 
41
90
  if result.empty?
42
91
  if ::Weeler.create_missing_translations
43
- interpolations = options.keys - I18n::RESERVED_KEYS
44
- keys = options[:count] ? PLURAL_KEYS.map { |k| [key, k].join(FLATTEN_SEPARATOR) } : [key]
45
- keys.each { |key| store_translation(locale, key, interpolations) }
92
+ return store_empty_translation locale, key, options
93
+ else
94
+ return nil
46
95
  end
47
- nil
48
96
  elsif result.first.key == key
49
- result.first.value
97
+ translation = result.first
98
+ if translation.value.blank?
99
+ fallback_value = fallback_backend_translation locale, key
100
+ translation.update_attributes value: fallback_value if fallback_value.present?
101
+ end
102
+ return translation.value
50
103
  else
51
104
  chop_range = (key.size + FLATTEN_SEPARATOR.size)..-1
52
105
  result = result.inject({}) do |hash, r|
53
106
  hash[r.key.slice(chop_range)] = r.value
54
107
  hash
55
108
  end
56
- result.deep_symbolize_keys
109
+ return result.deep_symbolize_keys
57
110
  end
58
111
  end
59
112
 
60
113
  # For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz']
61
- def expand_keys(key)
62
- key.to_s.split(FLATTEN_SEPARATOR).inject([]) do |keys, key|
114
+ def expand_keys(not_expanded_key)
115
+ not_expanded_key.to_s.split(FLATTEN_SEPARATOR).inject([]) do |keys, key|
63
116
  keys << [keys.last, key].compact.join(FLATTEN_SEPARATOR)
64
117
  end
65
118
  end
66
119
 
67
120
  # Store single empty translation
68
- def store_translation(locale, key, interpolations)
69
- translation = Weeler::Translation.new :locale => locale.to_s, :key => key
70
- translation.interpolations = interpolations
71
- translation.save
121
+ def store_empty_translation locale, singular_key, options
122
+ return_value = nil
123
+ interpolations = options.keys - I18n::RESERVED_KEYS
124
+
125
+ keys = options[:count] ? PLURAL_KEYS.map { |k| [singular_key, k].join(FLATTEN_SEPARATOR) } : [singular_key]
126
+
127
+
128
+ keys.each do |key|
129
+ translation = Weeler::Translation.find_or_initialize_by locale: locale.to_s, key: key
130
+ translation.interpolations = interpolations
131
+ fallback_value = fallback_backend_translation locale, key
132
+ if fallback_value.present?
133
+ translation.value = fallback_value
134
+ translation.save
135
+ reload_cache
136
+ else
137
+ translation.save
138
+ end
139
+ return_value = translation.value
140
+ end
141
+ return_value
142
+ end
143
+
144
+
145
+ def fallback_backend_translation locale, key
146
+ if I18n.backend.backends.size > 1
147
+ alternative_backend = I18n.backend.backends[1]
148
+ alternative_backend.send(:lookup, locale, key)
149
+ else
150
+ nil
151
+ end
72
152
  end
73
153
 
74
154
  end
@@ -0,0 +1,31 @@
1
+ # This module is intended to be mixed into the Weeler backend to allow
2
+ # remove all dublicates from translation table
3
+ #
4
+ # I18n.backend = I18n::Backend::Weeler.new
5
+ # I18n::Backend::Weeler::Translation.send(:include, I18n::Backend::Weeler::Dedupe)
6
+
7
+ module I18n
8
+ module Backend
9
+ class Weeler
10
+ module Dedupe
11
+ extend ActiveSupport::Concern
12
+
13
+ module ClassMethods
14
+ def dedupe
15
+ # find all models and group them on keys which should be common
16
+ grouped = all.group_by{|model| [model.locale,model.key] }
17
+ grouped.values.each do |duplicates|
18
+ # the first one we want to keep right?
19
+ first_one = duplicates.shift # or pop for last one
20
+ # if there are any more left, they are duplicates
21
+ # so delete all of them
22
+ duplicates.each{|double| double.destroy} # duplicates can now be destroyed
23
+ end
24
+ end
25
+ end
26
+
27
+ Translation.send(:include, Dedupe)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,7 +1,19 @@
1
+ # Weeler I18n backend export helper module.
2
+ #
3
+ # Export all translations in excel (xlsx)
4
+ #
5
+ # def export
6
+ # respond_to do |format|
7
+ # format.xlsx do
8
+ # outstrio = StringIO.new
9
+ # outstrio.write(I18n::Backend::Weeler::Translation.all.as_xlsx_package.to_stream.read)
10
+ # send_data(outstrio.string, filename: "translations" + '.xlsx')
11
+ # end
12
+ # end
13
+ # end
14
+
1
15
  begin
2
16
  require 'axlsx'
3
- require 'i18n/backend/weeler/exporter/active_record'
4
- require 'i18n/backend/weeler/exporter/active_record_relation'
5
17
  rescue LoadError => e
6
18
  puts "can't use Exporter because: #{e.message}"
7
19
  end
@@ -9,10 +21,60 @@ end
9
21
  module I18n
10
22
  module Backend
11
23
  class Weeler
24
+ module Exporter
25
+ extend ActiveSupport::Concern
26
+
27
+ module ClassMethods
28
+
29
+ # Prepare xlsx package from current scope
30
+ # Stores all translations in translations worksheet.
31
+ def as_xlsx_package
32
+ package = Axlsx::Package.new
33
+ package.use_shared_strings = true
34
+
35
+ sheet = package.workbook.add_worksheet(name: "translations")
36
+
37
+ locales = I18n.available_locales
38
+ sheet.add_row(Translation.title_row(locales))
12
39
 
13
- Translation.send(:include, Exporter::ActiveRecord)
14
- ActiveRecord::Relation::ActiveRecord_Relation_I18n_Backend_Weeler_Translation.send(:include, Exporter::ActiveRecordRelation)
40
+ types = []
41
+ (locales.size + 1).times { types << :string }
15
42
 
43
+ included_keys = []
44
+
45
+ self.current_scope.each do |translation|
46
+ unless included_keys.include? translation.key
47
+ sheet.add_row(Translation.translation_row_by_key_and_locales(translation.key, locales), types: types)
48
+ included_keys << translation.key
49
+ end
50
+ end
51
+ return package
52
+ end
53
+
54
+ def title_row locales
55
+ row = [ 'Key' ]
56
+ locales.each do |locale|
57
+ row.push(locale.capitalize)
58
+ end
59
+ row
60
+ end
61
+
62
+ def translation_row_by_key_and_locales key, locales
63
+ row = [ key ]
64
+ locales.each do |locale|
65
+ result = Translation.locale(locale).lookup(key).load
66
+ if result.first.present?
67
+ row.push(result.first.value)
68
+ else
69
+ row.push("")
70
+ end
71
+ end
72
+ row
73
+ end
74
+ end
75
+
76
+ Translation.send(:include, self)
77
+ end
16
78
  end
17
79
  end
18
- end
80
+ end