Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #36575 - Avoid translating labels during startup #9771

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

ekohl
Copy link
Member

@ekohl ekohl commented Jul 7, 2023

It's invalid to do translations during startup, such as in models or in controllers. You should always use N_() (see https://projects.theforeman.org/projects/foreman/wiki/Translating#Extracting-strings). This is because when it's running you actually don't have translations at all, so it's effectively a noop anyway.

When updating fast_gettext to 2.1+ (#9770) it turns into infinite recursion so it must be addressed if we want to move forward.

@theforeman-bot
Copy link
Member

Issues: #36575

@ekohl ekohl force-pushed the 36575-avoid-translations-during-startup branch 2 times, most recently from 814d2b1 to e902a84 Compare July 7, 2023 16:27
@ekohl ekohl marked this pull request as draft July 7, 2023 16:27
@ekohl
Copy link
Member Author

ekohl commented Jul 7, 2023

@ofedoren I've added some TODOs with procs, but I doubt it's valid. Mind having a look?

@@ -51,7 +51,7 @@ class Domain < ApplicationRecord

set_crud_hooks :domain

apipie :class, desc: "A class representing #{model_name.human} object" do
apipie :class, desc: "A class representing #{humanize_class_name} object" do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized this actually doesn't exist and we need something else to describe the object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, since the class names mostly taken as is for DSL, unless they explicitly renamed, it's okay to leave this untranslated and closer to something that is used within methods/return values. Although, there is a need to review all DSL documentation and make sure it's actually consistent.

Also, this whole string is not being translated...

@@ -33,7 +33,8 @@ module Api::V2::TaxonomiesController
param :ignore_types, Array, N_("List of resources types that will be automatically associated"), :required => false
resource_name = (param_name == :location) ? 'organization' : 'location'
resource_ids = "#{resource_name}_ids".to_sym
param resource_ids, Array, N_("Associated %{resource} IDs") % { resource: _(resource_name) }, :required => false
# TODO: translate resource_name?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm dumb for writing _(resource_name) instead of simply translating taxonomy names. Although, due to some language specialties, this would seems odd in the end anyway.

In this case, I'd better be more explicit or WET:

# ...
mapping = { location: ['organization', N_("Associated organization IDs")], organization: ['location', N_("Associated location IDs")] }
resource_ids = "#{mapping[param_name]&.at(0)}_ids".to_sym # & is just to be sure it doesn't crash, but can be omitted I guess
# ...
param resource_ids, Array, mapping[param_name]&.at(1), :required => false # & is for the same reason, can be changed to [1]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll expand it then.

@@ -51,7 +51,7 @@ class Domain < ApplicationRecord

set_crud_hooks :domain

apipie :class, desc: "A class representing #{model_name.human} object" do
apipie :class, desc: "A class representing #{humanize_class_name} object" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, since the class names mostly taken as is for DSL, unless they explicitly renamed, it's okay to leave this untranslated and closer to something that is used within methods/return values. Although, there is a need to review all DSL documentation and make sure it's actually consistent.

Also, this whole string is not being translated...

@ekohl ekohl force-pushed the 36575-avoid-translations-during-startup branch from 1eb6c35 to 7c974b4 Compare July 11, 2023 11:58
@ekohl
Copy link
Member Author

ekohl commented Aug 1, 2023

@ofedoren any idea if apipie descriptions can be callable? That way we can have translatable descriptions without having to rewrite so much.

@ofedoren
Copy link
Member

@ekohl, so sorry for the late reply.

It's not that they can be callable, but AFAIU we can defer the actual translation: https://github.com/theforeman/foreman/blob/develop/config/initializers/apipie.rb#L68. translate method should be called not at the initialization time, but rather "on demand", when the docs are being rendered in one way or another. Same should work for ApipieDSL, but didn't test though...

@ekohl
Copy link
Member Author

ekohl commented Oct 6, 2023

I split off some of the changes in #9854 for easier review.

@ekohl ekohl force-pushed the 36575-avoid-translations-during-startup branch from 7c974b4 to 9e7596c Compare October 9, 2023 09:42
@ekohl
Copy link
Member Author

ekohl commented Oct 9, 2023

Rebased on develop now #9854 is merged. This reduces the scope to apipie docs.

@@ -51,7 +51,7 @@ class Domain < ApplicationRecord

set_crud_hooks :domain

apipie :class, desc: "A class representing #{model_name.human} object" do
apipie :class, desc: "A class representing #{model_name.singular.humanize(capitalize: false)} object" do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ofedoren I wonder if it makes sense to enhance apipie to provide a desc_from_class or some other way to automatically derive this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like all the descriptions are quite the same, so yeah, it makes sense to add something like default_desc. Since I've noticed that apipie-dsl needs some love, I'm going to add this as well.

Since, ideally we want to support translation for all the strings (and apipie-dsl doesn't have dependency on gettext), we shouldn't hardcode such string in the lib, but somehow provide it to be evaluated in the scope of a certain class. My suggestion is to add a new entry called e.g. default_model_description to the config (https://github.com/theforeman/foreman/blob/develop/config/initializers/apipie.rb#L3), which will accept a proc, which will be evaluated in the class (to be able to call model_name) and save the string in the object. This could be translated later via callback we provide in the same config. It would look like:

ApipieDSL.configure do |config|
# ...
  config.default_model_description = ->(model) { N_("A class representing %s object") % model.model_name.singular.humanize(capitalize: false) }
# ...
end

That will be applied to all documented models unless there is an explicit override, such as :desc option or short method is called within a block. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do provide a translate method as ApipieDSL.translate(str, locale):

https://github.com/Apipie/apipie-dsl/blob/f7118ed268449199272ce1245b3e30aece84c7f8/lib/apipie_dsl/application.rb#L214-L220

AFAIK it doesn't support interpolating the translated strings with variables, which makes it rather hard to use. I think that would still be needed.

In the context of Foreman: do we configure the translate method:

config.translate = lambda do |str, loc|
old_loc = FastGettext.locale
FastGettext.set_locale(loc)
trans = _(str) if str
FastGettext.set_locale(old_loc)
trans
end

And I think we should rewrite that to use FastGettext.with_locale:

config.translate = lambda do |str, loc|
  str ? FastGettext.with_locale(loc) { _(str) } : nil
end

So I think your suggestion makes sense, but I'm not sure how to hook it up properly.

sections only: %w[all additional]
name_exl, title_exl = class_scope.model_name.human == 'Location' ? ['Europe', 'Europe/Prague'] : ['Red Hat', 'Red Hat/Engineering']
name_exl, title_exl = model_name.to_s == 'Location' ? ['Europe', 'Europe/Prague'] : ['Red Hat', 'Red Hat/Engineering']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ekohl I believe we can consider using class_scope here to determine the context of the model and this will help us to resolve the apipie issue.

name_exl, title_exl = class_scope.model_name.to_s == 'Location' ? ['Europe', 'Europe/Prague'] : ['Red Hat', 'Red Hat/Engineering']

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good catch.

@archanaserver
Copy link
Contributor

archanaserver commented Oct 12, 2023

@ekohl should I raise a new PR to resolve the rubocop issue that I can see here, on top of this PR?

and what does single commit workflow mean here?

@ekohl
Copy link
Member Author

ekohl commented Oct 12, 2023

@ekohl should I raise a new PR to resolve the rubocop issue that I can see here, on top of this PR?

I'll address it.

and what does single commit workflow mean here?

Generally speaking a PR should have a single commit. Sometimes contributors add small commits to fix review comments, but that makes it hard to cherry pick into stable releases. That check signals reviewers they should pay attention to it. In this case I chose it and I think it makes sense because I'm fixing individual commits. This is then a discussion a reviewer can have with the author. The check is not blocking, but GH isn't great a providing alternatives here.

@ofedoren
Copy link
Member

@ekohl, I've released apipie-dsl-2.6.0. Along with some dependencies upgrades it supports now default_class_description method for configuration, so you can try out my suggestion above. In that case, don't forget to simply remove all desc: options instead of updating them.

I'd also highly recommend to add to the config block (properly supported since 2.6.0 :/):

# ...
config.reload_dsl = false
# ...

Otherwise it's a pain to load the pages in development.

@ekohl
Copy link
Member Author

ekohl commented Oct 13, 2023

@archanaserver is updating to apipie-dsl 2.6.0 something you could look at? It would probably supersede this PR

@archanaserver
Copy link
Contributor

@archanaserver is updating to apipie-dsl 2.6.0 something you could look at? It would probably supersede this PR

Sure.

@ekohl
Copy link
Member Author

ekohl commented Oct 23, 2023

Needs to be rebased after #9867 is merged.

@ekohl ekohl force-pushed the 36575-avoid-translations-during-startup branch from 46931e4 to dd01d0e Compare November 16, 2023 17:44
@ekohl
Copy link
Member Author

ekohl commented Feb 29, 2024

@ofedoren can you take a look at this? I still think these translations are executed on application startup, which can't work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants