1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
|
require_relative '../../puppet/util/platform'
require_relative '../../puppet/file_system'
module Puppet::GettextConfig
LOCAL_PATH = File.absolute_path('../../../locales', File.dirname(__FILE__))
POSIX_PATH = '/usr/share/puppet/locale'
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_relative '../../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
|