para-i18n 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5c764053878b2cba8adb8ed576917295f425e49
4
- data.tar.gz: 872b3159ccda1fbc26dee0522318de70ea361bbe
3
+ metadata.gz: bffd454f68783dc4413fab9cff53b3c899b0f908
4
+ data.tar.gz: 783928239cee302a6e963057cc8f68becd744c8c
5
5
  SHA512:
6
- metadata.gz: bec576e0856d55fef7fc941406886ed633cb9687c529e1958134bb39672b902c2ce9301ec47e5198a595e9ab8180c4291ef44a525d30bd135480c08067eac215
7
- data.tar.gz: ef5efc77c67ec25580522c9a1abc4cc881012ac58a8b09c4c9e04be2856e996c82044e85f81e004f1224da6eefbdd81fd73fae63558fa61232278b17c88a8f2f
6
+ metadata.gz: aa848897075a6d40a94d92451e3b660e1c18aade214e90ca0a394d066b58dd03f3d143f6457f29e469b805215f37e451f21a093443f67cc7298b2bd8a7a047de
7
+ data.tar.gz: 3ce0e4a22d344090a9d98ba86fb505b778549e2a230d8e981b9288af8b0d16da180e8993d1b828c7d795d867092d01575c3a040e43e74f6f2c3c05ade9eaa975
@@ -2,6 +2,8 @@
2
2
  = target_locale_select
3
3
 
4
4
  = form.fieldset do
5
+ = I18n.with_locale(@target_locale) { form.input :_disabled_for_locale, as: :boolean, label: t("para.i18n.disabled_for_locale") }
6
+
5
7
  - translated_model_fields_for(resource.class).each do |field|
6
8
  = form.input field.field_name, as: :i18n, locale: @target_locale
7
9
 
@@ -4,3 +4,4 @@ fr:
4
4
  translate: "Traduire"
5
5
  translation: "Traduction"
6
6
  choose_locale: "Traduire en :"
7
+ disabled_for_locale: "Masquer pour la langue en cours"
@@ -8,6 +8,11 @@ require 'para/i18n/model'
8
8
  require 'para/i18n/resources_table'
9
9
  require 'para/i18n/resources_buttons'
10
10
 
11
+ require 'para/i18n/attribute_translation'
12
+ require 'para/i18n/attribute_translation/base'
13
+ require 'para/i18n/attribute_translation/simple_attribute'
14
+ require 'para/i18n/attribute_translation/attachment'
15
+
11
16
  require 'para/i18n/friendly_id'
12
17
 
13
18
  require 'para/i18n/i18n_input'
