require 'puppet/util/platform'
require 'puppet/file_system'
module Puppet::GettextConfig
LOCAL_PATH = File.absolute_path('../../../locales', File.dirname(__FILE__))
POSIX_PATH = File.absolute_path('../../../../../share/locale', File.dirname(__FILE__))
WINDOWS_PATH = File.absolute_path('../../../../../../puppet/share/locale', File.dirname(__FILE__))
# This is the only domain name that won't be a symbol, making it unique from environments.
DEFAULT_TEXT_DOMAIN = 'default-text-domain'
# Load gettext helpers and track whether they're available.
# Used instead of features because we initialize gettext before features is available.
begin
require 'fast_gettext'
require 'locale'
# Make translation methods (e.g. `_()` and `n_()`) available everywhere.
class ::Object
include FastGettext::Translation
end
@gettext_loaded = true
rescue LoadError
# Stub out gettext's `_` and `n_()` methods, which attempt to load translations,
# with versions that do nothing
require 'puppet/gettext/stubs'
@gettext_loaded = false
end
# @api private
# Whether we were able to require fast_gettext and locale
# @return [Boolean] true if translation gems were successfully loaded
def self.gettext_loaded?
@gettext_loaded
end
# @api private
# Returns the currently selected locale from FastGettext,
# or 'en' of gettext has not been loaded
# @return [String] the active locale
def self.current_locale
if gettext_loaded?
return FastGettext.default_locale
else
return 'en'
end
end
# @api private
# Returns a list of the names of the loaded text domains
# @return [[String]] the names of the loaded text domains
def self.loaded_text_domains
return [] if @gettext_disabled || !gettext_loaded?
return FastGettext.translation_repositories.keys
end
# @api private
# Clears the translation repository for the given text domain,
# creating it if it doesn't exist, then adds default translations
# and switches to using this domain.
# @param [String, Symbol] domain_name the name of the domain to create
def self.reset_text_domain(domain_name)
return if @gettext_disabled || !gettext_loaded?
domain_name = domain_name.to_sym
Puppet.debug { "Reset text domain to #{domain_name.inspect}" }
FastGettext.add_text_domain(domain_name,
type: :chain,
chain: [],
report_warning: false)
copy_default_translations(domain_name)
FastGettext.text_domain = domain_name
end
# @api private
# Resets the thread's configured text_domain to the default text domain.
# In Puppet Server, thread A may process a compile request that configures
# a domain, while thread B may invalidate that environment and delete the
# domain. That leaves thread A with an invalid text_domain selected.
# To avoid that, clear_text_domain after any processing that needs the
# non-default text domain.
def self.clear_text_domain
return if @gettext_disabled || !gettext_loaded?
FastGettext.text_domain = nil
end
# @api private
# Creates a default text domain containing the translations for
# Puppet as the start of chain. When semantic_puppet gets initialized,
# its translations are added to this chain. This is used as a cache
# so that all non-module translations only need to be loaded once as
# we create and reset environment-specific text domains.
#
# @return true if Puppet translations were successfully loaded, false
# otherwise
def self.create_default_text_domain
return if @gettext_disabled || !gettext_loaded?
FastGettext.add_text_domain(DEFAULT_TEXT_DOMAIN,
type: :chain,
chain: [],
report_warning: false)
FastGettext.default_text_domain = DEFAULT_TEXT_DOMAIN
load_translations('puppet', puppet_locale_path, translation_mode(puppet_locale_path), DEFAULT_TEXT_DOMAIN)
end
# @api private
# Switches the active text domain, if the requested domain exists.
# @param [String, Symbol] domain_name the name of the domain to switch to
def self.use_text_domain(domain_name)
return if @gettext_disabled || !gettext_loaded?
domain_name = domain_name.to_sym
if FastGettext.translation_repositories.include?(domain_name)
Puppet.debug { "Use text domain #{domain_name.inspect}" }
FastGettext.text_domain = domain_name
else
Puppet.debug { "Requested unknown text domain #{domain_name.inspect}" }
end
end
# @api private
# Delete all text domains.
def self.delete_all_text_domains
FastGettext.translation_repositories.clear
FastGettext.default_text_domain = nil
FastGettext.text_domain = nil
end
# @api private
# Deletes the text domain with the given name
# @param [String, Symbol] domain_name the name of the domain to delete
def self.delete_text_domain(domain_name)
return if @gettext_disabled || !gettext_loaded?
domain_name = domain_name.to_sym
deleted = FastGettext.translation_repositories.delete(domain_name)
if FastGettext.text_domain == domain_name
Puppet.debug { "Deleted current text domain #{domain_name.inspect}: #{!deleted.nil?}" }
FastGettext.text_domain = nil
else
Puppet.debug { "Deleted text domain #{domain_name.inspect}: #{!deleted.nil?}" }
end
end
# @api private
# Deletes all text domains except the default one
def self.delete_environment_text_domains
return if @gettext_disabled || !gettext_loaded?
FastGettext.translation_repositories.keys.each do |key|
# do not clear default translations
next if key == DEFAULT_TEXT_DOMAIN
FastGettext.translation_repositories.delete(key)
end
FastGettext.text_domain = nil
end
# @api private
# Adds translations from the default text domain to the specified
# text domain. Creates the default text domain if one does not exist
# (this will load Puppet's translations).
#
# Since we are currently (Nov 2017) vendoring semantic_puppet, in normal
# flows these translations will be copied along with Puppet's.
#
# @param [Symbol] domain_name the name of the domain to add translations to
def self.copy_default_translations(domain_name)
return if @gettext_disabled || !gettext_loaded?
if FastGettext.default_text_domain.nil?
create_default_text_domain
end
puppet_translations = FastGettext.translation_repositories[FastGettext.default_text_domain].chain
FastGettext.translation_repositories[domain_name].chain.push(*puppet_translations)
end
# @api private
# Search for puppet gettext config files
# @return [String] path to the config, or nil if not found
def self.puppet_locale_path
if Puppet::FileSystem.exist?(LOCAL_PATH)
return LOCAL_PATH
elsif Puppet::Util::Platform.windows? && Puppet::FileSystem.exist?(WINDOWS_PATH)
return WINDOWS_PATH
elsif !Puppet::Util::Platform.windows? && Puppet::FileSystem.exist?(POSIX_PATH)
return POSIX_PATH
else
nil
end
end
# @api private
# Determine which translation file format to use
# @param [String] conf_path the path to the gettext config file
# @return [Symbol] :mo if in a package structure, :po otherwise
def self.translation_mode(conf_path)
if WINDOWS_PATH == conf_path || POSIX_PATH == conf_path
return :mo
else
return :po
end
end
# @api private
# Prevent future gettext initializations
def self.disable_gettext
@gettext_disabled = true
end
# @api private
# Attempt to load translations for the given project.
# @param [String] project_name the project whose translations we want to load
# @param [String] locale_dir the path to the directory containing translations
# @param [Symbol] file_format translation file format to use, either :po or :mo
# @return true if initialization succeeded, false otherwise
def self.load_translations(project_name, locale_dir, file_format, text_domain = FastGettext.text_domain)
if project_name.nil? || project_name.empty?
raise Puppet::Error, "A project name must be specified in order to initialize translations."
end
return false if @gettext_disabled || !@gettext_loaded
return false unless locale_dir && Puppet::FileSystem.exist?(locale_dir)
unless file_format == :po || file_format == :mo
raise Puppet::Error, "Unsupported translation file format #{file_format}; please use :po or :mo"
end
add_repository_to_domain(project_name, locale_dir, file_format, text_domain)
return true
end
# @api private
# Add the translations for this project to the domain's repository chain
# chain for the currently selected text domain, if needed.
# @param [String] project_name the name of the project for which to load translations
# @param [String] locale_dir the path to the directory containing translations
# @param [Symbol] file_format the format of the translations files, :po or :mo
def self.add_repository_to_domain(project_name, locale_dir, file_format, text_domain = FastGettext.text_domain)
return if @gettext_disabled || !gettext_loaded?
current_chain = FastGettext.translation_repositories[text_domain].chain
repository = FastGettext::TranslationRepository.build(project_name,
path: locale_dir,
type: file_format,
report_warning: false)
current_chain << repository
end
# @api private
# Sets FastGettext's locale to the current system locale
def self.setup_locale
return if @gettext_disabled || !gettext_loaded?
set_locale(Locale.current.language)
end
# @api private
# Sets the language in which to display strings.
# @param [String] locale the language portion of a locale string (e.g. "ja")
def self.set_locale(locale)
return if @gettext_disabled || !gettext_loaded?
# make sure we're not using the `available_locales` machinery
FastGettext.default_available_locales = nil
FastGettext.default_locale = locale
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez