module Maglev::Translatable
def add_presence_validator(attributes)
def add_presence_validator(attributes) validate do attributes.each do |attribute| errors.add(attribute, :blank) if public_send(attribute).blank? end end end
def order_by_translated(attr, direction)
def order_by_translated(attr, direction) order(translated_arel_attribute(attr, Maglev::I18n.current_locale) => direction) end
def setup_accessors(attr)
def setup_accessors(attr) define_method("#{attr}=") do |value| public_send("#{attr}_translations=", translations_for(attr).merge(Maglev::I18n.current_locale.to_s => value)) end define_method(attr) { translations_for(attr)[Maglev::I18n.current_locale.to_s] } define_method("default_#{attr}") { translations_for(attr)[Maglev::I18n.default_locale.to_s] } end
def translate_attr_in(attr, locale, source_locale)
def translate_attr_in(attr, locale, source_locale) translations_for(attr)[locale.to_s] ||= translations_for(attr)[source_locale.to_s] end
def translated_arel_attribute(attr, locale)
def translated_arel_attribute(attr, locale) return Arel.sql("#{attr}_translations->>'#{locale}'") unless mysql? # MySQL and MariaDB JSON support 🤬🤬🤬 # Note: doesn't work with Rails 7.0.x json_extract = Arel::Nodes::NamedFunction.new( 'json_extract', [Arel::Nodes::SqlLiteral.new("#{attr}_translations"), Arel::Nodes.build_quoted("$.#{locale}")] ) Arel::Nodes::NamedFunction.new('json_unquote', [json_extract]) end
def translates(*attributes, presence: false)
def translates(*attributes, presence: false) attributes.each do |attr| # MariaDB doesn't support native JSON columns (longtext instead), we need to force it. attribute("#{attr}_translations", :json) if respond_to?(:attribute) setup_accessors(attr) end add_presence_validator(attributes) if presence end
def translations_for(attr)
def translations_for(attr) # With MySQL, there is no default value for JSON columns, so we need to check for nil public_send("#{attr}_translations").presence || {} end