@@ -0,0 +1,26 @@
1
+ module Para
2
+ module I18n
3
+ # This module redirects the reading and writing of the translated attributes to the
4
+ # right attribute translation class
5
+ #
6
+ module AttributeTranslation
7
+ def self.read(locale, resource, attribute)
8
+ attribute_translation_class(locale, resource.class, attribute).read(resource)
9
+ end
10
+
11
+ def self.write(locale, resource, attribute, value)
12
+ attribute_translation_class(locale, resource.class, attribute).write(resource, value)
13
+ end
14
+
15
+ def self.attribute_translation_class(locale, model, attribute)
16
+ attribute_translation_class = if AttributeTranslation::Attachment.matches?(model, attribute)
17
+ AttributeTranslation::Attachment.new(locale, model, attribute)
18
+ else
19
+ AttributeTranslation::SimpleAttribute.new(locale, model, attribute)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+
@@ -0,0 +1,58 @@
1
+ module Para
2
+ module I18n
3
+ module AttributeTranslation
4
+ class Attachment < Para::I18n::AttributeTranslation::Base
5
+ def read(resource)
6
+ attachment = resource.public_send(reflection_name)
7
+
8
+ if default_locale? || attachment.attached? || !fallback_locale
9
+ attachment
10
+ elsif fallback_locale
11
+ self.class.new(fallback_locale, model, attribute).read(resource)
12
+ end
13
+ end
14
+
15
+ def write(resource, value)
16
+ resource.public_send(:"#{reflection_name}=", value)
17
+ end
18
+
19
+ def self.matches?(model, attribute)
20
+ model.reflections.key?("#{attribute}_attachment") &&
21
+ model.reflections.key?("#{attribute}_blob")
22
+ end
23
+
24
+ def self.prepare(model, attribute)
25
+ return unless matches?(model, attribute)
26
+
27
+ (I18n.available_locales - [I18n.default_locale]).each do |locale|
28
+ new(locale, model, attribute).prepare_translated_attachment
29
+ end
30
+ end
31
+
32
+ def prepare_translated_attachment
33
+ model.alias_method(untranslated_getter_name, attribute)
34
+ model.alias_method(untranslated_setter_name, :"#{attribute}=")
35
+ model.has_one_attached(reflection_name)
36
+ end
37
+
38
+ private
39
+
40
+ def reflection_name
41
+ @reflection_name ||= if default_locale?
42
+ untranslated_getter_name
43
+ else
44
+ [attribute, locale, "translation"].join("_")
45
+ end
46
+ end
47
+
48
+ def untranslated_getter_name
49
+ @untranslated_getter_name ||= :"untranslated_#{attribute}"
50
+ end
51
+
52
+ def untranslated_setter_name
53
+ @untranslated_setter_name ||= :"untranslated_#{attribute}="
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module Para
2
+ module I18n
3
+ module AttributeTranslation
4
+ class Base
5
+ attr_reader :locale, :model, :attribute
6
+
7
+ def initialize(locale, model, attribute)
8
+ @locale = locale.to_s
9
+ @model = model
10
+ @attribute = attribute.to_s
11
+ end
12
+
13
+ def default_locale?
14
+ @default_locale ||= locale == I18n.default_locale.to_s
15
+ end
16
+
17
+ def fallback_locale
18
+ @fallback_locale ||= Para::I18n::Fallbacks.i18n_fallback_for(locale.to_sym)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module Para
2
+ module I18n
3
+ module AttributeTranslation
4
+ class SimpleAttribute < Para::I18n::AttributeTranslation::Base
5
+ def read(resource)
6
+ # Directly read the plain column / store accessor if the current locale is the
7
+ # default one..
8
+ if default_locale? && attribute != "_disabled_for_locale"
9
+ return resource.read_plain_or_store_attribute(attribute)
10
+ end
11
+
12
+ translations = resource.model_translations[locale]
13
+
14
+ if translations && (translation = translations[attribute])
15
+ translation
16
+ elsif fallback_locale
17
+ # If no translation was returned, try to fallback to the next locale
18
+ self.class.new(fallback_locale, model, attribute).read(resource)
19
+ end
20
+ end
21
+
22
+ def write(resource, value)
23
+ if default_locale? && attribute != "_disabled_for_locale"
24
+ return resource.write_plain_or_store_attribute(attribute, value)
25
+ end
26
+
27
+ # did not us ||= here to fix first assignation.
28
+ # Did not investigate on why ||= does not work
29
+ resource.model_translations[locale] = {} unless resource.model_translations[locale]
30
+ resource.model_translations[locale][attribute] = value
31
+ resource._translations_will_change!
32
+ value
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -8,6 +8,10 @@ module Para
8
8
  end
9
9
 
10
10
  initializer 'para.i18n.extend_para_routes' do
11
+ ::Para.config.routes.extend_routes_for(:component) do
12
+ resource :translation, only: [:edit, :update], controller: '/para/admin/translations'
13
+ end
14
+
11
15
  ::Para.config.routes.extend_routes_for(:crud_component) do
12
16
  resource :translation, only: [:edit, :update], controller: '/para/admin/translations'
13
17
  end
@@ -37,7 +41,7 @@ module Para
37
41
  icon: 'globe',
38
42
  label: ::I18n.t('para.i18n.translate'),
39
43
  url: @component.relation_path(resource, :translation, action: :edit)
