From a few hours of investigation, the puppetserver appears to track translation repositories from compile to compile, but either "forgets" about the locale after each compile, or must have the locale initialized in EVERY thread individually.
Some technical details:
- Puppet::GettextConfig.initialize_i18n checks translation_repositories before initializing FastGettext.locale, and thus only initializes the locale once ever (see https://github.com/puppetlabs/puppet/blob/5.3.2/lib/puppet/module.rb#L427-L446).
- FastGettext.translation_repositories are a class variable. According to the FastGettext readme, FastGettext.locale must be initialized once per thread: https://github.com/grosser/fast_gettext#3-choose-text-domain-and-locale-for-translation
- The locale should be negotiated at every module load, contrary to what this says: https://github.com/puppetlabs/puppet/blob/5.3.2/lib/puppet/gettext/config.rb#L83-L84 (watch out for performance implications) but the translation repositories should only needed to be loaded once.
We are going to explore refactoring the gettext initialization code to skip the logic around available_locales and FastGettext.locale in favor of just setting FastGettext.default_locale to the system locale directly, since default_locale is not thread local, unlike FastGettext.locale. That library should already handle the fallback cases from this configuration (though we will be writing tests to verify this, see
PUP-8080). In order to avoid breaking other users of GettextSetup, we will likely be implementing most of this directly in Puppet as gettext utils, and drop our runtime dependency on GettextSetup.
It looks like we might run into a similar issue when we start trying to use more text domains (see
PUP-8013 and https://github.com/grosser/fast_gettext/blob/master/lib/fast_gettext/storage.rb#L54) as the current text domain also appears to be a thread-local variable. Currently we're avoiding issues here by only using one text domain which is both set as FastGettext.text_domain and FastGettext.default_text_domain, the latter of which is not thread local.
Reproduction steps in a basic PE 2017.3.1 install
- localectl set-locale LANG=ja_JP.UTF-8
- add fail('trigger failure') to node default section of /etc/puppetlabs/code/environments/production/manifests/site.pp
- set disable_i18n=false in /etc/puppetlabs/puppet/puppet.conf
- systemctl restart pe-puppetserver
- puppet agent -t --noop (several times)
Expect to see
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: サーバエラー: Evaluation Error: a Function Callの検証中にエラーが生じました。trigger failure at /etc/puppetlabs/code/environments/production/manifests/site.pp:31:3 on node hwcijw3gfrqxsiw.delivery.puppetlabs.net
Will mostly see
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, trigger failure at /etc/puppetlabs/code/environments/production/manifests/site.pp:31:3 on node hwcijw3gfrqxsiw.delivery.puppetlabs.net