The main admin view in Rosetta features a list of all the available keys and their translation counterpart in the selected locale -- should it exist:
In data-terms, it means I need to LEFT JOIN translations on both the translation key and the locale. In SQL it looks like:
SELECT *
FROM translation_keys tk
LEFT JOIN translations t on t.translation_key_id = tk.id AND t.locale_id = %{LOCALE_ID}
Where %{LOCALE_ID} is set at request time to the selected locale in the application. To my knowledge, Rails does not allow to eager load records with a custom JOIN. A first implementation of this view added two custom scopes on TranslationKey that retrieved the required data for the view:
Every instance of TranslationKey loaded through one of those scopes would feature two additional attributes: translation_id and translation_value, used in the view to implement the required edit feature.
Problem
Straying away from the common path comes with its loads of problems and frustrations. With that setup, the partials consuming the loaded records expect translation_id and translation_value to be present on the TranslationKey model. When rendering the partial for a single record (not the collection, in a turbo stream for instance) it brings design challenges.
Moreovever, having TranslationKey bearing values for the translation model seems wrong. I'd rather handle a separate entity that clearly states the concern.
TranslationKey.includes(:translation_in_current_locale) will correctly load the associated records. However it comes with a cost, as it uses the global Rosetta.locale variable. This is necessary since the available locales are saved in the db and only known at runtime. I can't set those up beforehand.
It feels really sharp of a knife, however this is a tradeoff I am willing to accept for now considering the following points:
Users should never have to deal with TranslationKey#translation_in_current_locale on their own
We make sure each and every view in the admin that needs scoping to a locale does so through theLocaleScoped module that owns all the logic
The Rosetta::Store only access it through the load_translations method that properly scopes the locale to the store locale + knowing the fact that a store is always access in its own locale by design. (using Store#for_locale)
Background
The main admin view in Rosetta features a list of all the available keys and their translation counterpart in the selected locale -- should it exist:
In data-terms, it means I need to
LEFT JOIN
translations on both the translation key and the locale. In SQL it looks like:Where
%{LOCALE_ID}
is set at request time to the selected locale in the application. To my knowledge, Rails does not allow to eager load records with a custom JOIN. A first implementation of this view added two custom scopes onTranslationKey
that retrieved the required data for the view:https://github.com/virolea/rosetta/blob/3c4508cecc3af142fcc2efe6ffa69e931f15a5ce/app/models/rosetta/translation_key.rb#L7-L15
Every instance of
TranslationKey
loaded through one of those scopes would feature two additional attributes:translation_id
andtranslation_value
, used in the view to implement the required edit feature.Problem
Straying away from the common path comes with its loads of problems and frustrations. With that setup, the partials consuming the loaded records expect
translation_id
andtranslation_value
to be present on the TranslationKey model. When rendering the partial for a single record (not the collection, in a turbo stream for instance) it brings design challenges.Moreovever, having
TranslationKey
bearing values for the translation model seems wrong. I'd rather handle a separate entity that clearly states the concern.Solution
In https://github.com/virolea/rosetta/commit/e29d60a1c58c1837a42b1bb4eaa3023a16406db0, the custom scopes have been dropped in favour of custom
has_one
relationship that properly eager loads the translation model for a given (key, locale) tuple.https://github.com/virolea/rosetta/blob/e29d60a1c58c1837a42b1bb4eaa3023a16406db0/app/models/rosetta/translation_key.rb#L1-L7
TranslationKey.includes(:translation_in_current_locale)
will correctly load the associated records. However it comes with a cost, as it uses the global Rosetta.locale variable. This is necessary since the available locales are saved in the db and only known at runtime. I can't set those up beforehand.It feels really sharp of a knife, however this is a tradeoff I am willing to accept for now considering the following points:
TranslationKey#translation_in_current_locale
on their ownLocaleScoped
module that owns all the logicRosetta::Store
only access it through theload_translations
method that properly scopes the locale to the store locale + knowing the fact that a store is always access in its own locale by design. (usingStore#for_locale
)