I ended up using Active Admin plus activeadmin-globalize3 instead. Much easier.
Rails force all translations
-
06-03-2022 - |
Question
I am using globalize3
with rails_admin
thanks to this gist. What bugs me, is that the user can add as many translations as he wants.
Moreover, he isn't forced to translate the content in every single locale (as in I18n.available_locales
). I'd like that. How can you tackle such a situation?
Models (shortened):
class Project < ActiveRecord::Base
has_many :project_translations, :dependent => :destroy, :inverse_of => :project
accepts_nested_attributes_for :project_translations, :allow_destroy => true
class ProjectTranslation < ActiveRecord::Base
belongs_to :project
La solution
Autres conseils
It bugged me too, so I created custom field type that doesn't allow it.
The main class:
module RailsAdmin
module Config
module Fields
module Types
class GlobalizeTabs < RailsAdmin::Config::Fields::Association
RailsAdmin::Config::Fields::Types::register(:globalize_tabs, self)
register_instance_option :partial do
:form_globalize_tabs
end
def method_name
"#{super}_attributes".to_sym
end
# Reader for validation errors of the bound object
def errors
bindings[:object].errors[name]
end
def available_locales
I18n.available_locales
end
def current_locale
I18n.locale
end
# Returns array of Translation objects
# It gets existing or creates new empty translation for every locale
# It's used in fields_for method in partial
def translations
translated_locales = @bindings[:object].translated_locales
available_locales.collect do |locale|
translated_locales.include?(locale) ? @bindings[:object].translation_for(locale) : @bindings[:object].translations.new({ locale: locale })
end
end
end
end
end
end
end
It inherits from RailsAdmin::Config::Fields::Association
class, because it uses very similar to _form_nested_many
partial (that's used in has_many type).
The partial:
.controls
= form.errors_for(field)
%ul.nav.nav-tabs{ :style => 'margin-top:5px' }
- field.available_locales.each do |locale|
%li{ class: ( 'active' if locale == field.current_locale ) }
%a{ href: "##{locale}", data: { toggle: "tab" } }= locale
.tab-content
= form.fields_for field.name, field.translations, wrapper: false do |nested_form|
.fields.tab-pane{ id: nested_form.object.locale, class: ( 'active' if nested_form.object.locale == field.current_locale ) }
= nested_form.generate({:action => :nested, :model_config => field.associated_model_config, :nested_in => field.name })
= form.help_for(field)
It uses field.translations
method from the custom field class, that returns an array of Translation objects.
Every Translation object corresponds to available locale, and it's either an existing object from the database (if translation already exists) or new empty translation object.
E.g.
You've got this available locales:
I18n.available_locales = [:en, :cz, :ru]
You have Page model which includes some translated fields.
Also, you have an object of the class Page (a row in the database), that has translations for :en and :cz locales, but lacks one for the :ru.
So, field.translations
method inside _form_globalize_tabs
partial returns an array that contains:
2 existing translations for :en and :cz and 1 just initialized translation for :ru.
In the partial I'm passing this array to the fields_for
helper method from nested_form
gem, that returns 3 fieldsets for every translation object.
You can use this gem, if you don't want to mess with the code yourself: https://github.com/scarfaceDeb/rails_admin_globalize_field