40
- } if resource.class.translates?
44
+ } if resource.class.translates? && can?(:translate, resource)
41
45
  end
42
46
  end
43
47
  end
@@ -1,12 +1,21 @@
1
1
  module Para
2
2
  module I18n
3
3
  module Fallbacks
4
+ mattr_accessor :_disable_fallbacks
5
+
6
+ def self.without_i18n_fallbacks(&block)
7
+ self._disable_fallbacks = true
8
+ block.call
9
+ ensure
10
+ self._disable_fallbacks = false
11
+ end
12
+
4
13
  def self.i18n_fallback_for(locale)
5
- return unless ::I18n.respond_to?(:fallbacks)
14
+ return if _disable_fallbacks || !::I18n.respond_to?(:fallbacks)
6
15
 
7
16
  if (fallbacks = ::I18n.fallbacks[locale]) && fallbacks.length > 1
8
17
  fallbacks[1]
9
- else
18
+ elsif locale != ::I18n.default_locale
10
19
  ::I18n.default_locale
11
20
  end
12
21
  end
@@ -10,6 +10,15 @@ module FriendlyId
10
10
  def included(model_class)
11
11
  model_class.extend(ClassMethods)
12
12
 
13
+ # Support for I18n finder methods on ActiveRecord::Relation of the including
14
+ # model. Example :
15
+ model_class.instance_eval do
16
+ relation.class.send(:include, ClassMethods)
17
+ if (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) || ActiveRecord::VERSION::MAJOR == 5
18
+ model_class.send(:extend, ClassMethods)
19
+ end
20
+ end
21
+
13
22
  # Support for friendly finds on associations for Rails 4.0.1 and above.
14
23
  #
15
24
  # Borrowed from FriendlyId::Finders module
@@ -9,33 +9,17 @@ module Para
9
9
  end
10
10
 
11
11
  def read_translated_attribute(field, locale = ::I18n.locale)
12
- return read_plain_or_store_attribute(field) if locale == ::I18n.default_locale
13
-
14
- if model_translations[locale.to_s]
15
- if (translation = model_translations[locale.to_s][field.to_s])
16
- return translation
17
- end
18
- end
19
-
20
- # If no translation was returned, try to fallback to the next locale
21
- if (fallback_locale = Fallbacks.i18n_fallback_for(locale))
22
- read_translated_attribute(field, fallback_locale)
23
- end
12
+ Para::I18n::AttributeTranslation.read(locale, self, field)
24
13
  end
25
14
 
26
- def write_translated_attribute field, value, locale = ::I18n.locale
27
- return write_plain_or_store_attribute(field, value) if locale == ::I18n.default_locale
28
-
29
- # did not us ||= here to fix first assignation.
30
- # Did not investigate on why ||= does not work
31
- model_translations[locale.to_s] = {} unless model_translations[locale.to_s]
32
- model_translations[locale.to_s][field.to_s] = value
15
+ def write_translated_attribute(field, value, locale = ::I18n.locale)
16
+ Para::I18n::AttributeTranslation.write(locale, self, field, value)
33
17
  end
34
18
 
35
19
  def model_translations
36
20
  unless respond_to?(:_translations)
37
21
  raise "The model #{ self.class.name } is not translatable. " +
38
- "Please run `rails g i18n_admin:translate #{ self.model_name.element }` " +
22
+ "Please run `rails g para:i18n:translate #{ self.model_name.element }` " +
39
23
  "generator to create the model's migration."
40
24
  end
41
25
 
@@ -43,11 +27,20 @@ module Para
43
27
  end
44
28
 
45
29
  def translation_for(locale)
46
- model_translations[locale.to_s] || {}
30
+ case locale.to_sym
31
+ when I18n.default_locale then default_locale_translations
32
+ else model_translations[locale.to_s] || {}
33
+ end.with_indifferent_access
47
34
  end
48
35
 
49
- private
36
+ def disabled_for_locale?
37
+ self.class.translates? && _disabled_for_locale
38
+ end
50
39
 
