diff options
| author | Ben Sanders <ben@sanders.life> | 2026-08-02 09:49:25 -0400 |
|---|---|---|
| committer | Ben Sanders <ben@sanders.life> | 2026-08-02 09:49:25 -0400 |
| commit | b41479c91e6685511c1f8ba8586106b322073b62 (patch) | |
| tree | c9f1345999d754ae0a533849966427e792d9e68d /vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib | |
| parent | cfaceeb9a6d1295f5754be211858155cd309acc2 (diff) | |
added statscounter code
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib')
46 files changed, 4699 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n.rb new file mode 100644 index 0000000..429ce46 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n.rb @@ -0,0 +1,507 @@ +# frozen_string_literal: true + +require 'concurrent/map' +require 'concurrent/hash' + +require 'i18n/version' +require 'i18n/utils' +require 'i18n/exceptions' +require 'i18n/interpolate/ruby' + +module I18n + autoload :Backend, 'i18n/backend' + autoload :Config, 'i18n/config' + autoload :Gettext, 'i18n/gettext' + autoload :Locale, 'i18n/locale' + autoload :Tests, 'i18n/tests' + autoload :Middleware, 'i18n/middleware' + + RESERVED_KEYS = %i[ + cascade + deep_interpolation + skip_interpolation + default + exception_handler + fallback + fallback_in_progress + fallback_original_locale + format + object + raise + resolve + scope + separator + throw + ] + EMPTY_HASH = {}.freeze + + def self.new_double_nested_cache # :nodoc: + Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new } + end + + # Marks a key as reserved. Reserved keys are used internally, + # and can't also be used for interpolation. If you are using any + # extra keys as I18n options, you should call I18n.reserve_key + # before any I18n.translate (etc) calls are made. + def self.reserve_key(key) + RESERVED_KEYS << key.to_sym + @reserved_keys_pattern = nil + end + + def self.reserved_keys_pattern # :nodoc: + @reserved_keys_pattern ||= /(?<!%)%\{(#{RESERVED_KEYS.join("|")})\}/ + end + + module Base + # Gets I18n configuration object. + def config + if Fiber.respond_to?(:[]) + current = Fiber[:i18n_config] || self.config = I18n::Config.new + if current.respond_to?(:owned_by?) && !current.owned_by?(Fiber.current) + current = current.dup + Fiber[:i18n_config] = current + end + + current + else + Thread.current.thread_variable_get(:i18n_config) || + Thread.current.thread_variable_set(:i18n_config, I18n::Config.new) + end + end + + # Gets a mutable I18n configuration object. + def writable_config + current = config + return current unless current.frozen? + + current = current.dup + if Fiber.respond_to?(:[]) + Fiber[:i18n_config] = current + else + Thread.current.thread_variable_set(:i18n_config, current) + end + current + end + + # Sets I18n configuration object. + def config=(value) + if Fiber.respond_to?(:[]) + Fiber[:i18n_config] = value + value.owner = Fiber.current if value.respond_to?(:owner=) && !value.frozen? + else + Thread.current.thread_variable_set(:i18n_config, value) + end + end + + # Write methods which delegates to the configuration object + %w(locale backend default_locale available_locales default_separator + exception_handler load_path enforce_available_locales).each do |method| + module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1 + def #{method} + config.#{method} + end + + def #{method}=(value) + writable_config.#{method} = (value) + end + DELEGATORS + end + + # Tells the backend to reload translations. Used in situations like the + # Rails development environment. Backends can implement whatever strategy + # is useful. + def reload! + config.clear_available_locales_set + config.backend.reload! + end + + # Tells the backend to load translations now. Used in situations like the + # Rails production environment. Backends can implement whatever strategy + # is useful. + def eager_load! + config.backend.eager_load! + end + + # Translates, pluralizes and interpolates a given key using a given locale, + # scope, and default, as well as interpolation values. + # + # *LOOKUP* + # + # Translation data is organized as a nested hash using the upper-level keys + # as namespaces. <em>E.g.</em>, ActionView ships with the translation: + # <tt>:date => {:formats => {:short => "%b %d"}}</tt>. + # + # Translations can be looked up at any level of this hash using the key argument + # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt> + # returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>. + # + # Key can be either a single key or a dot-separated key (both Strings and Symbols + # work). <em>E.g.</em>, the short format can be looked up using both: + # I18n.t 'date.formats.short' + # I18n.t :'date.formats.short' + # + # Scope can be either a single key, a dot-separated key or an array of keys + # or dot-separated keys. Keys and scopes can be combined freely. So these + # examples will all look up the same short date format: + # I18n.t 'date.formats.short' + # I18n.t 'formats.short', :scope => 'date' + # I18n.t 'short', :scope => 'date.formats' + # I18n.t 'short', :scope => %w(date formats) + # + # *INTERPOLATION* + # + # Translations can contain interpolation variables which will be replaced by + # values passed to #translate as part of the options hash, with the keys matching + # the interpolation variable names. + # + # <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option + # value for the key +bar+ will be interpolated into the translation: + # I18n.t :foo, :bar => 'baz' # => 'foo baz' + # + # *PLURALIZATION* + # + # Translation data can contain pluralized translations. Pluralized translations + # are arrays of singular/plural versions of translations like <tt>['Foo', 'Foos']</tt>. + # + # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English + # pluralization rules. Other algorithms can be supported by custom backends. + # + # This returns the singular version of a pluralized translation: + # I18n.t :foo, :count => 1 # => 'Foo' + # + # These both return the plural version of a pluralized translation: + # I18n.t :foo, :count => 0 # => 'Foos' + # I18n.t :foo, :count => 2 # => 'Foos' + # + # The <tt>:count</tt> option can be used both for pluralization and interpolation. + # <em>E.g.</em>, with the translation + # <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will + # be interpolated to the pluralized translation: + # I18n.t :foo, :count => 1 # => '1 foo' + # + # *DEFAULTS* + # + # This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found: + # I18n.t :foo, :default => 'default' + # + # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no + # translation for <tt>:foo</tt> was found: + # I18n.t :foo, :default => :bar + # + # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> + # or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found. + # I18n.t :foo, :default => [:bar, 'default'] + # + # <b>BULK LOOKUP</b> + # + # This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>. + # I18n.t [:foo, :bar] + # + # Can be used with dot-separated nested keys: + # I18n.t [:'baz.foo', :'baz.bar'] + # + # Which is the same as using a scope option: + # I18n.t [:foo, :bar], :scope => :baz + # + # *LAMBDAS* + # + # Both translations and defaults can be given as Ruby lambdas. Lambdas will be + # called and passed the key and options. + # + # E.g. assuming the key <tt>:salutation</tt> resolves to: + # lambda { |key, options| options[:gender] == 'm' ? "Mr. #{options[:name]}" : "Mrs. #{options[:name]}" } + # + # Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith')</tt> will result in "Mrs. Smith". + # + # Note that the string returned by lambda will go through string interpolation too, + # so the following lambda would give the same result: + # lambda { |key, options| options[:gender] == 'm' ? "Mr. %{name}" : "Mrs. %{name}" } + # + # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when + # a cache layer is put in front of I18n.translate it will generate a cache key + # from the argument values passed to #translate. Therefore your lambdas should + # always return the same translations/values per unique combination of argument + # values. + # + # <b>Ruby 2.7+ keyword arguments warning</b> + # + # This method uses keyword arguments. + # There is a breaking change in ruby that produces warning with ruby 2.7 and won't work as expected with ruby 3.0 + # The "hash" parameter must be passed as keyword argument. + # + # Good: + # I18n.t(:salutation, :gender => 'w', :name => 'Smith') + # I18n.t(:salutation, **{ :gender => 'w', :name => 'Smith' }) + # I18n.t(:salutation, **any_hash) + # + # Bad: + # I18n.t(:salutation, { :gender => 'w', :name => 'Smith' }) + # I18n.t(:salutation, any_hash) + # + def translate(key = nil, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise + locale ||= config.locale + raise Disabled.new('t') if locale == false + enforce_available_locales!(locale) + + backend = config.backend + + if key.is_a?(Array) + key.map do |k| + translate_key(k, throw, raise, locale, backend, options) + end + else + translate_key(key, throw, raise, locale, backend, options) + end + end + alias :t :translate + + # Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With + # this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt> + def translate!(key, **options) + translate(key, **options, raise: true) + end + alias :t! :translate! + + # Returns an array of interpolation keys for the given translation key + # + # *Examples* + # + # Suppose we have the following: + # I18n.t 'example.zero' == 'Zero interpolations' + # I18n.t 'example.one' == 'One interpolation %{foo}' + # I18n.t 'example.two' == 'Two interpolations %{foo} %{bar}' + # I18n.t 'example.three' == ['One %{foo}', 'Two %{bar}', 'Three %{baz}'] + # I18n.t 'example.one', locale: :other == 'One interpolation %{baz}' + # + # Then we can expect the following results: + # I18n.interpolation_keys('example.zero') #=> [] + # I18n.interpolation_keys('example.one') #=> ['foo'] + # I18n.interpolation_keys('example.two') #=> ['foo', 'bar'] + # I18n.interpolation_keys('example.three') #=> ['foo', 'bar', 'baz'] + # I18n.interpolation_keys('one', scope: 'example', locale: :other) #=> ['baz'] + # I18n.interpolation_keys('does-not-exist') #=> [] + # I18n.interpolation_keys('example') #=> [] + def interpolation_keys(key, **options) + raise I18n::ArgumentError if !key.is_a?(String) || key.empty? + + return [] unless exists?(key, **options.slice(:locale, :scope)) + + translation = translate(key, **options.slice(:locale, :scope)) + interpolation_keys_from_translation(translation) + .flatten.compact + end + + # Returns true if a translation exists for a given key, otherwise returns false. + def exists?(key, _locale = nil, locale: _locale, **options) + locale ||= config.locale + raise Disabled.new('exists?') if locale == false + raise I18n::ArgumentError if (key.is_a?(String) && key.empty?) || key.nil? + + config.backend.exists?(locale, key, options) + end + + # Transliterates UTF-8 characters to ASCII. By default this method will + # transliterate only Latin strings to an ASCII approximation: + # + # I18n.transliterate("Ærøskøbing") + # # => "AEroskobing" + # + # I18n.transliterate("日本語") + # # => "???" + # + # It's also possible to add support for per-locale transliterations. I18n + # expects transliteration rules to be stored at + # <tt>i18n.transliterate.rule</tt>. + # + # Transliteration rules can either be a Hash or a Proc. Procs must accept a + # single string argument. Hash rules inherit the default transliteration + # rules, while Procs do not. + # + # *Examples* + # + # Setting a Hash in <locale>.yml: + # + # i18n: + # transliterate: + # rule: + # ü: "ue" + # ö: "oe" + # + # Setting a Hash using Ruby: + # + # store_translations(:de, i18n: { + # transliterate: { + # rule: { + # 'ü' => 'ue', + # 'ö' => 'oe' + # } + # } + # }) + # + # Setting a Proc: + # + # translit = lambda {|string| MyTransliterator.transliterate(string) } + # store_translations(:xx, :i18n => {:transliterate => {:rule => translit}) + # + # Transliterating strings: + # + # I18n.locale = :en + # I18n.transliterate("Jürgen") # => "Jurgen" + # I18n.locale = :de + # I18n.transliterate("Jürgen") # => "Juergen" + # I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen" + # I18n.transliterate("Jürgen", :locale => :de) # => "Juergen" + def transliterate(key, throw: false, raise: false, locale: nil, replacement: nil, **options) + locale ||= config.locale + raise Disabled.new('transliterate') if locale == false + enforce_available_locales!(locale) + + config.backend.transliterate(locale, key, replacement) + rescue I18n::ArgumentError => exception + handle_exception((throw && :throw || raise && :raise), exception, locale, key, options) + end + + # Localizes certain objects, such as dates and numbers to local formatting. + def localize(object, locale: nil, format: nil, **options) + locale ||= config.locale + raise Disabled.new('l') if locale == false + enforce_available_locales!(locale) + + format ||= :default + config.backend.localize(locale, object, format, options) + end + alias :l :localize + + # Executes block with given I18n.locale set. + def with_locale(tmp_locale = nil) + if tmp_locale == nil + yield + else + current_locale = self.locale + self.locale = tmp_locale + begin + yield + ensure + self.locale = current_locale + end + end + end + + # Merges the given locale, key and scope into a single array of keys. + # Splits keys that contain dots into multiple keys. Makes sure all + # keys are Symbols. + def normalize_keys(locale, key, scope, separator = nil) + separator ||= I18n.default_separator + + [ + *normalize_key(locale, separator), + *normalize_key(scope, separator), + *normalize_key(key, separator) + ] + end + + # Returns true when the passed locale, which can be either a String or a + # Symbol, is in the list of available locales. Returns false otherwise. + def locale_available?(locale) + I18n.config.available_locales_set.include?(locale) + end + + # Raises an InvalidLocale exception when the passed locale is not available. + def enforce_available_locales!(locale) + if locale != false && config.enforce_available_locales + raise I18n::InvalidLocale.new(locale) if !locale_available?(locale) + end + end + + def available_locales_initialized? + config.available_locales_initialized? + end + + private + + def translate_key(key, throw, raise, locale, backend, options) + result = catch(:exception) do + backend.translate(locale, key, options) + end + + if result.is_a?(MissingTranslation) + handle_exception((throw && :throw || raise && :raise), result, locale, key, options) + else + result + end + end + + # Any exceptions thrown in translate will be sent to the @@exception_handler + # which can be a Symbol, a Proc or any other Object unless they're forced to + # be raised or thrown (MissingTranslation). + # + # If exception_handler is a Symbol then it will simply be sent to I18n as + # a method call. A Proc will simply be called. In any other case the + # method #call will be called on the exception_handler object. + # + # Examples: + # + # I18n.exception_handler = :custom_exception_handler # this is the default + # I18n.custom_exception_handler(exception, locale, key, options) # will be called like this + # + # I18n.exception_handler = lambda { |*args| ... } # a lambda + # I18n.exception_handler.call(exception, locale, key, options) # will be called like this + # + # I18n.exception_handler = I18nExceptionHandler.new # an object + # I18n.exception_handler.call(exception, locale, key, options) # will be called like this + def handle_exception(handling, exception, locale, key, options) + case handling + when :raise + raise exception.respond_to?(:to_exception) ? exception.to_exception : exception + when :throw + throw :exception, exception + else + case handler = options[:exception_handler] || config.exception_handler + when Symbol + send(handler, exception, locale, key, options) + else + handler.call(exception, locale, key, options) + end + end + end + + @@normalized_key_cache = I18n.new_double_nested_cache + + def normalize_key(key, separator) + @@normalized_key_cache[separator][key] ||= + case key + when Array + key.flat_map { |k| normalize_key(k, separator) } + else + keys = key.to_s.split(separator) + keys.delete('') + keys.map! do |k| + case k + when /\A[-+]?([1-9]\d*|0)\z/ # integer + k.to_i + when 'true' + true + when 'false' + false + else + k.to_sym + end + end + keys + end + end + + def interpolation_keys_from_translation(translation) + case translation + when ::String + translation.scan(Regexp.union(I18n.config.interpolation_patterns)) + when ::Array + translation.map { |element| interpolation_keys_from_translation(element) } + else + [] + end + end + end + + extend Base +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend.rb new file mode 100644 index 0000000..863d618 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module I18n + module Backend + autoload :Base, 'i18n/backend/base' + autoload :Cache, 'i18n/backend/cache' + autoload :CacheFile, 'i18n/backend/cache_file' + autoload :Cascade, 'i18n/backend/cascade' + autoload :Chain, 'i18n/backend/chain' + autoload :Fallbacks, 'i18n/backend/fallbacks' + autoload :Flatten, 'i18n/backend/flatten' + autoload :Gettext, 'i18n/backend/gettext' + autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler' + autoload :KeyValue, 'i18n/backend/key_value' + autoload :LazyLoadable, 'i18n/backend/lazy_loadable' + autoload :Memoize, 'i18n/backend/memoize' + autoload :Metadata, 'i18n/backend/metadata' + autoload :Pluralization, 'i18n/backend/pluralization' + autoload :Simple, 'i18n/backend/simple' + autoload :Transliterator, 'i18n/backend/transliterator' + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/base.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/base.rb new file mode 100644 index 0000000..0c59cd7 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/base.rb @@ -0,0 +1,314 @@ +# frozen_string_literal: true + +require 'yaml' +require 'json' + +module I18n + module Backend + module Base + include I18n::Backend::Transliterator + + # Accepts a list of paths to translation files. Loads translations from + # plain Ruby (*.rb), YAML files (*.yml), or JSON files (*.json). See #load_rb, #load_yml, and #load_json + # for details. + def load_translations(*filenames) + filenames = I18n.load_path if filenames.empty? + filenames.flatten.each do |filename| + loaded_translations = load_file(filename) + yield filename, loaded_translations if block_given? + end + end + + # This method receives a locale, a data hash and options for storing translations. + # Should be implemented + def store_translations(locale, data, options = EMPTY_HASH) + raise NotImplementedError + end + + def translate(locale, key, options = EMPTY_HASH) + raise I18n::ArgumentError if (key.is_a?(String) || key.is_a?(Symbol)) && key.empty? + raise InvalidLocale.new(locale) unless locale + return nil if key.nil? && !options.key?(:default) + + entry = lookup(locale, key, options[:scope], options) unless key.nil? + + if entry.nil? && options.key?(:default) + entry = default(locale, key, options[:default], options) + else + entry = resolve_entry(locale, key, entry, options) + end + + count = options[:count] + + if entry.nil? && (subtrees? || !count) + if (options.key?(:default) && !options[:default].nil?) || !options.key?(:default) + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) + end + end + + entry = entry.dup if entry.is_a?(String) + entry = pluralize(locale, entry, count) if count + + if entry.nil? && !subtrees? + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) + end + + deep_interpolation = options[:deep_interpolation] + skip_interpolation = options[:skip_interpolation] + values = Utils.except(options, *RESERVED_KEYS) unless options.empty? + if !skip_interpolation && values && !values.empty? + entry = if deep_interpolation + deep_interpolate(locale, entry, values) + else + interpolate(locale, entry, values) + end + elsif entry.is_a?(String) && entry =~ I18n.reserved_keys_pattern + raise ReservedInterpolationKey.new($1.to_sym, entry) + end + entry + end + + def exists?(locale, key, options = EMPTY_HASH) + lookup(locale, key, options[:scope]) != nil + end + + # Acts the same as +strftime+, but uses a localized version of the + # format string. Takes a key from the date/time formats translations as + # a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>). + def localize(locale, object, format = :default, options = EMPTY_HASH) + if object.nil? && options.include?(:default) + return options[:default] + end + raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime) + + if Symbol === format + key = format + type = object.respond_to?(:sec) ? 'time' : 'date' + options = options.merge(:raise => true, :object => object, :locale => locale) + format = I18n.t(:"#{type}.formats.#{key}", **options) + end + + format = translate_localization_format(locale, object, format, options) + object.strftime(format) + end + + # Returns an array of locales for which translations are available + # ignoring the reserved translation meta data key :i18n. + def available_locales + raise NotImplementedError + end + + def reload! + eager_load! if eager_loaded? + end + + def eager_load! + @eager_loaded = true + end + + protected + + def eager_loaded? + @eager_loaded ||= false + end + + # The method which actually looks up for the translation in the store. + def lookup(locale, key, scope = [], options = EMPTY_HASH) + raise NotImplementedError + end + + def subtrees? + true + end + + # Evaluates defaults. + # If given subject is an Array, it walks the array and returns the + # first translation that can be resolved. Otherwise it tries to resolve + # the translation directly. + def default(locale, object, subject, options = EMPTY_HASH) + if options.size == 1 && options.has_key?(:default) + options = {} + else + options = Utils.except(options, :default) + end + + case subject + when Array + subject.each do |item| + result = resolve(locale, object, item, options) + return result unless result.nil? + end and nil + else + resolve(locale, object, subject, options) + end + end + + # Resolves a translation. + # If the given subject is a Symbol, it will be translated with the + # given options. If it is a Proc then it will be evaluated. All other + # subjects will be returned directly. + def resolve(locale, object, subject, options = EMPTY_HASH) + return subject if options[:resolve] == false + result = catch(:exception) do + case subject + when Symbol + I18n.translate( + subject, + **options.merge( + :locale => locale, + :throw => true, + :skip_interpolation => true + ) + ) + when Proc + date_or_time = options.delete(:object) || object + resolve(locale, object, subject.call(date_or_time, **options)) + else + subject + end + end + result unless result.is_a?(MissingTranslation) + end + alias_method :resolve_entry, :resolve + + # Picks a translation from a pluralized mnemonic subkey according to English + # pluralization rules : + # - It will pick the :one subkey if count is equal to 1. + # - It will pick the :other subkey otherwise. + # - It will pick the :zero subkey in the special case where count is + # equal to 0 and there is a :zero subkey present. This behaviour is + # not standard with regards to the CLDR pluralization rules. + # Other backends can implement more flexible or complex pluralization rules. + def pluralize(locale, entry, count) + entry = entry.reject { |k, _v| k == :attributes } if entry.is_a?(Hash) + return entry unless entry.is_a?(Hash) && count + + key = pluralization_key(entry, count) + raise InvalidPluralizationData.new(entry, count, key) unless entry.has_key?(key) + entry[key] + end + + # Interpolates values into a given subject. + # + # if the given subject is a string then: + # method interpolates "file %{file} opened by %%{user}", :file => 'test.txt', :user => 'Mr. X' + # # => "file test.txt opened by %{user}" + # + # if the given subject is an array then: + # each element of the array is recursively interpolated (until it finds a string) + # method interpolates ["yes, %{user}", ["maybe no, %{user}", "no, %{user}"]], :user => "bartuz" + # # => ["yes, bartuz", ["maybe no, bartuz", "no, bartuz"]] + def interpolate(locale, subject, values = EMPTY_HASH) + return subject if values.empty? + + case subject + when ::String then I18n.interpolate(subject, values) + when ::Array then subject.map { |element| interpolate(locale, element, values) } + else + subject + end + end + + # Deep interpolation + # + # deep_interpolate { people: { ann: "Ann is %{ann}", john: "John is %{john}" } }, + # ann: 'good', john: 'big' + # #=> { people: { ann: "Ann is good", john: "John is big" } } + def deep_interpolate(locale, data, values = EMPTY_HASH) + return data if values.empty? + + case data + when ::String + I18n.interpolate(data, values) + when ::Hash + data.each_with_object({}) do |(k, v), result| + result[k] = deep_interpolate(locale, v, values) + end + when ::Array + data.map do |v| + deep_interpolate(locale, v, values) + end + else + data + end + end + + # Loads a single translations file by delegating to #load_rb or + # #load_yml depending on the file extension and directly merges the + # data to the existing translations. Raises I18n::UnknownFileType + # for all other file extensions. + def load_file(filename) + type = File.extname(filename).tr('.', '').downcase + raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true) + data, keys_symbolized = send(:"load_#{type}", filename) + unless data.is_a?(Hash) + raise InvalidLocaleData.new(filename, 'expects it to return a hash, but does not') + end + data.each { |locale, d| store_translations(locale, d || {}, skip_symbolize_keys: keys_symbolized) } + + data + end + + # Loads a plain Ruby translations file. eval'ing the file must yield + # a Hash containing translation data with locales as toplevel keys. + def load_rb(filename) + translations = eval(IO.read(filename), binding, filename.to_s) + [translations, false] + end + + # Loads a YAML translations file. The data must have locales as + # toplevel keys. + def load_yml(filename) + begin + if YAML.respond_to?(:unsafe_load_file) # Psych 4.0 way + [YAML.unsafe_load_file(filename, symbolize_names: true, freeze: true), true] + else + [YAML.load_file(filename), false] + end + rescue TypeError, ScriptError, StandardError => e + raise InvalidLocaleData.new(filename, e.inspect) + end + end + alias_method :load_yaml, :load_yml + + # Loads a JSON translations file. The data must have locales as + # toplevel keys. + def load_json(filename) + begin + # Use #load_file as a proxy for a version of JSON where symbolize_names and freeze are supported. + if ::JSON.respond_to?(:load_file) + [::JSON.load_file(filename, symbolize_names: true, freeze: true), true] + else + [::JSON.parse(File.read(filename)), false] + end + rescue TypeError, StandardError => e + raise InvalidLocaleData.new(filename, e.inspect) + end + end + + def translate_localization_format(locale, object, format, options) + format.to_s.gsub(/%(|\^)[aAbBpP]/) do |match| + case match + when '%a' then I18n.t!(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday] + when '%^a' then I18n.t!(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday].upcase + when '%A' then I18n.t!(:"date.day_names", :locale => locale, :format => format)[object.wday] + when '%^A' then I18n.t!(:"date.day_names", :locale => locale, :format => format)[object.wday].upcase + when '%b' then I18n.t!(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon] + when '%^b' then I18n.t!(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon].upcase + when '%B' then I18n.t!(:"date.month_names", :locale => locale, :format => format)[object.mon] + when '%^B' then I18n.t!(:"date.month_names", :locale => locale, :format => format)[object.mon].upcase + when '%p' then I18n.t!(:"time.#{(object.respond_to?(:hour) ? object.hour : 0) < 12 ? :am : :pm}", :locale => locale, :format => format).upcase + when '%P' then I18n.t!(:"time.#{(object.respond_to?(:hour) ? object.hour : 0) < 12 ? :am : :pm}", :locale => locale, :format => format).downcase + end + end + rescue MissingTranslationData => e + e.message + end + + def pluralization_key(entry, count) + key = :zero if count == 0 && entry.has_key?(:zero) + key ||= count == 1 ? :one : :other + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cache.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cache.rb new file mode 100644 index 0000000..40c18d6 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cache.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +# This module allows you to easily cache all responses from the backend - thus +# speeding up the I18n aspects of your application quite a bit. +# +# To enable caching you can simply include the Cache module to the Simple +# backend - or whatever other backend you are using: +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Cache) +# +# You will also need to set a cache store implementation that you want to use: +# +# I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) +# +# You can use any cache implementation you want that provides the same API as +# ActiveSupport::Cache (only the methods #fetch and #write are being used). +# +# The cache_key implementation by default assumes you pass values that return +# a valid key from #hash (see +# https://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can +# configure your own digest method via which responds to #hexdigest (see +# https://ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL/Digest.html): +# +# I18n.cache_key_digest = OpenSSL::Digest::SHA256.new +# +# If you use a lambda as a default value in your translation like this: +# +# I18n.t(:"date.order", :default => lambda {[:month, :day, :year]}) +# +# Then you will always have a cache miss, because each time this method +# is called the lambda will have a different hash value. If you know +# the result of the lambda is a constant as in the example above, then +# to cache this you can make the lambda a constant, like this: +# +# DEFAULT_DATE_ORDER = lambda {[:month, :day, :year]} +# ... +# I18n.t(:"date.order", :default => DEFAULT_DATE_ORDER) +# +# If the lambda may result in different values for each call then consider +# also using the Memoize backend. +# +module I18n + class << self + @@cache_store = nil + @@cache_namespace = nil + @@cache_key_digest = nil + + def cache_store + @@cache_store + end + + def cache_store=(store) + @@cache_store = store + end + + def cache_namespace + @@cache_namespace + end + + def cache_namespace=(namespace) + @@cache_namespace = namespace + end + + def cache_key_digest + @@cache_key_digest + end + + def cache_key_digest=(key_digest) + @@cache_key_digest = key_digest + end + + def perform_caching? + !cache_store.nil? + end + end + + module Backend + # TODO Should the cache be cleared if new translations are stored? + module Cache + def translate(locale, key, options = EMPTY_HASH) + I18n.perform_caching? ? fetch(cache_key(locale, key, options)) { super } : super + end + + protected + + def fetch(cache_key, &block) + result = _fetch(cache_key, &block) + throw(:exception, result) if result.is_a?(MissingTranslation) + result = result.dup if result.frozen? rescue result + result + end + + def _fetch(cache_key, &block) + result = I18n.cache_store.read(cache_key) + return result unless result.nil? + result = catch(:exception, &block) + I18n.cache_store.write(cache_key, result) unless result.is_a?(Proc) + result + end + + def cache_key(locale, key, options) + # This assumes that only simple, native Ruby values are passed to I18n.translate. + "i18n/#{I18n.cache_namespace}/#{locale}/#{digest_item(key)}/#{digest_item(options)}" + end + + private + + def digest_item(key) + I18n.cache_key_digest ? I18n.cache_key_digest.hexdigest(key.to_s) : key.to_s.hash + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cache_file.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cache_file.rb new file mode 100644 index 0000000..0c5e192 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cache_file.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'openssl' + +module I18n + module Backend + # Overwrites the Base load_file method to cache loaded file contents. + module CacheFile + # Optionally provide path_roots array to normalize filename paths, + # to make the cached i18n data portable across environments. + attr_accessor :path_roots + + protected + + # Track loaded translation files in the `i18n.load_file` scope, + # and skip loading the file if its contents are still up-to-date. + def load_file(filename) + initialized = !respond_to?(:initialized?) || initialized? + key = I18n::Backend::Flatten.escape_default_separator(normalized_path(filename)) + old_mtime, old_digest = initialized && lookup(:i18n, key, :load_file) + return if (mtime = File.mtime(filename).to_i) == old_mtime || + (digest = OpenSSL::Digest::SHA256.file(filename).hexdigest) == old_digest + super + store_translations(:i18n, load_file: { key => [mtime, digest] }) + end + + # Translate absolute filename to relative path for i18n key. + def normalized_path(file) + return file unless path_roots + path = path_roots.find(&file.method(:start_with?)) || + raise(InvalidLocaleData.new(file, 'outside expected path roots')) + file.sub(path, path_roots.index(path).to_s) + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cascade.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cascade.rb new file mode 100644 index 0000000..782b07b --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/cascade.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# The Cascade module adds the ability to do cascading lookups to backends that +# are compatible to the Simple backend. +# +# By cascading lookups we mean that for any key that can not be found the +# Cascade module strips one segment off the scope part of the key and then +# tries to look up the key in that scope. +# +# E.g. when a lookup for the key :"foo.bar.baz" does not yield a result then +# the segment :bar will be stripped off the scope part :"foo.bar" and the new +# scope :foo will be used to look up the key :baz. If that does not succeed +# then the remaining scope segment :foo will be omitted, too, and again the +# key :baz will be looked up (now with no scope). +# +# To enable a cascading lookup one passes the :cascade option: +# +# I18n.t(:'foo.bar.baz', :cascade => true) +# +# This will return the first translation found for :"foo.bar.baz", :"foo.baz" +# or :baz in this order. +# +# The cascading lookup takes precedence over resolving any given defaults. +# I.e. defaults will kick in after the cascading lookups haven't succeeded. +# +# This behavior is useful for libraries like ActiveRecord validations where +# the library wants to give users a bunch of more or less fine-grained options +# of scopes for a particular key. +# +# Thanks to Clemens Kofler for the initial idea and implementation! See +# http://github.com/clemens/i18n-cascading-backend + +module I18n + module Backend + module Cascade + def lookup(locale, key, scope = [], options = EMPTY_HASH) + return super unless cascade = options[:cascade] + + cascade = { :step => 1 } unless cascade.is_a?(Hash) + step = cascade[:step] || 1 + offset = cascade[:offset] || 1 + separator = options[:separator] || I18n.default_separator + skip_root = cascade.has_key?(:skip_root) ? cascade[:skip_root] : true + + scope = I18n.normalize_keys(nil, key, scope, separator) + key = (scope.slice!(-offset, offset) || []).join(separator) + + begin + result = super + return result unless result.nil? + scope = scope.dup + end while (!scope.empty? || !skip_root) && scope.slice!(-step, step) + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/chain.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/chain.rb new file mode 100644 index 0000000..e081a91 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/chain.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +module I18n + module Backend + # Backend that chains multiple other backends and checks each of them when + # a translation needs to be looked up. This is useful when you want to use + # standard translations with a Simple backend but store custom application + # translations in a database or other backends. + # + # To use the Chain backend instantiate it and set it to the I18n module. + # You can add chained backends through the initializer or backends + # accessor: + # + # # preserves the existing Simple backend set to I18n.backend + # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend) + # + # The implementation assumes that all backends added to the Chain implement + # a lookup method with the same API as Simple backend does. + # + # Fallback translations using the :default option are only used by the last backend of a chain. + class Chain + module Implementation + include Base + + attr_accessor :backends + + def initialize(*backends) + self.backends = backends + end + + def initialized? + backends.all? do |backend| + backend.instance_eval do + return false unless initialized? + end + end + true + end + + def reload! + backends.each { |backend| backend.reload! } + end + + def eager_load! + backends.each { |backend| backend.eager_load! } + end + + def store_translations(locale, data, options = EMPTY_HASH) + backends.first.store_translations(locale, data, options) + end + + def available_locales + backends.map { |backend| backend.available_locales }.flatten.uniq + end + + def translate(locale, key, default_options = EMPTY_HASH) + namespace = nil + options = Utils.except(default_options, :default) + + backends.each do |backend| + catch(:exception) do + options = default_options if backend == backends.last + translation = backend.translate(locale, key, options) + if namespace_lookup?(translation, options) + namespace = _deep_merge(translation, namespace || {}) + elsif !translation.nil? || (options.key?(:default) && options[:default].nil?) + return translation + end + end + end + + return namespace if namespace + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) + end + + def exists?(locale, key, options = EMPTY_HASH) + backends.any? do |backend| + backend.exists?(locale, key, options) + end + end + + def localize(locale, object, format = :default, options = EMPTY_HASH) + backends.each do |backend| + catch(:exception) do + result = backend.localize(locale, object, format, options) and return result + end + end + throw(:exception, I18n::MissingTranslation.new(locale, format, options)) + end + + protected + def init_translations + backends.each do |backend| + backend.send(:init_translations) + end + end + + def translations + backends.reverse.each_with_object({}) do |backend, memo| + partial_translations = backend.instance_eval do + init_translations unless initialized? + translations + end + Utils.deep_merge!(memo, partial_translations) { |_, a, b| b || a } + end + end + + def namespace_lookup?(result, options) + result.is_a?(Hash) && !options.has_key?(:count) + end + + private + # This is approximately what gets used in ActiveSupport. + # However since we are not guaranteed to run in an ActiveSupport context + # it is wise to have our own copy. We underscore it + # to not pollute the namespace of the including class. + def _deep_merge(hash, other_hash) + copy = hash.dup + other_hash.each_pair do |k,v| + value_from_other = hash[k] + copy[k] = value_from_other.is_a?(Hash) && v.is_a?(Hash) ? _deep_merge(value_from_other, v) : v + end + copy + end + end + + include Implementation + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/fallbacks.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/fallbacks.rb new file mode 100644 index 0000000..0ab50a7 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/fallbacks.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +# I18n locale fallbacks are useful when you want your application to use +# translations from other locales when translations for the current locale are +# missing. E.g. you might want to use :en translations when translations in +# your applications main locale :de are missing. +# +# To enable locale fallbacks you can simply include the Fallbacks module to +# the Simple backend - or whatever other backend you are using: +# +# I18n::Backend::Simple.include(I18n::Backend::Fallbacks) +module I18n + @@fallbacks = nil + + class << self + # Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+. + def fallbacks + @@fallbacks ||= I18n::Locale::Fallbacks.new + if Fiber.respond_to?(:[]) + Fiber[:i18n_fallbacks] || @@fallbacks + else + Thread.current[:i18n_fallbacks] || @@fallbacks + end + end + + # Sets the current fallbacks implementation. Use this to set a different fallbacks implementation. + def fallbacks=(fallbacks) + @@fallbacks = fallbacks.is_a?(Array) ? I18n::Locale::Fallbacks.new(fallbacks) : fallbacks + if Fiber.respond_to?(:[]) + Fiber[:i18n_fallbacks] = @@fallbacks + else + Thread.current[:i18n_fallbacks] = @@fallbacks + end + end + end + + module Backend + module Fallbacks + # Overwrites the Base backend translate method so that it will try each + # locale given by I18n.fallbacks for the given locale. E.g. for the + # locale :"de-DE" it might try the locales :"de-DE", :de and :en + # (depends on the fallbacks implementation) until it finds a result with + # the given options. If it does not find any result for any of the + # locales it will then throw MissingTranslation as usual. + # + # The default option takes precedence over fallback locales only when + # it's a Symbol. When the default contains a String, Proc or Hash + # it is evaluated last after all the fallback locales have been tried. + def translate(locale, key, options = EMPTY_HASH) + return super unless options.fetch(:fallback, true) + return super if options[:fallback_in_progress] + default = extract_non_symbol_default!(options) if options[:default] + + fallback_options = options.merge(:fallback_in_progress => true, fallback_original_locale: locale) + I18n.fallbacks[locale].each do |fallback| + begin + catch(:exception) do + result = super(fallback, key, fallback_options) + unless result.nil? + on_fallback(locale, fallback, key, options) if locale.to_s != fallback.to_s + return result + end + end + rescue I18n::InvalidLocale + # we do nothing when the locale is invalid, as this is a fallback anyways. + end + end + + return if options.key?(:default) && options[:default].nil? + + return super(locale, nil, options.merge(:default => default)) if default + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) + end + + def resolve_entry(locale, object, subject, options = EMPTY_HASH) + return subject if options[:resolve] == false + result = catch(:exception) do + options.delete(:fallback_in_progress) if options.key?(:fallback_in_progress) + + case subject + when Symbol + I18n.translate(subject, **options.merge( + :locale => options[:fallback_original_locale], + :throw => true, + :skip_interpolation => true + )) + when Proc + date_or_time = options.delete(:object) || object + resolve_entry(options[:fallback_original_locale], object, subject.call(date_or_time, **options)) + else + subject + end + end + result unless result.is_a?(MissingTranslation) + end + + def extract_non_symbol_default!(options) + defaults = [options[:default]].flatten + first_non_symbol_default = defaults.detect{|default| !default.is_a?(Symbol)} + if first_non_symbol_default + options[:default] = defaults[0, defaults.index(first_non_symbol_default)] + end + return first_non_symbol_default + end + + def exists?(locale, key, options = EMPTY_HASH) + return super unless options.fetch(:fallback, true) + I18n.fallbacks[locale].each do |fallback| + begin + return true if super(fallback, key, options) + rescue I18n::InvalidLocale + # we do nothing when the locale is invalid, as this is a fallback anyways. + end + end + + false + end + + private + + # Overwrite on_fallback to add specified logic when the fallback succeeds. + def on_fallback(_original_locale, _fallback_locale, _key, _options) + nil + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/flatten.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/flatten.rb new file mode 100644 index 0000000..e9bd9d5 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/flatten.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +module I18n + module Backend + # This module contains several helpers to assist flattening translations. + # You may want to flatten translations for: + # + # 1) speed up lookups, as in the Memoize backend; + # 2) In case you want to store translations in a data store, as in ActiveRecord backend; + # + # You can check both backends above for some examples. + # This module also keeps all links in a hash so they can be properly resolved when flattened. + module Flatten + SEPARATOR_ESCAPE_CHAR = "\001" + FLATTEN_SEPARATOR = "." + + # normalize_keys the flatten way. This method is significantly faster + # and creates way less objects than the one at I18n.normalize_keys. + # It also handles escaping the translation keys. + def self.normalize_flat_keys(locale, key, scope, separator) + keys = [scope, key] + keys.flatten! + keys.compact! + + separator ||= I18n.default_separator + + if separator != FLATTEN_SEPARATOR + from_str = "#{FLATTEN_SEPARATOR}#{separator}" + to_str = "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}" + + keys.map! { |k| k.to_s.tr from_str, to_str } + end + + keys.join(".") + end + + # Receives a string and escape the default separator. + def self.escape_default_separator(key) #:nodoc: + key.to_s.tr(FLATTEN_SEPARATOR, SEPARATOR_ESCAPE_CHAR) + end + + # Shortcut to I18n::Backend::Flatten.normalize_flat_keys + # and then resolve_links. + def normalize_flat_keys(locale, key, scope, separator) + key = I18n::Backend::Flatten.normalize_flat_keys(locale, key, scope, separator) + resolve_link(locale, key) + end + + # Store flattened links. + def links + @links ||= I18n.new_double_nested_cache + end + + # Flatten keys for nested Hashes by chaining up keys: + # + # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind + # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" } + # + def flatten_keys(hash, escape, prev_key=nil, &block) + hash.each_pair do |key, value| + key = escape_default_separator(key) if escape + curr_key = [prev_key, key].compact.join(FLATTEN_SEPARATOR).to_sym + yield curr_key, value + flatten_keys(value, escape, curr_key, &block) if value.is_a?(Hash) + end + end + + # Receives a hash of translations (where the key is a locale and + # the value is another hash) and return a hash with all + # translations flattened. + # + # Nested hashes are included in the flattened hash just if subtree + # is true and Symbols are automatically stored as links. + def flatten_translations(locale, data, escape, subtree) + hash = {} + flatten_keys(data, escape) do |key, value| + if value.is_a?(Hash) + hash[key] = value if subtree + else + store_link(locale, key, value) if value.is_a?(Symbol) + hash[key] = value + end + end + hash + end + + protected + + def store_link(locale, key, link) + links[locale.to_sym][key.to_s] = link.to_s + end + + def resolve_link(locale, key) + key, locale = key.to_s, locale.to_sym + links = self.links[locale] + + if links.key?(key) + links[key] + elsif link = find_link(locale, key) + store_link(locale, key, key.gsub(*link)) + else + key + end + end + + def find_link(locale, key) #:nodoc: + links[locale].each_pair do |from, to| + return [from, to] if key[0, from.length] == from + end && nil + end + + def escape_default_separator(key) #:nodoc: + I18n::Backend::Flatten.escape_default_separator(key) + end + + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/gettext.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/gettext.rb new file mode 100644 index 0000000..0769646 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/gettext.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'i18n/gettext' +require 'i18n/gettext/po_parser' + +module I18n + module Backend + # Experimental support for using Gettext po files to store translations. + # + # To use this you can simply include the module to the Simple backend - or + # whatever other backend you are using. + # + # I18n::Backend::Simple.include(I18n::Backend::Gettext) + # + # Now you should be able to include your Gettext translation (*.po) files to + # the +I18n.load_path+ so they're loaded to the backend and you can use them as + # usual: + # + # I18n.load_path += Dir["path/to/locales/*.po"] + # + # Following the Gettext convention this implementation expects that your + # translation files are named by their locales. E.g. the file en.po would + # contain the translations for the English locale. + # + # To translate text <b>you must use</b> one of the translate methods provided by + # I18n::Gettext::Helpers. + # + # include I18n::Gettext::Helpers + # puts _("some string") + # + # Without it strings containing periods (".") will not be translated. + + module Gettext + class PoData < Hash + def set_comment(msgid_or_sym, comment) + # ignore + end + end + + protected + def load_po(filename) + locale = ::File.basename(filename, '.po').to_sym + data = normalize(locale, parse(filename)) + [{ locale => data }, false] + end + + def parse(filename) + GetText::PoParser.new.parse(::File.read(filename), PoData.new) + end + + def normalize(locale, data) + data.inject({}) do |result, (key, value)| + unless key.nil? || key.empty? + key = key.gsub(I18n::Gettext::CONTEXT_SEPARATOR, '|') + key, value = normalize_pluralization(locale, key, value) if key.index("\000") + + parts = key.split('|').reverse + normalized = parts.inject({}) do |_normalized, part| + { part => _normalized.empty? ? value : _normalized } + end + + Utils.deep_merge!(result, normalized) + end + result + end + end + + def normalize_pluralization(locale, key, value) + # FIXME po_parser includes \000 chars that can not be turned into Symbols + key = key.gsub("\000", I18n::Gettext::PLURAL_SEPARATOR).split(I18n::Gettext::PLURAL_SEPARATOR).first + + keys = I18n::Gettext.plural_keys(locale) + values = value.split("\000") + raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect} on #{locale} locale for msgid #{key.inspect} with values #{values.inspect}" if values.size != keys.size + + result = {} + values.each_with_index { |_value, ix| result[keys[ix]] = _value } + [key, result] + end + + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/interpolation_compiler.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/interpolation_compiler.rb new file mode 100644 index 0000000..0e5c872 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/interpolation_compiler.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +# The InterpolationCompiler module contains optimizations that can tremendously +# speed up the interpolation process on the Simple backend. +# +# It works by defining a pre-compiled method on stored translation Strings that +# already bring all the knowledge about contained interpolation variables etc. +# so that the actual recurring interpolation will be very fast. +# +# To enable pre-compiled interpolations you can simply include the +# InterpolationCompiler module to the Simple backend: +# +# I18n::Backend::Simple.include(I18n::Backend::InterpolationCompiler) +# +# Note that InterpolationCompiler does not yield meaningful results and consequently +# should not be used with Ruby 1.9 (YARV) but improves performance everywhere else +# (jRuby, Rubinius). +module I18n + module Backend + module InterpolationCompiler + module Compiler + extend self + + TOKENIZER = /(%%?\{[^}]+\})/ + + def compile_if_an_interpolation(string) + if interpolated_str?(string) + string = +string + string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + def i18n_interpolate(v = {}) + "#{compiled_interpolation_body(string)}" + end + RUBY_EVAL + end + + string + end + + def interpolated_str?(str) + str.kind_of?(::String) && str =~ TOKENIZER + end + + protected + # tokenize("foo %{bar} baz %%{buz}") # => ["foo ", "%{bar}", " baz ", "%%{buz}"] + def tokenize(str) + str.split(TOKENIZER) + end + + def compiled_interpolation_body(str) + tokenize(str).map do |token| + token.match(TOKENIZER) ? handle_interpolation_token(token) : escape_plain_str(token) + end.join + end + + def handle_interpolation_token(token) + token.start_with?('%%') ? token[1..] : compile_interpolation_token(token[2..-2]) + end + + def compile_interpolation_token(key) + "\#{#{interpolate_or_raise_missing(key)}}" + end + + def interpolate_or_raise_missing(key) + escaped_key = escape_key_sym(key) + RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key) + end + + def interpolate_key(key) + [direct_key(key), nil_key(key), missing_key(key)].join('||') + end + + def direct_key(key) + "((t = v[#{key}]) && t.respond_to?(:call) ? t.call : t)" + end + + def nil_key(key) + "(v.has_key?(#{key}) && '')" + end + + def missing_key(key) + "I18n.config.missing_interpolation_argument_handler.call(#{key}, v, self)" + end + + def reserved_key(key) + "raise(ReservedInterpolationKey.new(#{key}, self))" + end + + def escape_plain_str(str) + str.gsub(/"|\\|#/) {|x| "\\#{x}"} + end + + def escape_key_sym(key) + # rely on Ruby to do all the hard work :) + key.to_sym.inspect + end + end + + def interpolate(locale, string, values) + if string.respond_to?(:i18n_interpolate) + string.i18n_interpolate(values) + elsif values + super + else + string + end + end + + def store_translations(locale, data, options = EMPTY_HASH) + compile_all_strings_in(data) + super + end + + protected + def compile_all_strings_in(data) + data.each_value do |value| + Compiler.compile_if_an_interpolation(value) + compile_all_strings_in(value) if value.kind_of?(Hash) + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/key_value.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/key_value.rb new file mode 100644 index 0000000..b937e25 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/key_value.rb @@ -0,0 +1,204 @@ +# frozen_string_literal: true + +require 'i18n/backend/base' + +module I18n + + begin + require 'oj' + class JSON + class << self + def encode(value) + Oj::Rails.encode(value) + end + def decode(value) + Oj.load(value) + end + end + end + rescue LoadError + require 'active_support/json' + JSON = ActiveSupport::JSON + end + + module Backend + # This is a basic backend for key value stores. It receives on + # initialization the store, which should respond to three methods: + # + # * store#[](key) - Used to get a value + # * store#[]=(key, value) - Used to set a value + # * store#keys - Used to get all keys + # + # Since these stores only supports string, all values are converted + # to JSON before being stored, allowing it to also store booleans, + # hashes and arrays. However, this store does not support Procs. + # + # As the ActiveRecord backend, Symbols are just supported when loading + # translations from the filesystem or through explicit store translations. + # + # Also, avoid calling I18n.available_locales since it's a somehow + # expensive operation in most stores. + # + # == Example + # + # To setup I18n to use TokyoCabinet in memory is quite straightforward: + # + # require 'rufus/tokyo/cabinet' # gem install rufus-tokyo + # I18n.backend = I18n::Backend::KeyValue.new(Rufus::Tokyo::Cabinet.new('*')) + # + # == Performance + # + # You may make this backend even faster by including the Memoize module. + # However, notice that you should properly clear the cache if you change + # values directly in the key-store. + # + # == Subtrees + # + # In most backends, you are allowed to retrieve part of a translation tree: + # + # I18n.backend.store_translations :en, :foo => { :bar => :baz } + # I18n.t "foo" #=> { :bar => :baz } + # + # This backend supports this feature by default, but it slows down the storage + # of new data considerably and makes hard to delete entries. That said, you are + # allowed to disable the storage of subtrees on initialization: + # + # I18n::Backend::KeyValue.new(@store, false) + # + # This is useful if you are using a KeyValue backend chained to a Simple backend. + class KeyValue + module Implementation + attr_accessor :store + + include Base, Flatten + + def initialize(store, subtrees=true) + @store, @subtrees = store, subtrees + end + + def initialized? + !@store.nil? + end + + def store_translations(locale, data, options = EMPTY_HASH) + escape = options.fetch(:escape, true) + flatten_translations(locale, data, escape, @subtrees).each do |key, value| + key = "#{locale}.#{key}" + + case value + when Hash + if @subtrees && (old_value = @store[key]) + old_value = JSON.decode(old_value) + value = Utils.deep_merge!(Utils.deep_symbolize_keys(old_value), value) if old_value.is_a?(Hash) + end + when Proc + raise "Key-value stores cannot handle procs" + end + + @store[key] = JSON.encode(value) unless value.is_a?(Symbol) + end + end + + def available_locales + locales = @store.keys.map { |k| k =~ /\./; $` } + locales.uniq! + locales.compact! + locales.map! { |k| k.to_sym } + locales + end + + protected + + # Queries the translations from the key-value store and converts + # them into a hash such as the one returned from loading the + # haml files + def translations + @translations = Utils.deep_symbolize_keys(@store.keys.clone.map do |main_key| + main_value = JSON.decode(@store[main_key]) + main_key.to_s.split(".").reverse.inject(main_value) do |value, key| + {key.to_sym => value} + end + end.inject{|hash, elem| Utils.deep_merge!(hash, elem)}) + end + + def init_translations + # NO OP + # This call made also inside Simple Backend and accessed by + # other plugins like I18n-js and babilu and + # to use it along with the Chain backend we need to + # provide a uniform API even for protected methods :S + end + + def subtrees? + @subtrees + end + + def lookup(locale, key, scope = [], options = EMPTY_HASH) + key = normalize_flat_keys(locale, key, scope, options[:separator]) + value = @store["#{locale}.#{key}"] + value = JSON.decode(value) if value + + if value.is_a?(Hash) + Utils.deep_symbolize_keys(value) + elsif !value.nil? + value + elsif !@subtrees + SubtreeProxy.new("#{locale}.#{key}", @store) + end + end + + def pluralize(locale, entry, count) + if subtrees? + super + else + return entry unless entry.is_a?(Hash) + key = pluralization_key(entry, count) + entry[key] + end + end + end + + class SubtreeProxy + def initialize(master_key, store) + @master_key = master_key + @store = store + @subtree = nil + end + + def has_key?(key) + @subtree && @subtree.has_key?(key) || self[key] + end + + def [](key) + unless @subtree && value = @subtree[key] + value = @store["#{@master_key}.#{key}"] + if value + value = JSON.decode(value) + (@subtree ||= {})[key] = value + end + end + value + end + + def is_a?(klass) + Hash == klass || super + end + alias :kind_of? :is_a? + + def instance_of?(klass) + Hash == klass || super + end + + def nil? + @subtree.nil? + end + + def inspect + @subtree.inspect + end + end + + include Implementation + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/lazy_loadable.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/lazy_loadable.rb new file mode 100644 index 0000000..575b32b --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/lazy_loadable.rb @@ -0,0 +1,184 @@ +# frozen_string_literal: true + +module I18n + module Backend + # Backend that lazy loads translations based on the current locale. This + # implementation avoids loading all translations up front. Instead, it only + # loads the translations that belong to the current locale. This offers a + # performance incentive in local development and test environments for + # applications with many translations for many different locales. It's + # particularly useful when the application only refers to a single locales' + # translations at a time (ex. A Rails workload). The implementation + # identifies which translation files from the load path belong to the + # current locale by pattern matching against their path name. + # + # Specifically, a translation file is considered to belong to a locale if: + # a) the filename is in the I18n load path + # b) the filename ends in a supported extension (ie. .yml, .json, .po, .rb) + # c) the filename starts with the locale identifier + # d) the locale identifier and optional proceeding text is separated by an underscore, ie. "_". + # + # Examples: + # Valid files that will be selected by this backend: + # + # "files/locales/en_translation.yml" (Selected for locale "en") + # "files/locales/fr.po" (Selected for locale "fr") + # + # Invalid files that won't be selected by this backend: + # + # "files/locales/translation-file" + # "files/locales/en-translation.unsupported" + # "files/locales/french/translation.yml" + # "files/locales/fr/translation.yml" + # + # The implementation uses this assumption to defer the loading of + # translation files until the current locale actually requires them. + # + # The backend has two working modes: lazy_load and eager_load. + # + # Note: This backend should only be enabled in test environments! + # When the mode is set to false, the backend behaves exactly like the + # Simple backend, with an additional check that the paths being loaded + # abide by the format. If paths can't be matched to the format, an error is raised. + # + # You can configure lazy loaded backends through the initializer or backends + # accessor: + # + # # In test environments + # + # I18n.backend = I18n::Backend::LazyLoadable.new(lazy_load: true) + # + # # In other environments, such as production and CI + # + # I18n.backend = I18n::Backend::LazyLoadable.new(lazy_load: false) # default + # + class LocaleExtractor + class << self + def locale_from_path(path) + name = File.basename(path, ".*") + locale = name.split("_").first + locale.to_sym unless locale.nil? + end + end + end + + class LazyLoadable < Simple + def initialize(lazy_load: false) + @lazy_load = lazy_load + end + + # Returns whether the current locale is initialized. + def initialized? + if lazy_load? + initialized_locales[I18n.locale] + else + super + end + end + + # Clean up translations and uninitialize all locales. + def reload! + if lazy_load? + @initialized_locales = nil + @translations = nil + else + super + end + end + + # Eager loading is not supported in the lazy context. + def eager_load! + if lazy_load? + raise UnsupportedMethod.new(__method__, self.class, "Cannot eager load translations because backend was configured with lazy_load: true.") + else + super + end + end + + # Parse the load path and extract all locales. + def available_locales + if lazy_load? + I18n.load_path.map { |path| LocaleExtractor.locale_from_path(path) }.uniq + else + super + end + end + + def lookup(locale, key, scope = [], options = EMPTY_HASH) + if lazy_load? + I18n.with_locale(locale) do + super + end + else + super + end + end + + protected + + + # Load translations from files that belong to the current locale. + def init_translations + file_errors = if lazy_load? + initialized_locales[I18n.locale] = true + load_translations_and_collect_file_errors(filenames_for_current_locale) + else + @initialized = true + load_translations_and_collect_file_errors(I18n.load_path) + end + + raise InvalidFilenames.new(file_errors) unless file_errors.empty? + end + + def initialized_locales + @initialized_locales ||= Hash.new(false) + end + + private + + def lazy_load? + @lazy_load + end + + class FilenameIncorrect < StandardError + def initialize(file, expected_locale, unexpected_locales) + super "#{file} can only load translations for \"#{expected_locale}\". Found translations for: #{unexpected_locales}." + end + end + + # Loads each file supplied and asserts that the file only loads + # translations as expected by the name. The method returns a list of + # errors corresponding to offending files. + def load_translations_and_collect_file_errors(files) + errors = [] + + load_translations(files) do |file, loaded_translations| + assert_file_named_correctly!(file, loaded_translations) + rescue FilenameIncorrect => e + errors << e + end + + errors + end + + # Select all files from I18n load path that belong to current locale. + # These files must start with the locale identifier (ie. "en", "pt-BR"), + # followed by an "_" demarcation to separate proceeding text. + def filenames_for_current_locale + I18n.load_path.flatten.select do |path| + LocaleExtractor.locale_from_path(path) == I18n.locale + end + end + + # Checks if a filename is named in correspondence to the translations it loaded. + # The locale extracted from the path must be the single locale loaded in the translations. + def assert_file_named_correctly!(file, translations) + loaded_locales = translations.keys.map(&:to_sym) + expected_locale = LocaleExtractor.locale_from_path(file) + unexpected_locales = loaded_locales.reject { |locale| locale == expected_locale } + + raise FilenameIncorrect.new(file, expected_locale, unexpected_locales) unless unexpected_locales.empty? + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/memoize.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/memoize.rb new file mode 100644 index 0000000..3293d2b --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/memoize.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Memoize module simply memoizes the values returned by lookup using +# a flat hash and can tremendously speed up the lookup process in a backend. +# +# To enable it you can simply include the Memoize module to your backend: +# +# I18n::Backend::Simple.include(I18n::Backend::Memoize) +# +# Notice that it's the responsibility of the backend to define whenever the +# cache should be cleaned. +module I18n + module Backend + module Memoize + def available_locales + @memoized_locales ||= super + end + + def store_translations(locale, data, options = EMPTY_HASH) + reset_memoizations!(locale) + super + end + + def reload! + reset_memoizations! + super + end + + def eager_load! + memoized_lookup + available_locales + super + end + + protected + + def lookup(locale, key, scope = nil, options = EMPTY_HASH) + flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale, + key, scope, options[:separator]).to_sym + flat_hash = memoized_lookup[locale.to_sym] + flat_hash.key?(flat_key) ? flat_hash[flat_key] : (flat_hash[flat_key] = super) + end + + def memoized_lookup + @memoized_lookup ||= I18n.new_double_nested_cache + end + + def reset_memoizations!(locale=nil) + @memoized_locales = nil + (locale ? memoized_lookup[locale.to_sym] : memoized_lookup).clear + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/metadata.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/metadata.rb new file mode 100644 index 0000000..51ea7a2 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/metadata.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# I18n translation metadata is useful when you want to access information +# about how a translation was looked up, pluralized or interpolated in +# your application. +# +# msg = I18n.t(:message, :default => 'Hi!', :scope => :foo) +# msg.translation_metadata +# # => { :key => :message, :scope => :foo, :default => 'Hi!' } +# +# If a :count option was passed to #translate it will be set to the metadata. +# Likewise, if any interpolation variables were passed they will also be set. +# +# To enable translation metadata you can simply include the Metadata module +# into the Simple backend class - or whatever other backend you are using: +# +# I18n::Backend::Simple.include(I18n::Backend::Metadata) +# +module I18n + module Backend + module Metadata + class << self + def included(base) + Object.class_eval do + def translation_metadata + unless self.frozen? + @translation_metadata ||= {} + else + {} + end + end + + def translation_metadata=(translation_metadata) + @translation_metadata = translation_metadata unless self.frozen? + end + end unless Object.method_defined?(:translation_metadata) + end + end + + def translate(locale, key, options = EMPTY_HASH) + metadata = { + :locale => locale, + :key => key, + :scope => options[:scope], + :default => options[:default], + :separator => options[:separator], + :values => options.reject { |name, _value| RESERVED_KEYS.include?(name) } + } + with_metadata(metadata) { super } + end + + def interpolate(locale, entry, values = EMPTY_HASH) + metadata = entry.translation_metadata.merge(:original => entry) + with_metadata(metadata) { super } + end + + def pluralize(locale, entry, count) + with_metadata(:count => count) { super } + end + + protected + + def with_metadata(metadata, &block) + result = yield + result.translation_metadata = result.translation_metadata.merge(metadata) if result + result + end + + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/pluralization.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/pluralization.rb new file mode 100644 index 0000000..1d3277b --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/pluralization.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +# I18n Pluralization are useful when you want your application to +# customize pluralization rules. +# +# To enable locale specific pluralizations you can simply include the +# Pluralization module to the Simple backend - or whatever other backend you +# are using. +# +# I18n::Backend::Simple.include(I18n::Backend::Pluralization) +# +# You also need to make sure to provide pluralization algorithms to the +# backend, i.e. include them to your I18n.load_path accordingly. +module I18n + module Backend + module Pluralization + # Overwrites the Base backend translate method so that it will check the + # translation meta data space (:i18n) for a locale specific pluralization + # rule and use it to pluralize the given entry. I.e., the library expects + # pluralization rules to be stored at I18n.t(:'i18n.plural.rule') + # + # Pluralization rules are expected to respond to #call(count) and + # return a pluralization key. Valid keys depend on the pluralization + # rules for the locale, as defined in the CLDR. + # As of v41, 6 locale-specific plural categories are defined: + # :few, :many, :one, :other, :two, :zero + # + # n.b., The :one plural category does not imply the number 1. + # Instead, :one is a category for any number that behaves like 1 in + # that locale. For example, in some locales, :one is used for numbers + # that end in "1" (like 1, 21, 151) but that don't end in + # 11 (like 11, 111, 10311). + # Similar notes apply to the :two, and :zero plural categories. + # + # If you want to have different strings for the categories of count == 0 + # (e.g. "I don't have any cars") or count == 1 (e.g. "I have a single car") + # use the explicit `"0"` and `"1"` keys. + # https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules + def pluralize(locale, entry, count) + return entry unless entry.is_a?(Hash) && count + + pluralizer = pluralizer(locale) + if pluralizer.respond_to?(:call) + # Deprecation: The use of the `zero` key in this way is incorrect. + # Users that want a different string for the case of `count == 0` should use the explicit "0" key instead. + # We keep this incorrect behaviour for now for backwards compatibility until we can remove it. + # Ref: https://github.com/ruby-i18n/i18n/issues/629 + return entry[:zero] if count == 0 && entry.has_key?(:zero) + + # "0" and "1" are special cases + # https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules + if count == 0 || count == 1 + value = entry[symbolic_count(count)] + return value if value + end + + # Lateral Inheritance of "count" attribute (http://www.unicode.org/reports/tr35/#Lateral_Inheritance): + # > If there is no value for a path, and that path has a [@count="x"] attribute and value, then: + # > 1. If "x" is numeric, the path falls back to the path with [@count=«the plural rules category for x for that locale»], within that the same locale. + # > 2. If "x" is anything but "other", it falls back to a path [@count="other"], within that the same locale. + # > 3. If "x" is "other", it falls back to the path that is completely missing the count item, within that the same locale. + # Note: We don't yet implement #3 above, since we haven't decided how lateral inheritance attributes should be represented. + plural_rule_category = pluralizer.call(count) + + value = if entry.has_key?(plural_rule_category) || entry.has_key?(:other) + entry[plural_rule_category] || entry[:other] + else + raise InvalidPluralizationData.new(entry, count, plural_rule_category) + end + else + super + end + end + + protected + + def pluralizers + @pluralizers ||= {} + end + + def pluralizer(locale) + pluralizers[locale] ||= I18n.t(:'i18n.plural.rule', :locale => locale, :resolve => false) + end + + private + + # Normalizes categories of 0.0 and 1.0 + # and returns the symbolic version + def symbolic_count(count) + count = 0 if count == 0 + count = 1 if count == 1 + count.to_s.to_sym + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/simple.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/simple.rb new file mode 100644 index 0000000..7cef780 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/simple.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require 'i18n/backend/base' + +module I18n + module Backend + # A simple backend that reads translations from YAML files and stores them in + # an in-memory hash. Relies on the Base backend. + # + # The implementation is provided by a Implementation module allowing to easily + # extend Simple backend's behavior by including modules. E.g.: + # + # module I18n::Backend::Pluralization + # def pluralize(*args) + # # extended pluralization logic + # super + # end + # end + # + # I18n::Backend::Simple.include(I18n::Backend::Pluralization) + class Simple + module Implementation + include Base + + # Mutex to ensure that concurrent translations loading will be thread-safe + MUTEX = Mutex.new + + def initialized? + @initialized ||= false + end + + # Stores translations for the given locale in memory. + # This uses a deep merge for the translations hash, so existing + # translations will be overwritten by new ones only at the deepest + # level of the hash. + def store_translations(locale, data, options = EMPTY_HASH) + if I18n.enforce_available_locales && + I18n.available_locales_initialized? && + !I18n.locale_available?(locale) + return data + end + locale = locale.to_sym + translations[locale] ||= Concurrent::Hash.new + data = Utils.deep_symbolize_keys(data) unless options.fetch(:skip_symbolize_keys, false) + MUTEX.synchronize { Utils.deep_merge!(translations[locale], data) } + end + + # Get available locales from the translations hash + def available_locales + init_translations unless initialized? + translations.inject([]) do |locales, (locale, data)| + locales << locale unless data.size <= 1 && (data.empty? || data.has_key?(:i18n)) + locales + end + end + + # Clean up translations hash and set initialized to false on reload! + def reload! + @initialized = false + @translations = nil + super + end + + def eager_load! + init_translations unless initialized? + super + end + + def translations(do_init: false) + # To avoid returning empty translations, + # call `init_translations` + init_translations if do_init && !initialized? + + @translations ||= Concurrent::Hash.new do |h, k| + MUTEX.synchronize do + h[k] = Concurrent::Hash.new + end + end + end + + protected + + def init_translations + load_translations + @initialized = true + end + + # Looks up a translation from the translations hash. Returns nil if + # either key is nil, or locale, scope or key do not exist as a key in the + # nested translations hash. Splits keys or scopes containing dots + # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as + # <tt>%w(currency format)</tt>. + def lookup(locale, key, scope = [], options = EMPTY_HASH) + init_translations unless initialized? + keys = I18n.normalize_keys(locale, key, scope, options[:separator]) + + keys.inject(translations) do |result, _key| + return nil unless result.is_a?(Hash) + unless result.has_key?(_key) + _key = _key.to_s.to_sym + return nil unless result.has_key?(_key) + end + result = result[_key] + result = resolve_entry(locale, _key, result, Utils.except(options.merge(:scope => nil), :count)) if result.is_a?(Symbol) + result + end + end + end + + include Implementation + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/transliterator.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/transliterator.rb new file mode 100644 index 0000000..00679c5 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/backend/transliterator.rb @@ -0,0 +1,109 @@ +# encoding: utf-8 +# frozen_string_literal: true + +module I18n + module Backend + module Transliterator + DEFAULT_REPLACEMENT_CHAR = "?" + + # Given a locale and a UTF-8 string, return the locale's ASCII + # approximation for the string. + def transliterate(locale, string, replacement = nil) + @transliterators ||= {} + @transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule', + :locale => locale, :resolve => false, :default => {}) + @transliterators[locale].transliterate(string, replacement) + end + + # Get a transliterator instance. + def self.get(rule = nil) + if !rule || rule.kind_of?(Hash) + HashTransliterator.new(rule) + elsif rule.kind_of? Proc + ProcTransliterator.new(rule) + else + raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash." + end + end + + # A transliterator which accepts a Proc as its transliteration rule. + class ProcTransliterator + def initialize(rule) + @rule = rule + end + + def transliterate(string, replacement = nil) + @rule.call(string) + end + end + + # A transliterator which accepts a Hash of characters as its translation + # rule. + class HashTransliterator + DEFAULT_APPROXIMATIONS = { + "À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE", + "Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I", + "Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O", + "Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U", + "Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "ẞ"=>"SS", "à"=>"a", + "á"=>"a", "â"=>"a", "ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", + "è"=>"e", "é"=>"e", "ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", + "ï"=>"i", "ð"=>"d", "ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", + "ö"=>"o", "ø"=>"o", "ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", + "þ"=>"th", "ÿ"=>"y", "Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", + "ą"=>"a", "Ć"=>"C", "ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", + "Č"=>"C", "č"=>"c", "Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", + "ē"=>"e", "Ĕ"=>"E", "ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", + "Ě"=>"E", "ě"=>"e", "Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", + "ġ"=>"g", "Ģ"=>"G", "ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", + "Ĩ"=>"I", "ĩ"=>"i", "Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", + "į"=>"i", "İ"=>"I", "ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", + "Ķ"=>"K", "ķ"=>"k", "ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", + "Ľ"=>"L", "ľ"=>"l", "Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", + "ń"=>"n", "Ņ"=>"N", "ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", + "ŋ"=>"ng", "Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", + "Œ"=>"OE", "œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", + "ř"=>"r", "Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", + "Š"=>"S", "š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", + "ŧ"=>"t", "Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", + "Ů"=>"U", "ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", + "ŵ"=>"w", "Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", + "ż"=>"z", "Ž"=>"Z", "ž"=>"z", "Ǫ"=>"O", "ǫ"=>"o", "Ǭ"=>"O", + "ǭ"=>"o" + }.freeze + + def initialize(rule = nil) + @rule = rule + add_default_approximations + add rule if rule + end + + def transliterate(string, replacement = nil) + replacement ||= DEFAULT_REPLACEMENT_CHAR + string.gsub(/[^\x00-\x7f]/u) do |char| + approximations[char] || (replacement == :none ? char : replacement) + end + end + + private + + def approximations + @approximations ||= {} + end + + def add_default_approximations + DEFAULT_APPROXIMATIONS.each do |key, value| + approximations[key] = value + end + end + + # Add transliteration rules to the approximations hash. + def add(hash) + hash.each do |key, value| + approximations[key.to_s] = value.to_s + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/config.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/config.rb new file mode 100644 index 0000000..2424c27 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/config.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +require 'set' + +module I18n + class Config + # The only configuration value that is not global and scoped to thread is :locale. + # It defaults to the default_locale. + def locale + defined?(@locale) && @locale != nil ? @locale : default_locale + end + + def initialize + @owner = Fiber.current + end + + def owned_by?(fiber) + @owner == fiber + end + + def owner=(fiber) + @owner = fiber + end + + def initialize_copy(other) + @owner = Fiber.current + end + + # Returns a copied configuration with the provided attributes set. + def with(**attrs) + dup.tap do |copy| + attrs.each do |name, value| + copy.public_send("#{name}=", value) + end + end + end + + # Sets this configuration as the current one for the active execution context. + # The stored configuration is frozen to avoid sharing mutable state between fibers. + def set! + self.owner = Fiber.current unless frozen? + freeze + I18n.config = self + self + end + + # Sets the current locale pseudo-globally, i.e. in the Thread.current or Fiber local hash. + def locale=(locale) + I18n.enforce_available_locales!(locale) + @locale = locale && locale.to_sym + end + + # Returns the current backend. Defaults to +Backend::Simple+. + def backend + @@backend ||= Backend::Simple.new + end + + # Sets the current backend. Used to set a custom backend. + def backend=(backend) + @@backend = backend + end + + # Returns the current default locale. Defaults to :'en' + def default_locale + @@default_locale ||= :en + end + + # Sets the current default locale. Used to set a custom default locale. + def default_locale=(locale) + I18n.enforce_available_locales!(locale) + @@default_locale = locale && locale.to_sym + end + + # Returns an array of locales for which translations are available. + # Unless you explicitly set these through I18n.available_locales= + # the call will be delegated to the backend. + def available_locales + @@available_locales ||= nil + @@available_locales || backend.available_locales + end + + # Caches the available locales list as both strings and symbols in a Set, so + # that we can have faster lookups to do the available locales enforce check. + def available_locales_set #:nodoc: + @@available_locales_set ||= available_locales.inject(Set.new) do |set, locale| + set << locale.to_s << locale.to_sym + end + end + + # Sets the available locales. + def available_locales=(locales) + @@available_locales = Array(locales).map { |locale| locale.to_sym } + @@available_locales = nil if @@available_locales.empty? + @@available_locales_set = nil + end + + # Returns true if the available_locales have been initialized + def available_locales_initialized? + ( !!defined?(@@available_locales) && !!@@available_locales ) + end + + # Clears the available locales set so it can be recomputed again after I18n + # gets reloaded. + def clear_available_locales_set #:nodoc: + @@available_locales_set = nil + end + + # Returns the current default scope separator. Defaults to '.' + def default_separator + @@default_separator ||= '.' + end + + # Sets the current default scope separator. + def default_separator=(separator) + @@default_separator = separator + end + + # Returns the current exception handler. Defaults to an instance of + # I18n::ExceptionHandler. + def exception_handler + @@exception_handler ||= ExceptionHandler.new + end + + # Sets the exception handler. + def exception_handler=(exception_handler) + @@exception_handler = exception_handler + end + + # Returns the current handler for situations when interpolation argument + # is missing. MissingInterpolationArgument will be raised by default. + def missing_interpolation_argument_handler + @@missing_interpolation_argument_handler ||= lambda do |missing_key, provided_hash, string| + raise MissingInterpolationArgument.new(missing_key, provided_hash, string) + end + end + + # Sets the missing interpolation argument handler. It can be any + # object that responds to #call. The arguments that will be passed to #call + # are the same as for MissingInterpolationArgument initializer. Use +Proc.new+ + # if you don't care about arity. + # + # == Example: + # You can suppress raising an exception and return string instead: + # + # I18n.config.missing_interpolation_argument_handler = Proc.new do |key| + # "#{key} is missing" + # end + def missing_interpolation_argument_handler=(exception_handler) + @@missing_interpolation_argument_handler = exception_handler + end + + # Allow clients to register paths providing translation data sources. The + # backend defines acceptable sources. + # + # E.g. the provided SimpleBackend accepts a list of paths to translation + # files which are either named *.rb and contain plain Ruby Hashes or are + # named *.yml and contain YAML data. So for the SimpleBackend clients may + # register translation files like this: + # I18n.load_path << 'path/to/locale/en.yml' + def load_path + @@load_path ||= [] + end + + # Sets the load path instance. Custom implementations are expected to + # behave like a Ruby Array. + def load_path=(load_path) + @@load_path = load_path + @@available_locales_set = nil + backend.reload! + end + + # Whether or not to verify if locales are in the list of available locales. + # Defaults to true. + @@enforce_available_locales = true + def enforce_available_locales + @@enforce_available_locales + end + + def enforce_available_locales=(enforce_available_locales) + @@enforce_available_locales = enforce_available_locales + end + + # Returns the current interpolation patterns. Defaults to + # I18n::DEFAULT_INTERPOLATION_PATTERNS. + def interpolation_patterns + @@interpolation_patterns ||= I18n::DEFAULT_INTERPOLATION_PATTERNS.dup + end + + # Sets the current interpolation patterns. Used to set a interpolation + # patterns. + # + # E.g. using {{}} as a placeholder like "{{hello}}, world!": + # + # I18n.config.interpolation_patterns << /\{\{(\w+)\}\}/ + def interpolation_patterns=(interpolation_patterns) + @@interpolation_patterns = interpolation_patterns + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/exceptions.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/exceptions.rb new file mode 100644 index 0000000..100055c --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/exceptions.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +module I18n + class ExceptionHandler + def call(exception, _locale, _key, _options) + if exception.is_a?(MissingTranslation) + exception.message + else + raise exception + end + end + end + + class ArgumentError < ::ArgumentError; end + + class Disabled < ArgumentError + def initialize(method) + super(<<~MESSAGE) + I18n.#{method} is currently disabled, likely because your application is still in its loading phase. + + This method is meant to display text in the user locale, so calling it before the user locale has + been set is likely to display text from the wrong locale to some users. + + If you have a legitimate reason to access i18n data outside of the user flow, you can do so by passing + the desired locale explicitly with the `locale` argument, e.g. `I18n.#{method}(..., locale: :en)` + MESSAGE + end + end + + class InvalidLocale < ArgumentError + attr_reader :locale + def initialize(locale) + @locale = locale + super "#{locale.inspect} is not a valid locale" + end + end + + class InvalidLocaleData < ArgumentError + attr_reader :filename + def initialize(filename, exception_message) + @filename, @exception_message = filename, exception_message + super "can not load translations from #{filename}: #{exception_message}" + end + end + + class MissingTranslation < ArgumentError + module Base + PERMITTED_KEYS = [:scope, :default].freeze + + attr_reader :locale, :key, :options + + def initialize(locale, key, options = EMPTY_HASH) + @key, @locale, @options = key, locale, options.slice(*PERMITTED_KEYS) + options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) } + end + + def keys + @keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys| + keys << 'no key' if keys.size < 2 + end + end + + def message + if (default = options[:default]).is_a?(Array) && default.any? + other_options = ([key, *default]).map { |k| normalized_option(k).prepend('- ') }.join("\n") + "Translation missing. Options considered were:\n#{other_options}" + else + "Translation missing: #{keys.join('.')}" + end + end + + def normalized_option(key) + I18n.normalize_keys(locale, key, options[:scope]).join('.') + end + + alias :to_s :message + + def to_exception + MissingTranslationData.new(locale, key, options) + end + end + + include Base + end + + class MissingTranslationData < ArgumentError + include MissingTranslation::Base + end + + class InvalidPluralizationData < ArgumentError + attr_reader :entry, :count, :key + def initialize(entry, count, key) + @entry, @count, @key = entry, count, key + super "translation data #{entry.inspect} can not be used with :count => #{count}. key '#{key}' is missing." + end + end + + class MissingInterpolationArgument < ArgumentError + attr_reader :key, :values, :string + def initialize(key, values, string) + @key, @values, @string = key, values, string + super "missing interpolation argument #{key.inspect} in #{string.inspect} (#{values.inspect} given)" + end + end + + class ReservedInterpolationKey < ArgumentError + attr_reader :key, :string + def initialize(key, string) + @key, @string = key, string + super "reserved key #{key.inspect} used in #{string.inspect}" + end + end + + class UnknownFileType < ArgumentError + attr_reader :type, :filename + def initialize(type, filename) + @type, @filename = type, filename + super "can not load translations from #{filename}, the file type #{type} is not known" + end + end + + class UnsupportedMethod < ArgumentError + attr_reader :method, :backend_klass, :msg + def initialize(method, backend_klass, msg) + @method = method + @backend_klass = backend_klass + @msg = msg + super "#{backend_klass} does not support the ##{method} method. #{msg}" + end + end + + class InvalidFilenames < ArgumentError + NUMBER_OF_ERRORS_SHOWN = 20 + def initialize(file_errors) + super <<~MSG + Found #{file_errors.count} error(s). + The first #{[file_errors.count, NUMBER_OF_ERRORS_SHOWN].min} error(s): + #{file_errors.map(&:message).first(NUMBER_OF_ERRORS_SHOWN).join("\n")} + + To use the LazyLoadable backend: + 1. Filenames must start with the locale. + 2. An underscore must separate the locale with any optional text that follows. + 3. The file must only contain translation data for the single locale. + + Example: + "/config/locales/fr.yml" which contains: + ```yml + fr: + dog: + chien + ``` + MSG + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext.rb new file mode 100644 index 0000000..858daff --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module I18n + module Gettext + PLURAL_SEPARATOR = "\001" + CONTEXT_SEPARATOR = "\004" + + autoload :Helpers, 'i18n/gettext/helpers' + + @@plural_keys = { :en => [:one, :other] } + + class << self + # returns an array of plural keys for the given locale or the whole hash + # of locale mappings to plural keys so that we can convert from gettext's + # integer-index based style + # TODO move this information to the pluralization module + def plural_keys(*args) + args.empty? ? @@plural_keys : @@plural_keys[args.first] || @@plural_keys[:en] + end + + def extract_scope(msgid, separator) + scope = msgid.to_s.split(separator) + msgid = scope.pop + [scope, msgid] + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext/helpers.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext/helpers.rb new file mode 100644 index 0000000..d077619 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext/helpers.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'i18n/gettext' + +module I18n + module Gettext + # Implements classical Gettext style accessors. To use this include the + # module to the global namespace or wherever you want to use it. + # + # include I18n::Gettext::Helpers + module Helpers + # Makes dynamic translation messages readable for the gettext parser. + # <tt>_(fruit)</tt> cannot be understood by the gettext parser. To help the parser find all your translations, + # you can add <tt>fruit = N_("Apple")</tt> which does not translate, but tells the parser: "Apple" needs translation. + # * msgid: the message id. + # * Returns: msgid. + def N_(msgsid) + msgsid + end + + def gettext(msgid, options = EMPTY_HASH) + I18n.t(msgid, **{:default => msgid, :separator => '|'}.merge(options)) + end + alias _ gettext + + def sgettext(msgid, separator = '|') + scope, msgid = I18n::Gettext.extract_scope(msgid, separator) + I18n.t(msgid, :scope => scope, :default => msgid, :separator => separator) + end + alias s_ sgettext + + def pgettext(msgctxt, msgid) + separator = I18n::Gettext::CONTEXT_SEPARATOR + sgettext([msgctxt, msgid].join(separator), separator) + end + alias p_ pgettext + + def ngettext(msgid, msgid_plural, n = 1) + nsgettext(msgid, msgid_plural, n) + end + alias n_ ngettext + + # Method signatures: + # nsgettext('Fruits|apple', 'apples', 2) + # nsgettext(['Fruits|apple', 'apples'], 2) + def nsgettext(msgid, msgid_plural, n = 1, separator = '|') + if msgid.is_a?(Array) + msgid, msgid_plural, n, separator = msgid[0], msgid[1], msgid_plural, n + separator = '|' unless separator.is_a?(::String) + end + + scope, msgid = I18n::Gettext.extract_scope(msgid, separator) + default = { :one => msgid, :other => msgid_plural } + I18n.t(msgid, :default => default, :count => n, :scope => scope, :separator => separator) + end + alias ns_ nsgettext + + # Method signatures: + # npgettext('Fruits', 'apple', 'apples', 2) + # npgettext('Fruits', ['apple', 'apples'], 2) + def npgettext(msgctxt, msgid, msgid_plural, n = 1) + separator = I18n::Gettext::CONTEXT_SEPARATOR + + if msgid.is_a?(Array) + msgid_plural, msgid, n = msgid[1], [msgctxt, msgid[0]].join(separator), msgid_plural + else + msgid = [msgctxt, msgid].join(separator) + end + + nsgettext(msgid, msgid_plural, n, separator) + end + alias np_ npgettext + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext/po_parser.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext/po_parser.rb new file mode 100644 index 0000000..a07fdc5 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/gettext/po_parser.rb @@ -0,0 +1,329 @@ +=begin + poparser.rb - Generate a .mo + + Copyright (C) 2003-2009 Masao Mutoh <mutoh at highway.ne.jp> + + You may redistribute it and/or modify it under the same + license terms as Ruby. +=end + +#MODIFIED +# removed include GetText etc +# added stub translation method _(x) +require 'racc/parser' + +module GetText + + class PoParser < Racc::Parser + + def _(x) + x + end + +module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry', 108 + def unescape(orig) + ret = orig.gsub(/\\n/, "\n") + ret.gsub!(/\\t/, "\t") + ret.gsub!(/\\r/, "\r") + ret.gsub!(/\\"/, "\"") + ret + end + + def parse(str, data, ignore_fuzzy = true) + @comments = [] + @data = data + @fuzzy = false + @msgctxt = "" + $ignore_fuzzy = ignore_fuzzy + + str.strip! + @q = [] + until str.empty? do + case str + when /\A\s+/ + str = $' + when /\Amsgctxt/ + @q.push [:MSGCTXT, $&] + str = $' + when /\Amsgid_plural/ + @q.push [:MSGID_PLURAL, $&] + str = $' + when /\Amsgid/ + @q.push [:MSGID, $&] + str = $' + when /\Amsgstr/ + @q.push [:MSGSTR, $&] + str = $' + when /\A\[(\d+)\]/ + @q.push [:PLURAL_NUM, $1] + str = $' + when /\A\#~(.*)/ + $stderr.print _("Warning: obsolete msgid exists.\n") + $stderr.print " #{$&}\n" + @q.push [:COMMENT, $&] + str = $' + when /\A\#(.*)/ + @q.push [:COMMENT, $&] + str = $' + when /\A\"(.*)\"/ + @q.push [:STRING, $1] + str = $' + else + #c = str[0,1] + #@q.push [:STRING, c] + str = str[1..-1] + end + end + @q.push [false, '$end'] + if $DEBUG + @q.each do |a,b| + puts "[#{a}, #{b}]" + end + end + @yydebug = true if $DEBUG + do_parse + + if @comments.size > 0 + @data.set_comment(:last, @comments.join("\n")) + end + @data + end + + def next_token + @q.shift + end + + def on_message(msgid, msgstr) + if msgstr.size > 0 + @data[msgid] = msgstr + @data.set_comment(msgid, @comments.join("\n")) + end + @comments.clear + @msgctxt = "" + end + + def on_comment(comment) + @fuzzy = true if (/fuzzy/ =~ comment) + @comments << comment + end + + +..end src/poparser.ry modeval..id7a99570e05 + +##### racc 1.4.5 generates ### + +racc_reduce_table = [ + 0, 0, :racc_error, + 0, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 12, :_reduce_5, + 1, 13, :_reduce_none, + 1, 13, :_reduce_none, + 4, 15, :_reduce_8, + 5, 16, :_reduce_9, + 2, 17, :_reduce_10, + 1, 17, :_reduce_none, + 3, 18, :_reduce_12, + 1, 11, :_reduce_13, + 2, 14, :_reduce_14, + 1, 14, :_reduce_15 ] + +racc_reduce_n = 16 + +racc_shift_n = 26 + +racc_action_table = [ + 3, 13, 5, 7, 9, 15, 16, 17, 20, 17, + 13, 17, 13, 13, 11, 17, 23, 20, 13, 17 ] + +racc_action_check = [ + 1, 16, 1, 1, 1, 12, 12, 12, 18, 18, + 7, 14, 15, 9, 3, 19, 20, 21, 23, 25 ] + +racc_action_pointer = [ + nil, 0, nil, 14, nil, nil, nil, 3, nil, 6, + nil, nil, 0, nil, 4, 5, -6, nil, 2, 8, + 8, 11, nil, 11, nil, 12 ] + +racc_action_default = [ + -1, -16, -2, -16, -3, -13, -4, -16, -6, -16, + -7, 26, -16, -15, -5, -16, -16, -14, -16, -8, + -16, -9, -11, -16, -10, -12 ] + +racc_goto_table = [ + 12, 22, 14, 4, 24, 6, 2, 8, 18, 19, + 10, 21, 1, nil, nil, nil, 25 ] + +racc_goto_check = [ + 5, 9, 5, 3, 9, 4, 2, 6, 5, 5, + 7, 8, 1, nil, nil, nil, 5 ] + +racc_goto_pointer = [ + nil, 12, 5, 2, 4, -7, 6, 9, -7, -17 ] + +racc_goto_default = [ + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ] + +racc_token_table = { + false => 0, + Object.new => 1, + :COMMENT => 2, + :MSGID => 3, + :MSGCTXT => 4, + :MSGID_PLURAL => 5, + :MSGSTR => 6, + :STRING => 7, + :PLURAL_NUM => 8 } + +racc_use_result_var = true + +racc_nt_base = 9 + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ +'$end', +'error', +'COMMENT', +'MSGID', +'MSGCTXT', +'MSGID_PLURAL', +'MSGSTR', +'STRING', +'PLURAL_NUM', +'$start', +'msgfmt', +'comment', +'msgctxt', +'message', +'string_list', +'single_message', +'plural_message', +'msgstr_plural', +'msgstr_plural_line'] + +Racc_debug_parser = true + +##### racc system variables end ##### + + # reduce 0 omitted + + # reduce 1 omitted + + # reduce 2 omitted + + # reduce 3 omitted + + # reduce 4 omitted + +module_eval <<'.,.,', 'src/poparser.ry', 25 + def _reduce_5( val, _values, result ) + @msgctxt = unescape(val[1]) + "\004" + result + end +.,., + + # reduce 6 omitted + + # reduce 7 omitted + +module_eval <<'.,.,', 'src/poparser.ry', 48 + def _reduce_8( val, _values, result ) + if @fuzzy and $ignore_fuzzy + if val[1] != "" + $stderr.print _("Warning: fuzzy message was ignored.\n") + $stderr.print " msgid '#{val[1]}'\n" + else + on_message('', unescape(val[3])) + end + @fuzzy = false + else + on_message(@msgctxt + unescape(val[1]), unescape(val[3])) + end + result = "" + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 65 + def _reduce_9( val, _values, result ) + if @fuzzy and $ignore_fuzzy + if val[1] != "" + $stderr.print _("Warning: fuzzy message was ignored.\n") + $stderr.print "msgid = '#{val[1]}\n" + else + on_message('', unescape(val[3])) + end + @fuzzy = false + else + on_message(@msgctxt + unescape(val[1]) + "\000" + unescape(val[3]), unescape(val[4])) + end + result = "" + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 76 + def _reduce_10( val, _values, result ) + if val[0].size > 0 + result = val[0] + "\000" + val[1] + else + result = "" + end + result + end +.,., + + # reduce 11 omitted + +module_eval <<'.,.,', 'src/poparser.ry', 84 + def _reduce_12( val, _values, result ) + result = val[2] + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 91 + def _reduce_13( val, _values, result ) + on_comment(val[0]) + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 99 + def _reduce_14( val, _values, result ) + result = val.delete_if{|item| item == ""}.join + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 103 + def _reduce_15( val, _values, result ) + result = val[0] + result + end +.,., + + def _reduce_none( val, _values, result ) + result + end + + end # class PoParser + +end # module GetText diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/interpolate/ruby.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/interpolate/ruby.rb new file mode 100644 index 0000000..5b50593 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/interpolate/ruby.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# heavily based on Masao Mutoh's gettext String interpolation extension +# http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb + +module I18n + DEFAULT_INTERPOLATION_PATTERNS = [ + /%%/, + /%\{([\w|]+)\}/, # matches placeholders like "%{foo} or %{foo|word}" + /%<(\w+)>([^\d]*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d" + ].freeze + INTERPOLATION_PATTERN = Regexp.union(DEFAULT_INTERPOLATION_PATTERNS) + deprecate_constant :INTERPOLATION_PATTERN + + INTERPOLATION_PATTERNS_CACHE = Hash.new do |hash, patterns| + hash[patterns] = Regexp.union(patterns) + end + private_constant :INTERPOLATION_PATTERNS_CACHE + + class << self + # Return String or raises MissingInterpolationArgument exception. + # Missing argument's logic is handled by I18n.config.missing_interpolation_argument_handler. + def interpolate(string, values) + raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ I18n.reserved_keys_pattern + raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash) + interpolate_hash(string, values) + end + + def interpolate_hash(string, values) + pattern = INTERPOLATION_PATTERNS_CACHE[config.interpolation_patterns] + interpolated = false + + interpolated_string = string.gsub(pattern) do |match| + interpolated = true + + if match == '%%' + '%' + else + key = ($1 || $2 || match.tr("%{}", "")).to_sym + value = if values.key?(key) + values[key] + else + config.missing_interpolation_argument_handler.call(key, values, string) + end + value = value.call(values) if value.respond_to?(:call) + $3 ? sprintf("%#{$3}", value) : value + end + end + + interpolated ? interpolated_string : string + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale.rb new file mode 100644 index 0000000..c4078e6 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module I18n + module Locale + autoload :Fallbacks, 'i18n/locale/fallbacks' + autoload :Tag, 'i18n/locale/tag' + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/fallbacks.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/fallbacks.rb new file mode 100644 index 0000000..56d08d7 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/fallbacks.rb @@ -0,0 +1,107 @@ +# Locale Fallbacks +# +# Extends the I18n module to hold a fallbacks instance which is set to an +# instance of I18n::Locale::Fallbacks by default but can be swapped with a +# different implementation. +# +# Locale fallbacks will compute a number of fallback locales for a given locale. +# For example: +# +# <pre><code> +# I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en] </code></pre> +# +# Locale fallbacks always fall back to +# +# * all parent locales of a given locale (e.g. :es for :"es-MX") first, +# * the current default locales and all of their parents second +# +# The default locales are set to [] by default but can be set to something else. +# +# One can additionally add any number of additional fallback locales manually. +# These will be added before the default locales to the fallback chain. For +# example: +# +# # using a custom locale as default fallback locale +# +# I18n.fallbacks = I18n::Locale::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de) +# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"en-GB", :en] +# I18n.fallbacks[:"de-CH"] # => [:"de-CH", :de, :"en-GB", :en] +# +# # mapping fallbacks to an existing instance +# +# # people speaking Catalan also speak Spanish as spoken in Spain +# fallbacks = I18n.fallbacks +# fallbacks.map(:ca => :"es-ES") +# fallbacks[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en] +# +# # people speaking Arabian as spoken in Palestine also speak Hebrew as spoken in Israel +# fallbacks.map(:"ar-PS" => :"he-IL") +# fallbacks[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en] +# fallbacks[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en] +# +# # people speaking Sami as spoken in Finland also speak Swedish and Finnish as spoken in Finland +# fallbacks.map(:sms => [:"se-FI", :"fi-FI"]) +# fallbacks[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en] + +module I18n + module Locale + class Fallbacks < Hash + def initialize(*mappings) + @map = {} + map(mappings.pop) if mappings.last.is_a?(Hash) + self.defaults = mappings.empty? ? [] : mappings + end + + def defaults=(defaults) + @defaults = defaults.flat_map { |default| compute(default, false) } + end + attr_reader :defaults + + def [](locale) + raise InvalidLocale.new(locale) if locale.nil? + raise Disabled.new('fallback#[]') if locale == false + locale = locale.to_sym + super || store(locale, compute(locale)) + end + + def map(*args, &block) + if args.count == 1 && !block_given? + mappings = args.first + mappings.each do |from, to| + from, to = from.to_sym, Array(to) + to.each do |_to| + @map[from] ||= [] + @map[from] << _to.to_sym + end + end + else + @map.map(*args, &block) + end + end + + def empty? + @map.empty? && @defaults.empty? + end + + def inspect + "#<#{self.class.name} @map=#{@map.inspect} @defaults=#{@defaults.inspect}>" + end + + protected + + def compute(tags, include_defaults = true, exclude = []) + result = [] + Array(tags).each do |tag| + tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym } - exclude + result += tags + tags.each { |_tag| result += compute(@map[_tag], false, exclude + result) if @map[_tag] } + end + + result.push(*defaults) if include_defaults + result.uniq! + result.compact! + result + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag.rb new file mode 100644 index 0000000..a640b44 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag.rb @@ -0,0 +1,28 @@ +# encoding: utf-8 + +module I18n + module Locale + module Tag + autoload :Parents, 'i18n/locale/tag/parents' + autoload :Rfc4646, 'i18n/locale/tag/rfc4646' + autoload :Simple, 'i18n/locale/tag/simple' + + class << self + # Returns the current locale tag implementation. Defaults to +I18n::Locale::Tag::Simple+. + def implementation + @@implementation ||= Simple + end + + # Sets the current locale tag implementation. Use this to set a different locale tag implementation. + def implementation=(implementation) + @@implementation = implementation + end + + # Factory method for locale tags. Delegates to the current locale tag implementation. + def tag(tag) + implementation.tag(tag) + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/parents.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/parents.rb new file mode 100644 index 0000000..6283e66 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/parents.rb @@ -0,0 +1,24 @@ +module I18n + module Locale + module Tag + module Parents + def parent + @parent ||= + begin + segs = to_a + segs.compact! + segs.length > 1 ? self.class.tag(*segs[0..(segs.length - 2)].join('-')) : nil + end + end + + def self_and_parents + @self_and_parents ||= [self].concat parents + end + + def parents + @parents ||= parent ? [parent].concat(parent.parents) : [] + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/rfc4646.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/rfc4646.rb new file mode 100644 index 0000000..4ce4c75 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/rfc4646.rb @@ -0,0 +1,74 @@ +# RFC 4646/47 compliant Locale tag implementation that parses locale tags to +# subtags such as language, script, region, variant etc. +# +# For more information see by http://en.wikipedia.org/wiki/IETF_language_tag +# +# Rfc4646::Parser does not implement grandfathered tags. + +module I18n + module Locale + module Tag + RFC4646_SUBTAGS = [ :language, :script, :region, :variant, :extension, :privateuse, :grandfathered ] + RFC4646_FORMATS = { :language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase } + + class Rfc4646 < Struct.new(*RFC4646_SUBTAGS) + class << self + # Parses the given tag and returns a Tag instance if it is valid. + # Returns false if the given tag is not valid according to RFC 4646. + def tag(tag) + matches = parser.match(tag) + new(*matches) if matches + end + + def parser + @@parser ||= Rfc4646::Parser + end + + def parser=(parser) + @@parser = parser + end + end + + include Parents + + RFC4646_FORMATS.each do |name, format| + define_method(name) { self[name].send(format) unless self[name].nil? } + end + + def to_sym + to_s.to_sym + end + + def to_s + @tag ||= to_a.compact.join("-") + end + + def to_a + members.collect { |attr| self.send(attr) } + end + + module Parser + PATTERN = %r{\A(?: + ([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language + (?:-([a-z]{4}))? # script + (?:-([a-z]{2}|\d{3}))? # region + (?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant + (?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension + (?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag + (x(?:-[0-9a-z]{1,8})+)| # privateuse tag + /* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered + )\z}xi + + class << self + def match(tag) + c = PATTERN.match(tag.to_s).captures + c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here? + rescue + false + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/simple.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/simple.rb new file mode 100644 index 0000000..18d55c2 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/locale/tag/simple.rb @@ -0,0 +1,39 @@ +# Simple Locale tag implementation that computes subtags by simply splitting +# the locale tag at '-' occurrences. +module I18n + module Locale + module Tag + class Simple + class << self + def tag(tag) + new(tag) + end + end + + include Parents + + attr_reader :tag + + def initialize(*tag) + @tag = tag.join('-').to_sym + end + + def subtags + @subtags = tag.to_s.split('-').map!(&:to_s) + end + + def to_sym + tag + end + + def to_s + tag.to_s + end + + def to_a + subtags + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/middleware.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/middleware.rb new file mode 100644 index 0000000..8999834 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/middleware.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module I18n + class Middleware + + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + ensure + if Fiber.respond_to?(:[]) + Fiber[:i18n_config] = I18n::Config.new + else + Thread.current.thread_variable_set(:i18n_config, I18n::Config.new) + end + end + + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests.rb new file mode 100644 index 0000000..30d0ed5 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module I18n + module Tests + autoload :Basics, 'i18n/tests/basics' + autoload :Defaults, 'i18n/tests/defaults' + autoload :Interpolation, 'i18n/tests/interpolation' + autoload :Link, 'i18n/tests/link' + autoload :Localization, 'i18n/tests/localization' + autoload :Lookup, 'i18n/tests/lookup' + autoload :Pluralization, 'i18n/tests/pluralization' + autoload :Procs, 'i18n/tests/procs' + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/basics.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/basics.rb new file mode 100644 index 0000000..833762b --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/basics.rb @@ -0,0 +1,58 @@ +module I18n + module Tests + module Basics + def teardown + I18n.available_locales = nil + end + + test "available_locales returns the available_locales produced by the backend, by default" do + I18n.backend.store_translations('de', :foo => 'bar') + I18n.backend.store_translations('en', :foo => 'foo') + + assert_equal I18n.available_locales, I18n.backend.available_locales + end + + test "available_locales can be set to something else independently from the actual locale data" do + I18n.backend.store_translations('de', :foo => 'bar') + I18n.backend.store_translations('en', :foo => 'foo') + + I18n.available_locales = :foo + assert_equal [:foo], I18n.available_locales + + I18n.available_locales = [:foo, 'bar'] + assert_equal [:foo, :bar], I18n.available_locales + + I18n.available_locales = nil + assert_equal I18n.available_locales, I18n.backend.available_locales + end + + test "available_locales memoizes when set explicitly" do + I18n.backend.expects(:available_locales).never + I18n.available_locales = [:foo] + I18n.backend.store_translations('de', :bar => 'baz') + I18n.reload! + assert_equal [:foo], I18n.available_locales + end + + test "available_locales delegates to the backend when not set explicitly" do + original_available_locales_value = I18n.backend.available_locales + I18n.backend.expects(:available_locales).returns(original_available_locales_value).twice + assert_equal I18n.backend.available_locales, I18n.available_locales + end + + test "exists? is implemented by the backend" do + I18n.backend.store_translations(:foo, :bar => 'baz') + assert I18n.exists?(:bar, :foo) + end + + test "storing a nil value as a translation removes it from the available locale data" do + I18n.backend.store_translations(:en, :to_be_deleted => 'bar') + assert_equal 'bar', I18n.t(:to_be_deleted, :default => 'baz') + + I18n.cache_store.clear if I18n.respond_to?(:cache_store) && I18n.cache_store + I18n.backend.store_translations(:en, :to_be_deleted => nil) + assert_equal 'baz', I18n.t(:to_be_deleted, :default => 'baz') + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/defaults.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/defaults.rb new file mode 100644 index 0000000..e5db336 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/defaults.rb @@ -0,0 +1,59 @@ +# encoding: utf-8 + +module I18n + module Tests + module Defaults + def setup + super + I18n.backend.store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' }) + end + + test "defaults: given nil as a key it returns the given default" do + assert_equal 'default', I18n.t(nil, :default => 'default') + end + + test "defaults: given a symbol as a default it translates the symbol" do + assert_equal 'bar', I18n.t(nil, :default => :'foo.bar') + end + + test "defaults: given a symbol as a default and a scope it stays inside the scope when looking up the symbol" do + assert_equal 'bar', I18n.t(:missing, :default => :bar, :scope => :foo) + end + + test "defaults: given an array as a default it returns the first match" do + assert_equal 'bar', I18n.t(:does_not_exist, :default => [:does_not_exist_2, :'foo.bar']) + end + + test "defaults: given an array as a default with false it returns false" do + assert_equal false, I18n.t(:does_not_exist, :default => [false]) + end + + test "defaults: given false it returns false" do + assert_equal false, I18n.t(:does_not_exist, :default => false) + end + + test "defaults: given nil it returns nil" do + assert_nil I18n.t(:does_not_exist, :default => nil) + end + + test "defaults: given an array of missing keys it raises a MissingTranslationData exception" do + assert_raises I18n::MissingTranslationData do + I18n.t(:does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3], :raise => true) + end + end + + test "defaults: using a custom scope separator" do + # data must have been stored using the custom separator when using the ActiveRecord backend + I18n.backend.store_translations(:en, { :foo => { :bar => 'bar' } }, { :separator => '|' }) + assert_equal 'bar', I18n.t(nil, :default => :'foo|bar', :separator => '|') + end + + # Addresses issue: #599 + test "defaults: only interpolates once when resolving defaults" do + I18n.backend.store_translations(:en, :greeting => 'hey %{name}') + assert_equal 'hey %{dont_interpolate_me}', + I18n.t(:does_not_exist, :name => '%{dont_interpolate_me}', default: [:greeting]) + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/interpolation.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/interpolation.rb new file mode 100644 index 0000000..991c4ec --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/interpolation.rb @@ -0,0 +1,185 @@ +# encoding: utf-8 + +module I18n + module Tests + module Interpolation + # If no interpolation parameter is not given, I18n should not alter the string. + # This behavior is due to three reasons: + # + # * Checking interpolation keys in all strings hits performance, badly; + # + # * This allows us to retrieve untouched values through I18n. For example + # I could have a middleware that returns I18n lookup results in JSON + # to be processed through Javascript. Leaving the keys untouched allows + # the interpolation to happen at the javascript level; + # + # * Security concerns: if I allow users to translate a web site, they can + # insert %{} in messages causing the I18n lookup to fail in every request. + # + test "interpolation: given no values it does not alter the string" do + assert_equal 'Hi %{name}!', interpolate(:default => 'Hi %{name}!') + end + + test "interpolation: given values it interpolates them into the string" do + assert_equal 'Hi David!', interpolate(:default => 'Hi %{name}!', :name => 'David') + end + + test "interpolation: works with a pipe" do + assert_equal 'Hi david!', interpolate(:default => 'Hi %{name|lowercase}!', :'name|lowercase' => 'david') + end + + test "interpolation: given a nil value it still interpolates it into the string" do + assert_equal 'Hi !', interpolate(:default => 'Hi %{name}!', :name => nil) + end + + test "interpolation: given a lambda as a value it calls it if the string contains the key" do + assert_equal 'Hi David!', interpolate(:default => 'Hi %{name}!', :name => lambda { |*args| 'David' }) + end + + test "interpolation: given a lambda as a value it does not call it if the string does not contain the key" do + assert_nothing_raised { interpolate(:default => 'Hi!', :name => lambda { |*args| raise 'fail' }) } + end + + test "interpolation: given values but missing a key it raises I18n::MissingInterpolationArgument" do + assert_raises(I18n::MissingInterpolationArgument) do + interpolate(:default => '%{foo}', :bar => 'bar') + end + end + + test "interpolation: it does not raise I18n::MissingInterpolationArgument for escaped variables" do + assert_nothing_raised do + assert_equal 'Barr %{foo}', interpolate(:default => '%{bar} %%{foo}', :bar => 'Barr') + end + end + + test "interpolation: it does not change the original, stored translation string" do + I18n.backend.store_translations(:en, :interpolate => 'Hi %{name}!') + assert_equal 'Hi David!', interpolate(:interpolate, :name => 'David') + assert_equal 'Hi Yehuda!', interpolate(:interpolate, :name => 'Yehuda') + end + + test "interpolation: given an array interpolates each element" do + I18n.backend.store_translations(:en, :array_interpolate => ['Hi', 'Mr. %{name}', 'or sir %{name}']) + assert_equal ['Hi', 'Mr. Bartuz', 'or sir Bartuz'], interpolate(:array_interpolate, :name => 'Bartuz') + end + + test "interpolation: given the translation is in utf-8 it still works" do + assert_equal 'Häi David!', interpolate(:default => 'Häi %{name}!', :name => 'David') + end + + test "interpolation: given the value is in utf-8 it still works" do + assert_equal 'Hi ゆきひろ!', interpolate(:default => 'Hi %{name}!', :name => 'ゆきひろ') + end + + test "interpolation: given the translation and the value are in utf-8 it still works" do + assert_equal 'こんにちは、ゆきひろさん!', interpolate(:default => 'こんにちは、%{name}さん!', :name => 'ゆきひろ') + end + + if Object.const_defined?(:Encoding) + test "interpolation: given a euc-jp translation and a utf-8 value it raises Encoding::CompatibilityError" do + assert_raises(Encoding::CompatibilityError) do + interpolate(:default => euc_jp('こんにちは、%{name}さん!'), :name => 'ゆきひろ') + end + end + + test "interpolation: given a utf-8 translation and a euc-jp value it raises Encoding::CompatibilityError" do + assert_raises(Encoding::CompatibilityError) do + interpolate(:default => 'こんにちは、%{name}さん!', :name => euc_jp('ゆきひろ')) + end + end + + test "interpolation: ASCII strings in the backend should be encoded to UTF8 if interpolation options are in UTF8" do + I18n.backend.store_translations 'en', 'encoding' => ('%{who} let me go'.dup.force_encoding(Encoding::US_ASCII)) + result = I18n.t 'encoding', :who => "måmmå miå" + assert_equal Encoding::UTF_8, result.encoding + end + + test "interpolation: UTF8 strings in the backend are still returned as UTF8 with ASCII interpolation" do + I18n.backend.store_translations 'en', 'encoding' => 'måmmå miå %{what}' + result = I18n.t 'encoding', :what => 'let me go'.dup.force_encoding(Encoding::US_ASCII) + assert_equal Encoding::UTF_8, result.encoding + end + + test "interpolation: UTF8 strings in the backend are still returned as UTF8 even with numbers interpolation" do + I18n.backend.store_translations 'en', 'encoding' => '%{count} times: måmmå miå' + result = I18n.t 'encoding', :count => 3 + assert_equal Encoding::UTF_8, result.encoding + end + end + + test "interpolation: given a translations containing a reserved key it raises I18n::ReservedInterpolationKey" do + assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{exception_handler}') } + assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{default}') } + assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{separator}') } + assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{scope}') } + assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '%{scope}') } + + I18n.backend.store_translations(:en, :interpolate => 'Hi %{scope}!') + assert_raises(I18n::ReservedInterpolationKey) { interpolate(:interpolate) } + end + + test "interpolation: it does not raise I18n::ReservedInterpolationKey for escaped variables" do + assert_nothing_raised do + assert_equal '%{separator}', interpolate(:foo => :bar, :default => '%%{separator}') + end + + # Note: The two interpolations below do not remove the escape character (%) because + # I18n should not alter the strings when no interpolation parameters are given, + # see the comment at the top of this file. + assert_nothing_raised do + assert_equal '%%{scope}', interpolate(:default => '%%{scope}') + end + + I18n.backend.store_translations(:en, :interpolate => 'Hi %%{scope}!') + assert_nothing_raised do + assert_equal 'Hi %%{scope}!', interpolate(:interpolate) + end + end + + test "interpolation: deep interpolation for default string" do + assert_equal 'Hi %{name}!', interpolate(:default => 'Hi %{name}!', :deep_interpolation => true) + end + + test "interpolation: deep interpolation for interpolated string" do + assert_equal 'Hi Ann!', interpolate(:default => 'Hi %{name}!', :name => 'Ann', :deep_interpolation => true) + end + + test "interpolation: deep interpolation for Hash" do + people = { :people => { :ann => 'Ann is %{ann}', :john => 'John is %{john}' } } + interpolated_people = { :people => { :ann => 'Ann is good', :john => 'John is big' } } + assert_equal interpolated_people, interpolate(:default => people, :ann => 'good', :john => 'big', :deep_interpolation => true) + end + + test "interpolation: deep interpolation for Array" do + people = { :people => ['Ann is %{ann}', 'John is %{john}'] } + interpolated_people = { :people => ['Ann is good', 'John is big'] } + assert_equal interpolated_people, interpolate(:default => people, :ann => 'good', :john => 'big', :deep_interpolation => true) + end + + protected + + def capture(stream) + begin + stream = stream.to_s + eval "$#{stream} = StringIO.new" + yield + result = eval("$#{stream}").string + ensure + eval("$#{stream} = #{stream.upcase}") + end + + result + end + + def euc_jp(string) + string.encode(Encoding::EUC_JP) + end + + def interpolate(*args) + options = args.last.is_a?(Hash) ? args.pop : {} + key = args.pop + I18n.backend.translate('en', key, options) + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/link.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/link.rb new file mode 100644 index 0000000..d2f20e8 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/link.rb @@ -0,0 +1,66 @@ +# encoding: utf-8 + +module I18n + module Tests + module Link + test "linked lookup: if a key resolves to a symbol it looks up the symbol" do + I18n.backend.store_translations 'en', { + :link => :linked, + :linked => 'linked' + } + assert_equal 'linked', I18n.backend.translate('en', :link) + end + + test "linked lookup: if a key resolves to a dot-separated symbol it looks up the symbol" do + I18n.backend.store_translations 'en', { + :link => :"foo.linked", + :foo => { :linked => 'linked' } + } + assert_equal('linked', I18n.backend.translate('en', :link)) + end + + test "linked lookup: if a dot-separated key resolves to a symbol it looks up the symbol" do + I18n.backend.store_translations 'en', { + :foo => { :link => :linked }, + :linked => 'linked' + } + assert_equal('linked', I18n.backend.translate('en', :'foo.link')) + end + + test "linked lookup: if a dot-separated key resolves to a dot-separated symbol it looks up the symbol" do + I18n.backend.store_translations 'en', { + :foo => { :link => :"bar.linked" }, + :bar => { :linked => 'linked' } + } + assert_equal('linked', I18n.backend.translate('en', :'foo.link')) + end + + test "linked lookup: links always refer to the absolute key" do + I18n.backend.store_translations 'en', { + :foo => { :link => :linked, :linked => 'linked in foo' }, + :linked => 'linked absolutely' + } + assert_equal 'linked absolutely', I18n.backend.translate('en', :link, :scope => :foo) + end + + test "linked lookup: a link can resolve to a namespace in the middle of a dot-separated key" do + I18n.backend.store_translations 'en', { + :activemodel => { :errors => { :messages => { :blank => "can't be blank" } } }, + :activerecord => { :errors => { :messages => :"activemodel.errors.messages" } } + } + assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank") + assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank") + end + + test "linked lookup: a link can resolve with option :count" do + I18n.backend.store_translations 'en', { + :counter => :counted, + :counted => { :foo => { :one => "one", :other => "other" }, :bar => "bar" } + } + assert_equal "one", I18n.t(:'counter.foo', count: 1) + assert_equal "other", I18n.t(:'counter.foo', count: 2) + assert_equal "bar", I18n.t(:'counter.bar', count: 3) + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization.rb new file mode 100644 index 0000000..53b1502 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization.rb @@ -0,0 +1,19 @@ +module I18n + module Tests + module Localization + autoload :Date, 'i18n/tests/localization/date' + autoload :DateTime, 'i18n/tests/localization/date_time' + autoload :Time, 'i18n/tests/localization/time' + autoload :Procs, 'i18n/tests/localization/procs' + + def self.included(base) + base.class_eval do + include I18n::Tests::Localization::Date + include I18n::Tests::Localization::DateTime + include I18n::Tests::Localization::Procs + include I18n::Tests::Localization::Time + end + end + end + end +end
\ No newline at end of file diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/date.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/date.rb new file mode 100644 index 0000000..c21fbbf --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/date.rb @@ -0,0 +1,122 @@ +# encoding: utf-8 + +module I18n + module Tests + module Localization + module Date + def setup + super + setup_date_translations + @date = ::Date.new(2008, 3, 1) + end + + test "localize Date: given the short format it uses it" do + assert_equal '01. Mär', I18n.l(@date, :format => :short, :locale => :de) + end + + test "localize Date: given the long format it uses it" do + assert_equal '01. März 2008', I18n.l(@date, :format => :long, :locale => :de) + end + + test "localize Date: given the default format it uses it" do + assert_equal '01.03.2008', I18n.l(@date, :format => :default, :locale => :de) + end + + test "localize Date: given a day name format it returns the correct day name" do + assert_equal 'Samstag', I18n.l(@date, :format => '%A', :locale => :de) + end + + test "localize Date: given a uppercased day name format it returns the correct day name in upcase" do + assert_equal 'samstag'.upcase, I18n.l(@date, :format => '%^A', :locale => :de) + end + + test "localize Date: given an abbreviated day name format it returns the correct abbreviated day name" do + assert_equal 'Sa', I18n.l(@date, :format => '%a', :locale => :de) + end + + test "localize Date: given an meridian indicator format it returns the correct meridian indicator" do + assert_equal 'AM', I18n.l(@date, :format => '%p', :locale => :de) + assert_equal 'am', I18n.l(@date, :format => '%P', :locale => :de) + end + + test "localize Date: given an abbreviated and uppercased day name format it returns the correct abbreviated day name in upcase" do + assert_equal 'sa'.upcase, I18n.l(@date, :format => '%^a', :locale => :de) + end + + test "localize Date: given a month name format it returns the correct month name" do + assert_equal 'März', I18n.l(@date, :format => '%B', :locale => :de) + end + + test "localize Date: given a uppercased month name format it returns the correct month name in upcase" do + assert_equal 'märz'.upcase, I18n.l(@date, :format => '%^B', :locale => :de) + end + + test "localize Date: given an abbreviated month name format it returns the correct abbreviated month name" do + assert_equal 'Mär', I18n.l(@date, :format => '%b', :locale => :de) + end + + test "localize Date: given an abbreviated and uppercased month name format it returns the correct abbreviated month name in upcase" do + assert_equal 'mär'.upcase, I18n.l(@date, :format => '%^b', :locale => :de) + end + + test "localize Date: given a date format with the month name upcased it returns the correct value" do + assert_equal '1. FEBRUAR 2008', I18n.l(::Date.new(2008, 2, 1), :format => "%-d. %^B %Y", :locale => :de) + end + + test "localize Date: given missing translations it returns the correct error message" do + assert_equal 'Translation missing: fr.date.abbr_month_names', I18n.l(@date, :format => '%b', :locale => :fr) + end + + test "localize Date: given an unknown format it does not fail" do + assert_nothing_raised { I18n.l(@date, :format => '%x') } + end + + test "localize Date: does not modify the options hash" do + options = { :format => '%b', :locale => :de } + assert_equal 'Mär', I18n.l(@date, **options) + assert_equal({ :format => '%b', :locale => :de }, options) + assert_nothing_raised { I18n.l(@date, **options.freeze) } + end + + test "localize Date: given nil with default value it returns default" do + assert_equal 'default', I18n.l(nil, :default => 'default') + end + + test "localize Date: given nil it raises I18n::ArgumentError" do + assert_raises(I18n::ArgumentError) { I18n.l(nil) } + end + + test "localize Date: given a plain Object it raises I18n::ArgumentError" do + assert_raises(I18n::ArgumentError) { I18n.l(Object.new) } + end + + test "localize Date: given a format is missing it raises I18n::MissingTranslationData" do + assert_raises(I18n::MissingTranslationData) { I18n.l(@date, :format => :missing) } + end + + test "localize Date: it does not alter the format string" do + assert_equal '01. Februar 2009', I18n.l(::Date.parse('2009-02-01'), :format => :long, :locale => :de) + assert_equal '01. Oktober 2009', I18n.l(::Date.parse('2009-10-01'), :format => :long, :locale => :de) + end + + protected + + def setup_date_translations + I18n.backend.store_translations :de, { + :date => { + :formats => { + :default => "%d.%m.%Y", + :short => "%d. %b", + :long => "%d. %B %Y", + }, + :day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag), + :abbr_day_names => %w(So Mo Di Mi Do Fr Sa), + :month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil), + :abbr_month_names => %w(Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil) + } + } + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/date_time.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/date_time.rb new file mode 100644 index 0000000..b5d3527 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/date_time.rb @@ -0,0 +1,103 @@ +# encoding: utf-8 + +module I18n + module Tests + module Localization + module DateTime + def setup + super + setup_datetime_translations + @datetime = ::DateTime.new(2008, 3, 1, 6) + @other_datetime = ::DateTime.new(2008, 3, 1, 18) + end + + test "localize DateTime: given the short format it uses it" do + assert_equal '01. Mär 06:00', I18n.l(@datetime, :format => :short, :locale => :de) + end + + test "localize DateTime: given the long format it uses it" do + assert_equal '01. März 2008 06:00', I18n.l(@datetime, :format => :long, :locale => :de) + end + + test "localize DateTime: given the default format it uses it" do + assert_equal 'Sa, 01. Mär 2008 06:00:00 +0000', I18n.l(@datetime, :format => :default, :locale => :de) + end + + test "localize DateTime: given a day name format it returns the correct day name" do + assert_equal 'Samstag', I18n.l(@datetime, :format => '%A', :locale => :de) + end + + test "localize DateTime: given a uppercased day name format it returns the correct day name in upcase" do + assert_equal 'samstag'.upcase, I18n.l(@datetime, :format => '%^A', :locale => :de) + end + + test "localize DateTime: given an abbreviated day name format it returns the correct abbreviated day name" do + assert_equal 'Sa', I18n.l(@datetime, :format => '%a', :locale => :de) + end + + test "localize DateTime: given an abbreviated and uppercased day name format it returns the correct abbreviated day name in upcase" do + assert_equal 'sa'.upcase, I18n.l(@datetime, :format => '%^a', :locale => :de) + end + + test "localize DateTime: given a month name format it returns the correct month name" do + assert_equal 'März', I18n.l(@datetime, :format => '%B', :locale => :de) + end + + test "localize DateTime: given a uppercased month name format it returns the correct month name in upcase" do + assert_equal 'märz'.upcase, I18n.l(@datetime, :format => '%^B', :locale => :de) + end + + test "localize DateTime: given an abbreviated month name format it returns the correct abbreviated month name" do + assert_equal 'Mär', I18n.l(@datetime, :format => '%b', :locale => :de) + end + + test "localize DateTime: given an abbreviated and uppercased month name format it returns the correct abbreviated month name in upcase" do + assert_equal 'mär'.upcase, I18n.l(@datetime, :format => '%^b', :locale => :de) + end + + test "localize DateTime: given a date format with the month name upcased it returns the correct value" do + assert_equal '1. FEBRUAR 2008', I18n.l(::DateTime.new(2008, 2, 1, 6), :format => "%-d. %^B %Y", :locale => :de) + end + + test "localize DateTime: given missing translations it returns the correct error message" do + assert_equal 'Translation missing: fr.date.abbr_month_names', I18n.l(@datetime, :format => '%b', :locale => :fr) + end + + test "localize DateTime: given a meridian indicator format it returns the correct meridian indicator" do + assert_equal 'AM', I18n.l(@datetime, :format => '%p', :locale => :de) + assert_equal 'PM', I18n.l(@other_datetime, :format => '%p', :locale => :de) + end + + test "localize DateTime: given a meridian indicator format it returns the correct meridian indicator in downcase" do + assert_equal 'am', I18n.l(@datetime, :format => '%P', :locale => :de) + assert_equal 'pm', I18n.l(@other_datetime, :format => '%P', :locale => :de) + end + + test "localize DateTime: given an unknown format it does not fail" do + assert_nothing_raised { I18n.l(@datetime, :format => '%x') } + end + + test "localize DateTime: given a format is missing it raises I18n::MissingTranslationData" do + assert_raises(I18n::MissingTranslationData) { I18n.l(@datetime, :format => :missing) } + end + + protected + + def setup_datetime_translations + # time translations might have been set up in Tests::Api::Localization::Time + I18n.backend.store_translations :de, { + :time => { + :formats => { + :default => "%a, %d. %b %Y %H:%M:%S %z", + :short => "%d. %b %H:%M", + :long => "%d. %B %Y %H:%M" + }, + :am => 'am', + :pm => 'pm' + } + } + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/procs.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/procs.rb new file mode 100644 index 0000000..2c5d8e1 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/procs.rb @@ -0,0 +1,118 @@ +# encoding: utf-8 + +module I18n + module Tests + module Localization + module Procs + test "localize: using day names from lambdas" do + setup_time_proc_translations + time = ::Time.utc(2008, 3, 1, 6, 0) + assert_match(/Суббота/, I18n.l(time, :format => "%A, %d %B", :locale => :ru)) + assert_match(/суббота/, I18n.l(time, :format => "%d %B (%A)", :locale => :ru)) + end + + test "localize: using month names from lambdas" do + setup_time_proc_translations + time = ::Time.utc(2008, 3, 1, 6, 0) + assert_match(/марта/, I18n.l(time, :format => "%d %B %Y", :locale => :ru)) + assert_match(/Март /, I18n.l(time, :format => "%B %Y", :locale => :ru)) + end + + test "localize: using abbreviated day names from lambdas" do + setup_time_proc_translations + time = ::Time.utc(2008, 3, 1, 6, 0) + assert_match(/марта/, I18n.l(time, :format => "%d %b %Y", :locale => :ru)) + assert_match(/март /, I18n.l(time, :format => "%b %Y", :locale => :ru)) + end + + test "localize Date: given a format that resolves to a Proc it calls the Proc with the object" do + setup_time_proc_translations + date = ::Date.new(2008, 3, 1) + assert_equal '[Sat, 01 Mar 2008, {}]', I18n.l(date, :format => :proc, :locale => :ru) + end + + test "localize Date: given a format that resolves to a Proc it calls the Proc with the object and extra options" do + setup_time_proc_translations + date = ::Date.new(2008, 3, 1) + assert_equal %|[Sat, 01 Mar 2008, #{{:foo=>"foo"}}]|, I18n.l(date, :format => :proc, :foo => 'foo', :locale => :ru) + end + + test "localize DateTime: given a format that resolves to a Proc it calls the Proc with the object" do + setup_time_proc_translations + datetime = ::DateTime.new(2008, 3, 1, 6) + assert_equal '[Sat, 01 Mar 2008 06:00:00 +00:00, {}]', I18n.l(datetime, :format => :proc, :locale => :ru) + end + + test "localize DateTime: given a format that resolves to a Proc it calls the Proc with the object and extra options" do + setup_time_proc_translations + datetime = ::DateTime.new(2008, 3, 1, 6) + assert_equal %|[Sat, 01 Mar 2008 06:00:00 +00:00, #{{:foo=>"foo"}}]|, I18n.l(datetime, :format => :proc, :foo => 'foo', :locale => :ru) + end + + test "localize Time: given a format that resolves to a Proc it calls the Proc with the object" do + setup_time_proc_translations + time = ::Time.utc(2008, 3, 1, 6, 0) + assert_equal I18n::Tests::Localization::Procs.inspect_args([time], {}), I18n.l(time, :format => :proc, :locale => :ru) + end + + test "localize Time: given a format that resolves to a Proc it calls the Proc with the object and extra options" do + setup_time_proc_translations + time = ::Time.utc(2008, 3, 1, 6, 0) + options = { :foo => 'foo' } + assert_equal I18n::Tests::Localization::Procs.inspect_args([time], options), I18n.l(time, **options.merge(:format => :proc, :locale => :ru)) + end + + protected + + def self.inspect_args(args, kwargs) + args << kwargs + args = args.map do |arg| + case arg + when ::Time, ::DateTime + arg.strftime('%a, %d %b %Y %H:%M:%S %Z').sub('+0000', '+00:00') + when ::Date + arg.strftime('%a, %d %b %Y') + when Hash + arg.delete(:fallback_in_progress) + arg.delete(:fallback_original_locale) + arg.inspect + else + arg.inspect + end + end + "[#{args.join(', ')}]" + end + + def setup_time_proc_translations + I18n.backend.store_translations :ru, { + :time => { + :formats => { + :proc => lambda { |*args, **kwargs| I18n::Tests::Localization::Procs.inspect_args(args, kwargs) } + } + }, + :date => { + :formats => { + :proc => lambda { |*args, **kwargs| I18n::Tests::Localization::Procs.inspect_args(args, kwargs) } + }, + :'day_names' => lambda { |key, options| + (options[:format] =~ /^%A/) ? + %w(Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота) : + %w(воскресенье понедельник вторник среда четверг пятница суббота) + }, + :'month_names' => lambda { |key, options| + (options[:format] =~ /(%d|%e)(\s*)?(%B)/) ? + %w(января февраля марта апреля мая июня июля августа сентября октября ноября декабря).unshift(nil) : + %w(Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь).unshift(nil) + }, + :'abbr_month_names' => lambda { |key, options| + (options[:format] =~ /(%d|%e)(\s*)(%b)/) ? + %w(янв. февр. марта апр. мая июня июля авг. сент. окт. нояб. дек.).unshift(nil) : + %w(янв. февр. март апр. май июнь июль авг. сент. окт. нояб. дек.).unshift(nil) + }, + } + } + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/time.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/time.rb new file mode 100644 index 0000000..456a760 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/localization/time.rb @@ -0,0 +1,103 @@ +# encoding: utf-8 + +module I18n + module Tests + module Localization + module Time + def setup + super + setup_time_translations + @time = ::Time.utc(2008, 3, 1, 6, 0) + @other_time = ::Time.utc(2008, 3, 1, 18, 0) + end + + test "localize Time: given the short format it uses it" do + assert_equal '01. Mär 06:00', I18n.l(@time, :format => :short, :locale => :de) + end + + test "localize Time: given the long format it uses it" do + assert_equal '01. März 2008 06:00', I18n.l(@time, :format => :long, :locale => :de) + end + + # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this? + # def test_localize_given_the_default_format_it_uses_it + # assert_equal 'Sa, 01. Mar 2008 06:00:00 +0000', I18n.l(@time, :format => :default, :locale => :de) + # end + + test "localize Time: given a day name format it returns the correct day name" do + assert_equal 'Samstag', I18n.l(@time, :format => '%A', :locale => :de) + end + + test "localize Time: given a uppercased day name format it returns the correct day name in upcase" do + assert_equal 'samstag'.upcase, I18n.l(@time, :format => '%^A', :locale => :de) + end + + test "localize Time: given an abbreviated day name format it returns the correct abbreviated day name" do + assert_equal 'Sa', I18n.l(@time, :format => '%a', :locale => :de) + end + + test "localize Time: given an abbreviated and uppercased day name format it returns the correct abbreviated day name in upcase" do + assert_equal 'sa'.upcase, I18n.l(@time, :format => '%^a', :locale => :de) + end + + test "localize Time: given a month name format it returns the correct month name" do + assert_equal 'März', I18n.l(@time, :format => '%B', :locale => :de) + end + + test "localize Time: given a uppercased month name format it returns the correct month name in upcase" do + assert_equal 'märz'.upcase, I18n.l(@time, :format => '%^B', :locale => :de) + end + + test "localize Time: given an abbreviated month name format it returns the correct abbreviated month name" do + assert_equal 'Mär', I18n.l(@time, :format => '%b', :locale => :de) + end + + test "localize Time: given an abbreviated and uppercased month name format it returns the correct abbreviated month name in upcase" do + assert_equal 'mär'.upcase, I18n.l(@time, :format => '%^b', :locale => :de) + end + + test "localize Time: given a date format with the month name upcased it returns the correct value" do + assert_equal '1. FEBRUAR 2008', I18n.l(::Time.utc(2008, 2, 1, 6, 0), :format => "%-d. %^B %Y", :locale => :de) + end + + test "localize Time: given missing translations it returns the correct error message" do + assert_equal 'Translation missing: fr.date.abbr_month_names', I18n.l(@time, :format => '%b', :locale => :fr) + end + + test "localize Time: given a meridian indicator format it returns the correct meridian indicator" do + assert_equal 'AM', I18n.l(@time, :format => '%p', :locale => :de) + assert_equal 'PM', I18n.l(@other_time, :format => '%p', :locale => :de) + end + + test "localize Time: given a meridian indicator format it returns the correct meridian indicator in upcase" do + assert_equal 'am', I18n.l(@time, :format => '%P', :locale => :de) + assert_equal 'pm', I18n.l(@other_time, :format => '%P', :locale => :de) + end + + test "localize Time: given an unknown format it does not fail" do + assert_nothing_raised { I18n.l(@time, :format => '%x') } + end + + test "localize Time: given a format is missing it raises I18n::MissingTranslationData" do + assert_raises(I18n::MissingTranslationData) { I18n.l(@time, :format => :missing) } + end + + protected + + def setup_time_translations + I18n.backend.store_translations :de, { + :time => { + :formats => { + :default => "%a, %d. %b %Y %H:%M:%S %z", + :short => "%d. %b %H:%M", + :long => "%d. %B %Y %H:%M", + }, + :am => 'am', + :pm => 'pm' + } + } + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/lookup.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/lookup.rb new file mode 100644 index 0000000..f1bee79 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/lookup.rb @@ -0,0 +1,87 @@ +# encoding: utf-8 + +module I18n + module Tests + module Lookup + def setup + super + I18n.backend.store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' }, :falsy => false, :truthy => true, + :string => "a", :array => %w(a b c), :hash => { "a" => "b" }) + end + + test "lookup: it returns a string" do + assert_equal("a", I18n.t(:string)) + end + + test "lookup: it returns hash" do + assert_equal({ :a => "b" }, I18n.t(:hash)) + end + + test "lookup: it returns an array" do + assert_equal(%w(a b c), I18n.t(:array)) + end + + test "lookup: it returns a native true" do + assert I18n.t(:truthy) === true + end + + test "lookup: it returns a native false" do + assert I18n.t(:falsy) === false + end + + test "lookup: given a missing key, no default and no raise option it returns an error message" do + assert_equal "Translation missing: en.missing", I18n.t(:missing) + end + + test "lookup: given a missing key, no default and the raise option it raises MissingTranslationData" do + assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } + end + + test "lookup: does not raise an exception if no translation data is present for the given locale" do + assert_nothing_raised { I18n.t(:foo, :locale => :xx) } + end + + test "lookup: does not modify the options hash" do + options = {} + assert_equal "a", I18n.t(:string, **options) + assert_equal({}, options) + assert_nothing_raised { I18n.t(:string, **options.freeze) } + end + + test "lookup: given an array of keys it translates all of them" do + assert_equal %w(bar baz), I18n.t([:bar, :baz], :scope => [:foo]) + end + + test "lookup: using a custom scope separator" do + # data must have been stored using the custom separator when using the ActiveRecord backend + I18n.backend.store_translations(:en, { :foo => { :bar => 'bar' } }, { :separator => '|' }) + assert_equal 'bar', I18n.t('foo|bar', :separator => '|') + end + + # In fact it probably *should* fail but Rails currently relies on using the default locale instead. + # So we'll stick to this for now until we get it fixed in Rails. + test "lookup: given nil as a locale it does not raise but use the default locale" do + # assert_raises(I18n::InvalidLocale) { I18n.t(:bar, :locale => nil) } + assert_nothing_raised { I18n.t(:bar, :locale => nil) } + end + + test "lookup: a resulting String is not frozen" do + assert !I18n.t(:string).frozen? + end + + test "lookup: a resulting Array is not frozen" do + assert !I18n.t(:array).frozen? + end + + test "lookup: a resulting Hash is not frozen" do + assert !I18n.t(:hash).frozen? + end + + # Addresses issue: #599 + test "lookup: only interpolates once when resolving symbols" do + I18n.backend.store_translations(:en, foo: :bar, bar: '%{value}') + assert_equal '%{dont_interpolate_me}', I18n.t(:foo, value: '%{dont_interpolate_me}') + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/pluralization.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/pluralization.rb new file mode 100644 index 0000000..19e73f3 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/pluralization.rb @@ -0,0 +1,35 @@ +# encoding: utf-8 + +module I18n + module Tests + module Pluralization + test "pluralization: given 0 it returns the :zero translation if it is defined" do + assert_equal 'zero', I18n.t(:default => { :zero => 'zero' }, :count => 0) + end + + test "pluralization: given 0 it returns the :other translation if :zero is not defined" do + assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 0) + end + + test "pluralization: given 1 it returns the singular translation" do + assert_equal 'bar', I18n.t(:default => { :one => 'bar' }, :count => 1) + end + + test "pluralization: given 2 it returns the :other translation" do + assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 2) + end + + test "pluralization: given 3 it returns the :other translation" do + assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 3) + end + + test "pluralization: given nil it returns the whole entry" do + assert_equal({ :one => 'bar' }, I18n.t(:default => { :one => 'bar' }, :count => nil)) + end + + test "pluralization: given incomplete pluralization data it raises I18n::InvalidPluralizationData" do + assert_raises(I18n::InvalidPluralizationData) { I18n.t(:default => { :one => 'bar' }, :count => 2) } + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/procs.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/procs.rb new file mode 100644 index 0000000..d377117 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/tests/procs.rb @@ -0,0 +1,71 @@ +# encoding: utf-8 + +module I18n + module Tests + module Procs + test "lookup: given a translation is a proc it calls the proc with the key and interpolation values" do + I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| I18n::Tests::Procs.filter_args(*args) }) + assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(:a_lambda, :foo => 'foo') + end + + test "lookup: given a translation is a proc it passes the interpolation values as keyword arguments" do + I18n.backend.store_translations(:en, :a_lambda => lambda { |key, foo:, **| I18n::Tests::Procs.filter_args(key, foo: foo) }) + assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(:a_lambda, :foo => 'foo') + end + + test "defaults: given a default is a Proc it calls it with the key and interpolation values" do + proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) } + assert_equal %|[nil, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => proc, :foo => 'foo') + end + + test "defaults: given a default is a key that resolves to a Proc it calls it with the key and interpolation values" do + the_lambda = lambda { |*args| I18n::Tests::Procs.filter_args(*args) } + I18n.backend.store_translations(:en, :a_lambda => the_lambda) + assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => :a_lambda, :foo => 'foo') + assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => [nil, :a_lambda], :foo => 'foo') + end + + test "interpolation: given an interpolation value is a lambda it calls it with key and values before interpolating it" do + proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) } + if RUBY_VERSION < "3.4" + assert_match %r(\[\{:foo=>#<Proc.*>\}\]), I18n.t(nil, :default => '%{foo}', :foo => proc) + else + assert_match %r(\[\{foo: #<Proc.*>\}\]), I18n.t(nil, :default => '%{foo}', :foo => proc) + end + end + + test "interpolation: given a key resolves to a Proc that returns a string then interpolation still works" do + proc = lambda { |*args| "%{foo}: " + I18n::Tests::Procs.filter_args(*args) } + assert_equal %|foo: [nil, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => proc, :foo => 'foo') + end + + test "pluralization: given a key resolves to a Proc that returns valid data then pluralization still works" do + proc = lambda { |*args| { :zero => 'zero', :one => 'one', :other => 'other' } } + assert_equal 'zero', I18n.t(:default => proc, :count => 0) + assert_equal 'one', I18n.t(:default => proc, :count => 1) + assert_equal 'other', I18n.t(:default => proc, :count => 2) + end + + test "lookup: given the option :resolve => false was passed it does not resolve proc translations" do + I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| I18n::Tests::Procs.filter_args(*args) }) + assert_equal Proc, I18n.t(:a_lambda, :resolve => false).class + end + + test "lookup: given the option :resolve => false was passed it does not resolve proc default" do + assert_equal Proc, I18n.t(nil, :default => lambda { |*args| I18n::Tests::Procs.filter_args(*args) }, :resolve => false).class + end + + + def self.filter_args(*args) + args.map do |arg| + if arg.is_a?(Hash) + arg.delete(:fallback_in_progress) + arg.delete(:fallback_original_locale) + arg.delete(:skip_interpolation) + end + arg + end.inspect + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/utils.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/utils.rb new file mode 100644 index 0000000..8841561 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/utils.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module I18n + module Utils + class << self + if Hash.method_defined?(:except) + def except(hash, *keys) + hash.except(*keys) + end + else + def except(hash, *keys) + hash = hash.dup + keys.each { |k| hash.delete(k) } + hash + end + end + + def deep_merge(hash, other_hash, &block) + deep_merge!(hash.dup, other_hash, &block) + end + + def deep_merge!(hash, other_hash, &block) + hash.merge!(other_hash) do |key, this_val, other_val| + if this_val.is_a?(Hash) && other_val.is_a?(Hash) + deep_merge(this_val, other_val, &block) + elsif block_given? + yield key, this_val, other_val + else + other_val + end + end + end + + def deep_symbolize_keys(hash) + hash.each_with_object({}) do |(key, value), result| + result[key.respond_to?(:to_sym) ? key.to_sym : key] = deep_symbolize_keys_in_object(value) + result + end + end + + private + + def deep_symbolize_keys_in_object(value) + case value + when Hash + deep_symbolize_keys(value) + when Array + value.map { |e| deep_symbolize_keys_in_object(e) } + else + value + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/version.rb b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/version.rb new file mode 100644 index 0000000..3d937a4 --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/i18n-1.15.2/lib/i18n/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module I18n + VERSION = "1.15.2" +end |
