diff --git a/app/models/concerns/globalizable.rb b/app/models/concerns/globalizable.rb index 3523f1c29..96f6340c9 100644 --- a/app/models/concerns/globalizable.rb +++ b/app/models/concerns/globalizable.rb @@ -1,4 +1,5 @@ module Globalizable + MIN_TRANSLATIONS = 1 extend ActiveSupport::Concern included do @@ -8,11 +9,18 @@ module Globalizable def locales_not_marked_for_destruction translations.reject(&:_destroy).map(&:locale) end + validate :check_translations_number, on: :update, if: :translations_required? + after_validation :copy_error_to_current_translation, on: :update def description self.read_attribute(:description).try :html_safe end + + def translations_required? + translated_attribute_names.any?{|attr| required_attribute?(attr)} + end + if self.paranoid? && translation_class.attribute_names.include?("hidden_at") translation_class.send :acts_as_paranoid, column: :hidden_at end @@ -21,6 +29,38 @@ module Globalizable private + def required_attribute?(attribute) + presence_validators = [ActiveModel::Validations::PresenceValidator, + ActiveRecord::Validations::PresenceValidator] + + attribute_validators(attribute).any?{|validator| presence_validators.include? validator } + end + + def attribute_validators(attribute) + self.class.validators_on(attribute).map(&:class) + end + + def check_translations_number + errors.add(:base, :translations_too_short) unless traslations_count_valid? + end + + def traslations_count_valid? + translations.reject(&:marked_for_destruction?).count >= MIN_TRANSLATIONS + end + + def copy_error_to_current_translation + return unless errors.added?(:base, :translations_too_short) + + if locales_persisted_and_marked_for_destruction.include?(I18n.locale) + locale = I18n.locale + else + locale = locales_persisted_and_marked_for_destruction.first + end + + translation = translation_for(locale) + translation.errors.add(:base, :translations_too_short) + end + def searchable_globalized_values values = {} translations.each do |translation| diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index 6e5f78f68..576436da1 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -448,6 +448,7 @@ en: valuation: cannot_comment_valuation: "You cannot comment a valuation" messages: + translations_too_short: Is mandatory to provide one translation at least record_invalid: "Validation failed: %{errors}" another_poll_active: There is another poll active for the given period restrict_dependent_destroy: diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index e8d8a4efb..7935a2b46 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -450,6 +450,7 @@ es: valuation: cannot_comment_valuation: "No puedes comentar una evaluación" messages: + translations_too_short: El obligatorio proporcionar una traducción como mínimo record_invalid: "Error de validación: %{errors}" another_poll_active: Hay otra encuesta activa para este periodo. restrict_dependent_destroy: diff --git a/spec/shared/features/edit_translatable.rb b/spec/shared/features/edit_translatable.rb index 9a49f1407..c189561be 100644 --- a/spec/shared/features/edit_translatable.rb +++ b/spec/shared/features/edit_translatable.rb @@ -236,6 +236,19 @@ shared_examples "edit_translatable" do |factory_name, path_name, input_fields, t expect_not_to_have_language "Español" end + scenario "Remove all translations should show an error message", :js do + skip("can't have invalid translations") if required_fields.empty? + + visit path + + click_link "Remove language" + click_link "Remove language" + + click_button update_button_text + + expect(page).to have_content "Is mandatory to provide one translation at least" + end + scenario "Remove a translation with invalid data", :js do skip("can't have invalid translations") if required_fields.empty?