40
+ # This method allows reading an attribute from the ActiveRecord table, whether it's
41
+ # a plain column of the table, or a field of a store (hash, json etc.) of the table,
42
+ # accessed through the ActiveRecord.store_accessor interface.
43
+ #
51
44
  def read_plain_or_store_attribute(field)
52
45
  if plain_column?(field)
53
46
  read_attribute(field)
@@ -68,6 +61,8 @@ module Para
68
61
  end
69
62
  end
70
63
 
64
+ private
65
+
71
66
  def plain_column?(field)
72
67
  self.class.columns_hash.key?(field.to_s)
73
68
  end
@@ -80,25 +75,49 @@ module Para
80
75
  store.first if store
81
76
  end
82
77
 
78
+ def default_locale_translations
79
+ translated_attribute_names.each_with_object({}) do |attribute_name, hash|
80
+ hash[attribute_name] = read_plain_or_store_attribute(attribute_name)
81
+ end
82
+ end
83
+
83
84
  module ClassMethods
84
85
  def translates(*fields)
85
86
  self.translated_attribute_names = fields.map(&:to_sym)
86
87
  self.translatable = true
87
88
 
88
89
  fields.each do |field|
89
- define_method field do
90
- read_translated_attribute(field)
91
- end
90
+ prepare_attribute_translation(field)
91
+ end
92
+
93
+ define_method(:_disabled_for_locale) do
94
+ read_translated_attribute(:_disabled_for_locale) == "1"
95
+ end
92
96
 
93
- define_method :"#{ field }=" do |value|
94
- write_translated_attribute(field, value)
95
- end
97
+ define_method(:_disabled_for_locale=) do |value|
98
+ write_translated_attribute(:_disabled_for_locale, value)
96
99
  end
97
100
  end
98
101
 
99
102
  def translates?
100
103
  translatable
101
104
  end
105
+
106
+ private
107
+
108
+ def prepare_attribute_translation(attribute)
109
+ # Let the Para::I18n::AttributeTranslation::Attachment module handle
110
+ # ActiveStorage attachment fields translation preparation.
111
+ Para::I18n::AttributeTranslation::Attachment.prepare(self, attribute)
112
+
113
+ define_method(attribute) do
114
+ read_translated_attribute(attribute)
115
+ end
116
+
117
+ define_method(:"#{attribute}=") do |value|
118
+ write_translated_attribute(attribute, value)
119
+ end
120
+ end
102
121
  end
103
122
  end
104
123
  end
@@ -2,10 +2,10 @@ module Para
2
2
  module I18n
3
3
  module ResourcesButtons
4
4
  def translate_button(resource)
5
- return unless resource.class.translates?
5
+ return unless resource.class.translates? && view.can?(:translate, resource)
6
6
 
7
7
  path = component.relation_path(resource, :translation, action: :edit)
8
- options = { class: 'btn btn-info' }
8
+ options = { class: 'btn btn-default' }
9
9
 
10
10
  view.link_to(path, options) do
11
11
  content_tag(:i, '', class: 'fa fa-globe')
@@ -1,5 +1,5 @@
1
1
  module Para
2
2
  module I18n
3
- VERSION = "0.3.1"
3
+ VERSION = "0.4.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: para-i18n
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentin Ballestrino
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-15 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: para
@@ -94,6 +94,10 @@ files:
94
94
  - lib/generators/para/i18n/translate/templates/model_migration.rb.erb
95
95
  - lib/generators/para/i18n/translate/translate_generator.rb
96
96
  - lib/para/i18n.rb
97
+ - lib/para/i18n/attribute_translation.rb
98
+ - lib/para/i18n/attribute_translation/attachment.rb
99
+ - lib/para/i18n/attribute_translation/base.rb
100
+ - lib/para/i18n/attribute_translation/simple_attribute.rb
97
101
  - lib/para/i18n/engine.rb
98
102
  - lib/para/i18n/fallbacks.rb
99
103
  - lib/para/i18n/friendly_id.rb