Module: Models::Translatable

Extended by:
ActiveSupport::Concern, Mobility
Included in:
CatalogItem, Item, ProductLine, ProductSpecification, Video
Defined in:
app/concerns/models/translatable.rb

Overview

Mobility-based translation handling for translated models: guards against
NULL translations containers and compacts redundant sub-locale values
(e.g. en-US identical to en) into the base locale before validation.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#do_not_compact_translation_containerObject

Returns the value of attribute do_not_compact_translation_container.



14
15
16
# File 'app/concerns/models/translatable.rb', line 14

def do_not_compact_translation_container
  @do_not_compact_translation_container
end

Class Method Details

.skip_compaction_for(*attributes) ⇒ void

This method returns an undefined value.

Marks attributes whose regional translations must never be folded into
the base locale during compaction.

Parameters:

  • attributes (Array<Symbol, String>)

    attribute names to skip



40
41
42
# File 'app/concerns/models/translatable.rb', line 40

def skip_compaction_for(*attributes)
  self.attributes_to_skip_compaction = attributes.map(&:to_s)
end

Instance Method Details

#compact_translation_containervoid

This method returns an undefined value.

This method cleans up the translation container, prevents blanks from saving
Also if a locale and its fallback are the same, only keep the fallback
e.g if you have en-US and an en translation which are the same, we don't need
to store and maintain en-US

This works with column_fallback by moving values from sub-locales (en-US, en-CA)
to their base locale (en) and then removing the sub-locale.

A regional value that genuinely DIFFERS from its base is left alone: folding
it would overwrite the base with regional copy and lose the distinction.
Only the base locale is affected — I18n.fallbacks[:fr] is [:fr], so fr
and en are never compaction candidates in the first place.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/concerns/models/translatable.rb', line 61

def compact_translation_container
  return if translations.blank?

  translations.dup.each do |translation_locale, translated_values|
    locale_sym = translation_locale.to_sym
    _, locale_fallback = I18n.fallbacks[locale_sym]

    # Skip if there's no fallback or if this is already a base locale
    next unless locale_fallback.present? && locale_sym != locale_fallback

    # For sub-locales like en-US, en-CA that fall back to en:
    # Move their values to the fallback locale and remove the sub-locale
    moved_keys = translated_values.filter_map do |key, value|
      # Skip if this attribute is marked to skip compaction
      next if attributes_to_skip_compaction.include?(key.to_s)

      backend_accessor = begin
        send(:"#{key}_backend")
      rescue StandardError
        nil
      end
      next if backend_accessor.nil?

      # Only fold a REDUNDANT regional value into its base. This used to write
      # unconditionally, so a genuinely distinct regional value (a Canadian
      # name differing from the US one) silently overwrote the base — the
      # opposite of the "if a locale and its fallback are the same, only keep
      # the fallback" intent documented above, and the reason callers reach
      # for do_not_compact_translation_container.
      base_value = backend_accessor.read(locale_fallback)
      next unless base_value.blank? || base_value == value

      # Write the value to the fallback locale (this will use column_fallback if configured)
      backend_accessor.write(locale_fallback, value)

      # Clear the sub-locale value
      backend_accessor.write(locale_sym, nil)
      key
    end

    # Remove the now-empty sub-locale from translations, but only once every
    # key really moved — a key retained above because it differs from its base
    # still needs its container entry.
    if moved_keys.size == translated_values.keys.size
      Rails.logger.info "Compacting translation locale: #{translation_locale} from #{self.class.name} #{id} (moved to fallback #{locale_fallback})"
      translations.delete(translation_locale)
    end
  end

  @do_not_compact_translation_container = true
end