pax_global_header00006660000000000000000000000064144371716730014527gustar00rootroot0000000000000052 comment=395aa5e9c0f29ca20af83750fb1c7e01cb07a67b i18n-1.14.1/000077500000000000000000000000001443717167300123725ustar00rootroot00000000000000i18n-1.14.1/.github/000077500000000000000000000000001443717167300137325ustar00rootroot00000000000000i18n-1.14.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001443717167300161155ustar00rootroot00000000000000i18n-1.14.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000006611443717167300206120ustar00rootroot00000000000000--- name: Bug report about: Create a bug report title: "[BUG]" labels: '' assignees: '' --- ## What I tried to do * Fill this out! ## What I expected to happen * Fill this out! ## What actually happened * Fill this out! ## Versions of i18n, rails, and anything else you think is necessary * Fill this out! ---- Bonus points for providing an application or a small code example which reproduces the issue. Thanks! :heart: i18n-1.14.1/.github/funding.yml000066400000000000000000000000201443717167300160770ustar00rootroot00000000000000github: [radar] i18n-1.14.1/.github/workflows/000077500000000000000000000000001443717167300157675ustar00rootroot00000000000000i18n-1.14.1/.github/workflows/ruby.yml000066400000000000000000000037561443717167300175060ustar00rootroot00000000000000name: Ruby on: # Trigger the workflow on push or pull request, # but only for the master branch push: branches: - master pull_request: branches: - master jobs: build: env: BUNDLE_GEMFILE: ${{ matrix.gemfile }} strategy: fail-fast: false matrix: ruby_version: [3.2, 3.1, "3.0", 2.7, 2.6, jruby] gemfile: - Gemfile - gemfiles/Gemfile.rails-5.2.x - gemfiles/Gemfile.rails-6.0.x - gemfiles/Gemfile.rails-6.1.x - gemfiles/Gemfile.rails-7.0.x - gemfiles/Gemfile.rails-main exclude: # Ruby 3.2 is not supported by Rails 5.2.x - ruby_version: 3.2 gemfile: gemfiles/Gemfile.rails-5.2.x # Ruby 3.1 is not supported by Rails 5.2.x - ruby_version: 3.1 gemfile: gemfiles/Gemfile.rails-5.2.x # Ruby 3.x is not supported by Rails 5.2.x - ruby_version: "3.0" gemfile: gemfiles/Gemfile.rails-5.2.x # Ruby 2.6.x is not supported by Rails main - ruby_version: 2.6 gemfile: gemfiles/Gemfile.rails-main # Ruby 2.6.x is not supported by Rails 7.0.x - ruby_version: 2.6 gemfile: gemfiles/Gemfile.rails-7.0.x # JRuby 9.4.2.0 (3.1.0) is not supported by Rails 5.2.x - ruby_version: jruby gemfile: gemfiles/Gemfile.rails-5.2.x # JRuby is not supported by Rails 7.0.x - ruby_version: jruby gemfile: gemfiles/Gemfile.rails-7.0.x # JRuby is not supported by Rails main - ruby_version: jruby gemfile: gemfiles/Gemfile.rails-main runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby_version }} bundler-cache: true # 'bundle install' and cache - name: Build and test with Rake run: bundle exec rake i18n-1.14.1/.gitignore000066400000000000000000000002251443717167300143610ustar00rootroot00000000000000.DS_Store test/rails/fixtures nbproject/ vendor/**/* *.swp pkg .bundle .rvmrc .ruby-version .ruby-gemset .tool-versions Gemfile.lock gemfiles/*.lock i18n-1.14.1/CHANGELOG.md000066400000000000000000000001601443717167300142000ustar00rootroot00000000000000# Changelog has moved For changes, please see our [Releases page](https://github.com/svenfuchs/i18n/releases). i18n-1.14.1/Gemfile000066400000000000000000000002311443717167300136610ustar00rootroot00000000000000source 'https://rubygems.org' gemspec gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake', '~> 13' gem 'minitest', '~> 5.14' gem 'json' i18n-1.14.1/MIT-LICENSE000066400000000000000000000020451443717167300140270ustar00rootroot00000000000000Copyright (c) 2008 The Ruby I18n team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.i18n-1.14.1/README.md000066400000000000000000000065621443717167300136620ustar00rootroot00000000000000# Ruby I18n [![Gem Version](https://badge.fury.io/rb/i18n.svg)](https://badge.fury.io/rb/i18n) [![Build Status](https://github.com/ruby-i18n/i18n/workflows/Ruby/badge.svg)](https://github.com/ruby-i18n/i18n/actions?query=workflow%3ARuby) Ruby internationalization and localization (i18n) solution. Currently maintained by @radar. ## Usage ### Rails You will most commonly use this library within a Rails app. [See the Rails Guide](https://guides.rubyonrails.org/i18n.html) for an example of its usage. ### Ruby (without Rails) If you want to use this library without Rails, you can simply add `i18n` to your `Gemfile`: ```ruby gem 'i18n' ``` Then configure I18n with some translations, and a default locale: ```ruby I18n.load_path += Dir[File.expand_path("config/locales") + "/*.yml"] I18n.default_locale = :en # (note that `en` is already the default!) ``` A simple translation file in your project might live at `config/locales/en.yml` and look like: ```yml en: test: "This is a test" ``` You can then access this translation by doing: ```ruby I18n.t(:test) ``` You can switch locales in your project by setting `I18n.locale` to a different value: ```ruby I18n.locale = :de I18n.t(:test) # => "Dies ist ein Test" ``` ## Features * Translation and localization * Interpolation of values to translations * Pluralization (CLDR compatible) * Customizable transliteration to ASCII * Flexible defaults * Bulk lookup * Lambdas as translation data * Custom key/scope separator * Custom exception handlers * Extensible architecture with a swappable backend ## Pluggable Features * Cache * Pluralization: lambda pluralizers stored as translation data * Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation) * [Gettext support](https://github.com/ruby-i18n/i18n/wiki/Gettext) * Translation metadata ## Alternative Backend * Chain * ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs) * KeyValue (uses active_support/json and cannot store procs) For more information and lots of resources see [the 'Resources' page on the wiki](https://github.com/ruby-i18n/i18n/wiki/Resources). ## Tests You can run tests both with * `rake test` or just `rake` * run any test file directly, e.g. `ruby -Ilib:test test/api/simple_test.rb` You can run all tests against all Gemfiles with * `ruby test/run_all.rb` The structure of the test suite is a bit unusual as it uses modules to reuse particular tests in different test cases. The reason for this is that we need to enforce the I18n API across various combinations of extensions. E.g. the Simple backend alone needs to support the same API as any combination of feature and/or optimization modules included to the Simple backend. We test this by reusing the same API definition (implemented as test methods) in test cases with different setups. You can find the test cases that enforce the API in test/api. And you can find the API definition test methods in test/api/tests. All other test cases (e.g. as defined in test/backend, test/core_ext) etc. follow the usual test setup and should be easy to grok. ## More Documentation Additional documentation can be found here: https://github.com/ruby-i18n/i18n/wiki ## Contributors * @radar * @carlosantoniodasilva * @josevalim * @knapo * @tigrish * [and many more](https://github.com/ruby-i18n/i18n/graphs/contributors) ## License MIT License. See the included MIT-LICENSE file. i18n-1.14.1/Rakefile000066400000000000000000000004231443717167300140360ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rake/testtask' task :default => [:test] Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.libs << 'test' t.pattern = "test/**/*_test.rb" t.verbose = true t.warning = true end Rake::Task['test'].comment = "Run all i18n tests" i18n-1.14.1/benchmark/000077500000000000000000000000001443717167300143245ustar00rootroot00000000000000i18n-1.14.1/benchmark/example.yml000066400000000000000000000114401443717167300165020ustar00rootroot00000000000000en: first: "First" activemodel: errors: messages: :"activerecord.errors.messages" activerecord: errors: messages: inclusion: "is not included in the list" exclusion: "is reserved" invalid: "is invalid" confirmation: "doesn't match confirmation" accepted: "must be accepted" empty: "can't be empty" blank: "can't be blank" too_long: "is too long (maximum is %{count} characters)" too_short: "is too short (minimum is %{count} characters)" wrong_length: "is the wrong length (should be %{count} characters)" taken: "has already been taken" not_a_number: "is not a number" greater_than: "must be greater than %{count}" greater_than_or_equal_to: "must be greater than or equal to %{count}" equal_to: "must be equal to %{count}" less_than: "must be less than %{count}" less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" record_invalid: "Validation failed: %{errors}" models: user: blank: "This is a custom blank message for %{model}: %{attribute}" attributes: login: blank: "This is a custom blank message for User login" models: user: "Dude" attributes: admins: user: login: "Handle" date: formats: # Use the strftime parameters for formats. # When no format has been given, it uses default. # You can provide other formats here if you like! default: "%Y-%m-%d" short: "%b %d" long: "%B %d, %Y" day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] # Don't forget the nil at the beginning; there's no such thing as a 0th month month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] # Used in date_select and datime_select. order: - :year, - :month, - :day time: formats: default: "%a, %d %b %Y %H:%M:%S %z" short: "%d %b %H:%M" long: "%B %d, %Y %H:%M" am: "am" pm: "pm" support: array: words_connector: ", " two_words_connector: " and " last_word_connector: ", and " activemodel: errors: messages: inclusion: "is not included in the list" exclusion: "is reserved" invalid: "is invalid" confirmation: "doesn't match confirmation" accepted: "must be accepted" empty: "can't be empty" blank: "can't be blank" too_long: "is too long (maximum is %{count} characters)" too_short: "is too short (minimum is %{count} characters)" wrong_length: "is the wrong length (should be %{count} characters)" taken: "has already been taken" not_a_number: "is not a number" greater_than: "must be greater than %{count}" greater_than_or_equal_to: "must be greater than or equal to %{count}" equal_to: "must be equal to %{count}" less_than: "must be less than %{count}" less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" record_invalid: "Validation failed: %{errors}" models: user: blank: "This is a custom blank message for %{model}: %{attribute}" attributes: login: blank: "This is a custom blank message for User login" models: user: "Dude" attributes: user: login: "Handle" model_data: date: formats: # Use the strftime parameters for formats. # When no format has been given, it uses default. # You can provide other formats here if you like! default: "%Y-%m-%d" short: "%b %d" long: "%B %d, %Y" day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] # Don't forget the nil at the beginning; there's no such thing as a 0th month month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] # Used in date_select and datime_select. order: - :year - :month - :day time: formats: default: "%a, %d %b %Y %H:%M:%S %z" short: "%d %b %H:%M" long: "%B %d, %Y %H:%M" am: "am" pm: "pm" support: array: words_connector: ", " two_words_connector: " and " last_word_connector: ", and " i18n-1.14.1/benchmark/run.rb000066400000000000000000000056551443717167300154700ustar00rootroot00000000000000#!/usr/bin/ruby $:.unshift File.expand_path('../../lib', __FILE__) require 'bundler/setup' require 'i18n' require 'benchmark' require 'yaml' N = (ARGV.shift || 1000).to_i YAML_HASH = YAML.load_file(File.expand_path("example.yml", File.dirname(__FILE__))) module Backends Simple = I18n::Backend::Simple.new Interpolation = Class.new(I18n::Backend::Simple) do include I18n::Backend::InterpolationCompiler end.new begin require 'active_support' KeyValue = I18n::Backend::KeyValue.new({}, true) puts "Running KeyValue with ActiveSupport #{ActiveSupport::VERSION::STRING}" rescue LoadError puts 'Skipping KeyValue since ActiveSupport could not be loaded.' end end ORDER = %w(Simple Interpolation KeyValue) ORDER.map!(&:to_sym) if RUBY_VERSION > '1.9' module Benchmark WIDTH = 20 def self.rt(label = "", n=N, &blk) print label.ljust(WIDTH) time, objects = measure_objects(n, &blk) time = time.respond_to?(:real) ? time.real : time print format("%8.2f ms %8d objects\n", time * 1000, objects) rescue Exception => e print "FAILED: #{e.message}" end if ObjectSpace.respond_to?(:allocated_objects) def self.measure_objects(n, &blk) obj = ObjectSpace.allocated_objects t = Benchmark.realtime { n.times(&blk) } [t, ObjectSpace.allocated_objects - obj] end else def self.measure_objects(n, &blk) [Benchmark.measure { n.times(&blk) }, 0] end end end benchmarker = lambda do |backend_name| I18n.backend = Backends.const_get(backend_name) puts "=> #{backend_name}\n\n" Benchmark.rt "store", 1 do I18n.backend.store_translations(*YAML_HASH.to_a.first) end I18n.backend.translate :en, :first Benchmark.rt "available_locales" do I18n.backend.available_locales end Benchmark.rt "t (depth=3)" do I18n.backend.translate :en, :"activerecord.models.user" end Benchmark.rt "t (depth=5)" do I18n.backend.translate :en, :"activerecord.attributes.admins.user.login" end Benchmark.rt "t (depth=7)" do I18n.backend.translate :en, :"activerecord.errors.models.user.attributes.login.blank" end Benchmark.rt "t w/ default" do I18n.backend.translate :en, :"activerecord.models.another", :default => "Another" end Benchmark.rt "t w/ interpolation" do I18n.backend.translate :en, :"activerecord.errors.models.user.blank", :model => "User", :attribute => "name" end Benchmark.rt "t w/ link" do I18n.backend.translate :en, :"activemodel.errors.messages.blank" end Benchmark.rt "t subtree" do I18n.backend.translate :en, :"activerecord.errors.messages" end puts end # Run! puts puts "Running benchmarks with N = #{N}\n\n" (ORDER & Backends.constants).each(&benchmarker) Backends.constants.each do |backend_name| backend = Backends.const_get(backend_name) backend.reload! backend.extend I18n::Backend::Memoize end puts "Running memoized benchmarks with N = #{N}\n\n" (ORDER & Backends.constants).each(&benchmarker) i18n-1.14.1/gemfiles/000077500000000000000000000000001443717167300141655ustar00rootroot00000000000000i18n-1.14.1/gemfiles/Gemfile.rails-5.0.x000066400000000000000000000003361443717167300173410ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', '~> 5.0.0' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake', '~> 13' gem 'minitest', '~> 5.14' platforms :mri do gem 'oj' end i18n-1.14.1/gemfiles/Gemfile.rails-5.1.x000066400000000000000000000003251443717167300173400ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', '~> 5.1.0' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake' gem 'minitest', '~> 5.14' platforms :mri do gem 'oj' end i18n-1.14.1/gemfiles/Gemfile.rails-5.2.x000066400000000000000000000003251443717167300173410ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', '~> 5.2.0' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake' gem 'minitest', '~> 5.14' platforms :mri do gem 'oj' end i18n-1.14.1/gemfiles/Gemfile.rails-6.0.x000066400000000000000000000003251443717167300173400ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', '~> 6.0.0' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake' gem 'minitest', '~> 5.14' platforms :mri do gem 'oj' end i18n-1.14.1/gemfiles/Gemfile.rails-6.1.x000066400000000000000000000003231443717167300173370ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', '~> 6.1' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake' gem 'minitest', '~> 5.14' platforms :mri do gem 'oj' end i18n-1.14.1/gemfiles/Gemfile.rails-7.0.x000066400000000000000000000003231443717167300173370ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', '~> 7.0' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake' gem 'minitest', '~> 5.14' platforms :mri do gem 'oj' end i18n-1.14.1/gemfiles/Gemfile.rails-main000066400000000000000000000003571443717167300175200ustar00rootroot00000000000000source 'https://rubygems.org' gemspec :path => '..' gem 'activesupport', github: 'rails/rails', branch: 'main' gem 'mocha', '~> 1.7.0' gem 'test_declarative', '0.0.6' gem 'rake' gem 'minitest', '~> 5.1' platforms :mri do gem 'oj' end i18n-1.14.1/i18n.gemspec000066400000000000000000000022771443717167300145260ustar00rootroot00000000000000# encoding: utf-8 $: << File.expand_path('../lib', __FILE__) require 'i18n/version' Gem::Specification.new do |s| s.name = "i18n" s.version = I18n::VERSION s.authors = ["Sven Fuchs", "Joshua Harvey", "Matt Aimonetti", "Stephan Soller", "Saimon Moore", "Ryan Bigg"] s.email = "rails-i18n@googlegroups.com" s.homepage = "https://github.com/ruby-i18n/i18n" s.summary = "New wave Internationalization support for Ruby" s.description = "New wave Internationalization support for Ruby." s.license = "MIT" s.metadata = { 'bug_tracker_uri' => 'https://github.com/ruby-i18n/i18n/issues', 'changelog_uri' => 'https://github.com/ruby-i18n/i18n/releases', 'documentation_uri' => 'https://guides.rubyonrails.org/i18n.html', 'source_code_uri' => 'https://github.com/ruby-i18n/i18n', } s.files = Dir.glob("lib/**/*") + %w(README.md MIT-LICENSE) s.platform = Gem::Platform::RUBY s.require_path = 'lib' s.required_rubygems_version = '>= 1.3.5' s.required_ruby_version = '>= 2.3.0' s.add_dependency 'concurrent-ruby', '~> 1.0' end i18n-1.14.1/lib/000077500000000000000000000000001443717167300131405ustar00rootroot00000000000000i18n-1.14.1/lib/i18n.rb000066400000000000000000000360411443717167300142500ustar00rootroot00000000000000# 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 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 Thread.current[:i18n_config] ||= I18n::Config.new end # Sets I18n configuration object. def config=(value) Thread.current[:i18n_config] = value 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) 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. E.g., ActionView ships with the translation: # :date => {:formats => {:short => "%b %d"}}. # # Translations can be looked up at any level of this hash using the key argument # and the scope option. E.g., in this example I18n.t :date # returns the whole translations hash {:formats => {:short => "%b %d"}}. # # Key can be either a single key or a dot-separated key (both Strings and Symbols # work). E.g., 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. # # E.g., with a translation :foo => "foo %{bar}" 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 ['Foo', 'Foos']. # # Note that I18n::Backend::Simple 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 :count option can be used both for pluralization and interpolation. # E.g., with the translation # :foo => ['%{count} foo', '%{count} foos'], count will # be interpolated to the pluralized translation: # I18n.t :foo, :count => 1 # => '1 foo' # # *DEFAULTS* # # This returns the translation for :foo or default if no translation was found: # I18n.t :foo, :default => 'default' # # This returns the translation for :foo or the translation for :bar if no # translation for :foo was found: # I18n.t :foo, :default => :bar # # Returns the translation for :foo or the translation for :bar # or default if no translations for :foo and :bar were found. # I18n.t :foo, :default => [:bar, 'default'] # # *BULK LOOKUP* # # This returns an array with the translations for :foo and :bar. # 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 :salutation resolves to: # lambda { |key, options| options[:gender] == 'm' ? "Mr. #{options[:name]}" : "Mrs. #{options[:name]}" } # # Then I18n.t(:salutation, :gender => 'w', :name => 'Smith') 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. # # *Ruby 2.7+ keyword arguments warning* # # 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 translate that adds :raise => true. With # this option, if no translation is found, it will raise I18n::MissingTranslationData def translate!(key, **options) translate(key, **options, raise: true) end alias :t! :translate! # 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? 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 # i18n.transliterate.rule. # # 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 .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 end extend Base end i18n-1.14.1/lib/i18n/000077500000000000000000000000001443717167300137175ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/backend.rb000066400000000000000000000020501443717167300156300ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/000077500000000000000000000000001443717167300153065ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/backend/base.rb000066400000000000000000000301271443717167300165500ustar00rootroot00000000000000# 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] values = Utils.except(options, *RESERVED_KEYS) unless options.empty? if values entry = if deep_interpolation deep_interpolate(locale, entry, values) else interpolate(locale, entry, values) end 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 (e.g., :short in :'date.formats'). 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)) 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) [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 i18n-1.14.1/lib/i18n/backend/cache.rb000066400000000000000000000067001443717167300167010ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/cache_file.rb000066400000000000000000000025551443717167300177040ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/cascade.rb000066400000000000000000000042271443717167300172230ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/chain.rb000066400000000000000000000105161443717167300167200ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/fallbacks.rb000066400000000000000000000107551443717167300175650ustar00rootroot00000000000000# 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 Thread.current[:i18n_fallbacks] || @@fallbacks 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 Thread.current[:i18n_fallbacks] = @@fallbacks 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)) 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) 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 i18n-1.14.1/lib/i18n/backend/flatten.rb000066400000000000000000000074711443717167300173010ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/gettext.rb000066400000000000000000000054631443717167300173270ustar00rootroot00000000000000# 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 you must use 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 i18n-1.14.1/lib/i18n/backend/interpolation_compiler.rb000066400000000000000000000073241443717167300224220ustar00rootroot00000000000000# 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 = /(%%\{[^\}]+\}|%\{[^\}]+\})/ INTERPOLATION_SYNTAX_PATTERN = /(%)?(%\{([^\}]+)\})/ def compile_if_an_interpolation(string) if interpolated_str?(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 =~ INTERPOLATION_SYNTAX_PATTERN 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| (matchdata = token.match(INTERPOLATION_SYNTAX_PATTERN)) ? handle_interpolation_token(token, matchdata) : escape_plain_str(token) end.join end def handle_interpolation_token(interpolation, matchdata) escaped, pattern, key = matchdata.values_at(1, 2, 3) escaped ? pattern : compile_interpolation_token(key.to_sym) 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 i18n-1.14.1/lib/i18n/backend/key_value.rb000066400000000000000000000136431443717167300176260ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/lazy_loadable.rb000066400000000000000000000144061443717167300204420ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/memoize.rb000066400000000000000000000027141443717167300173040ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/metadata.rb000066400000000000000000000042521443717167300174160ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/pluralization.rb000066400000000000000000000104111443717167300205250ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/backend/simple.rb000066400000000000000000000073121443717167300171270ustar00rootroot00000000000000# 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) 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. currency.format is regarded the same as # %w(currency format). 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 i18n-1.14.1/lib/i18n/backend/transliterator.rb000066400000000000000000000107731443717167300207200ustar00rootroot00000000000000# 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" }.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 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 i18n-1.14.1/lib/i18n/config.rb000066400000000000000000000130411443717167300155100ustar00rootroot00000000000000# 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 # Sets the current locale pseudo-globally, i.e. in the Thread.current 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 i18n-1.14.1/lib/i18n/exceptions.rb000066400000000000000000000112641443717167300164310ustar00rootroot00000000000000# frozen_string_literal: true require 'cgi' 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 i18n-1.14.1/lib/i18n/gettext.rb000066400000000000000000000014141443717167300157300ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/gettext/000077500000000000000000000000001443717167300154035ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/gettext/helpers.rb000066400000000000000000000050571443717167300174010ustar00rootroot00000000000000# 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. # _(fruit) cannot be understood by the gettext parser. To help the parser find all your translations, # you can add fruit = N_("Apple") 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 i18n-1.14.1/lib/i18n/gettext/po_parser.rb000066400000000000000000000151051443717167300177240ustar00rootroot00000000000000=begin poparser.rb - Generate a .mo Copyright (C) 2003-2009 Masao Mutoh 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 i18n-1.14.1/lib/i18n/interpolate/000077500000000000000000000000001443717167300162455ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/interpolate/ruby.rb000066400000000000000000000036661443717167300175660ustar00rootroot00000000000000# 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 "%.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 i18n-1.14.1/lib/i18n/locale.rb000066400000000000000000000002351443717167300155030ustar00rootroot00000000000000# frozen_string_literal: true module I18n module Locale autoload :Fallbacks, 'i18n/locale/fallbacks' autoload :Tag, 'i18n/locale/tag' end end i18n-1.14.1/lib/i18n/locale/000077500000000000000000000000001443717167300151565ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/locale/fallbacks.rb000066400000000000000000000063531443717167300174340ustar00rootroot00000000000000# 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: # #

# I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en] 
# # 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 protected def compute(tags, include_defaults = true, exclude = []) result = Array(tags).flat_map do |tag| tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym } - exclude tags.each { |_tag| tags += compute(@map[_tag], false, exclude + tags) if @map[_tag] } tags end result.push(*defaults) if include_defaults result.uniq! result.compact! result end end end end i18n-1.14.1/lib/i18n/locale/tag.rb000066400000000000000000000014521443717167300162600ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/locale/tag/000077500000000000000000000000001443717167300157315ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/locale/tag/parents.rb000066400000000000000000000010131443717167300177250ustar00rootroot00000000000000module 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 i18n-1.14.1/lib/i18n/locale/tag/rfc4646.rb000066400000000000000000000046631443717167300173650ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/locale/tag/simple.rb000066400000000000000000000012211443717167300175430ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/middleware.rb000066400000000000000000000003541443717167300163630ustar00rootroot00000000000000# frozen_string_literal: true module I18n class Middleware def initialize(app) @app = app end def call(env) @app.call(env) ensure Thread.current[:i18n_config] = I18n::Config.new end end end i18n-1.14.1/lib/i18n/tests.rb000066400000000000000000000007371443717167300154150ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/000077500000000000000000000000001443717167300150615ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/tests/basics.rb000066400000000000000000000043341443717167300166560ustar00rootroot00000000000000module 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 i18n-1.14.1/lib/i18n/tests/defaults.rb000066400000000000000000000036631443717167300172250ustar00rootroot00000000000000# 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 end end end i18n-1.14.1/lib/i18n/tests/interpolation.rb000066400000000000000000000170131443717167300202770ustar00rootroot00000000000000# 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'.force_encoding("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'.force_encoding("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}') } 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 i18n-1.14.1/lib/i18n/tests/link.rb000066400000000000000000000051121443717167300163420ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/localization.rb000066400000000000000000000011131443717167300200720ustar00rootroot00000000000000module 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 endi18n-1.14.1/lib/i18n/tests/localization/000077500000000000000000000000001443717167300175515ustar00rootroot00000000000000i18n-1.14.1/lib/i18n/tests/localization/date.rb000066400000000000000000000123751443717167300210230ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/localization/date_time.rb000066400000000000000000000107741443717167300220420ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/localization/procs.rb000066400000000000000000000131261443717167300212270ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/localization/time.rb000066400000000000000000000105441443717167300210400ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/lookup.rb000066400000000000000000000054631443717167300167270ustar00rootroot00000000000000# 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 end end end i18n-1.14.1/lib/i18n/tests/pluralization.rb000066400000000000000000000025211443717167300203030ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/tests/procs.rb000066400000000000000000000064131443717167300165400ustar00rootroot00000000000000# 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) } assert_match %r(\[\{:foo=>#\}\]), I18n.t(nil, :default => '%{foo}', :foo => proc) 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) end arg end.inspect end end end end i18n-1.14.1/lib/i18n/utils.rb000066400000000000000000000024671443717167300154150ustar00rootroot00000000000000# 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 i18n-1.14.1/lib/i18n/version.rb000066400000000000000000000001041443717167300157240ustar00rootroot00000000000000# frozen_string_literal: true module I18n VERSION = "1.14.1" end i18n-1.14.1/test/000077500000000000000000000000001443717167300133515ustar00rootroot00000000000000i18n-1.14.1/test/api/000077500000000000000000000000001443717167300141225ustar00rootroot00000000000000i18n-1.14.1/test/api/all_features_test.rb000066400000000000000000000031301443717167300201510ustar00rootroot00000000000000require 'test_helper' begin require 'rubygems' require 'active_support' rescue LoadError puts "not testing with Cache enabled because active_support can not be found" end class I18nAllFeaturesApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Metadata include I18n::Backend::Cache include I18n::Backend::Cascade include I18n::Backend::Fallbacks include I18n::Backend::Pluralization include I18n::Backend::Memoize end def setup I18n.backend = I18n::Backend::Chain.new(Backend.new, I18n::Backend::Simple.new) I18n.cache_store = cache_store super end def teardown I18n.cache_store.clear if I18n.cache_store I18n.cache_store = nil super end def cache_store ActiveSupport::Cache.lookup_store(:memory_store) if cache_available? end def cache_available? defined?(ActiveSupport) && defined?(ActiveSupport::Cache) end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use a Chain backend with an all features backend" do assert_equal I18n::Backend::Chain, I18n.backend.class assert_equal Backend, I18n.backend.backends.first.class end # links: test that keys stored on one backend can link to keys stored on another backend end i18n-1.14.1/test/api/cascade_test.rb000066400000000000000000000013201443717167300170650ustar00rootroot00000000000000require 'test_helper' class I18nCascadeApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Cascade end def setup I18n.backend = Backend.new super end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use a backend with Cascade included" do assert_equal Backend, I18n.backend.class end end i18n-1.14.1/test/api/chain_test.rb000066400000000000000000000012611443717167300165700ustar00rootroot00000000000000require 'test_helper' class I18nApiChainTest < I18n::TestCase def setup super I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, I18n.backend) end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use the Chain backend" do assert_equal I18n::Backend::Chain, I18n.backend.class end end i18n-1.14.1/test/api/fallbacks_test.rb000066400000000000000000000014621443717167300174330ustar00rootroot00000000000000require 'test_helper' class I18nFallbacksApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup I18n.backend = Backend.new super end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use a backend with Fallbacks included" do assert_equal Backend, I18n.backend.class end # links: test that keys stored on one backend can link to keys stored on another backend end i18n-1.14.1/test/api/key_value_test.rb000066400000000000000000000012671443717167300175000ustar00rootroot00000000000000require 'test_helper' class I18nKeyValueApiTest < I18n::TestCase include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization # include Tests::Api::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time # include Tests::Api::Localization::Procs def setup I18n.backend = I18n::Backend::KeyValue.new({}) super end test "make sure we use the KeyValue backend" do assert_equal I18n::Backend::KeyValue, I18n.backend.class end end if I18n::TestCase.key_value? i18n-1.14.1/test/api/lazy_loadable_test.rb000066400000000000000000000012531443717167300203110ustar00rootroot00000000000000require 'test_helper' class I18nLazyLoadableBackendApiTest < I18n::TestCase def setup I18n.backend = I18n::Backend::LazyLoadable.new super end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use the LazyLoadable backend" do assert_equal I18n::Backend::LazyLoadable, I18n.backend.class end end i18n-1.14.1/test/api/memoize_test.rb000066400000000000000000000027661443717167300171660ustar00rootroot00000000000000require 'test_helper' class I18nMemoizeBackendWithSimpleApiTest < I18n::TestCase include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs class MemoizeBackend < I18n::Backend::Simple include I18n::Backend::Memoize end def setup I18n.backend = MemoizeBackend.new super end test "make sure we use the MemoizeBackend backend" do assert_equal MemoizeBackend, I18n.backend.class end end class I18nMemoizeBackendWithKeyValueApiTest < I18n::TestCase include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time # include I18n::Tests::Procs # include I18n::Tests::Localization::Procs class MemoizeBackend < I18n::Backend::KeyValue include I18n::Backend::Memoize end def setup I18n.backend = MemoizeBackend.new({}) super end test "make sure we use the MemoizeBackend backend" do assert_equal MemoizeBackend, I18n.backend.class end end if I18n::TestCase.key_value? i18n-1.14.1/test/api/override_test.rb000066400000000000000000000022421443717167300173250ustar00rootroot00000000000000require 'test_helper' class I18nOverrideTest < I18n::TestCase module OverrideInverse def translate(key, **options) super(key, **options).reverse end alias :t :translate end module OverrideSignature def translate(*args) args.first + args[1] end alias :t :translate end def setup super @I18n = I18n.dup @I18n.backend = I18n::Backend::Simple.new end test "make sure modules can overwrite I18n methods" do @I18n.extend OverrideInverse @I18n.backend.store_translations('en', :foo => 'bar') assert_equal 'rab', @I18n.translate(:foo, :locale => 'en') assert_equal 'rab', @I18n.t(:foo, :locale => 'en') assert_equal 'rab', @I18n.translate!(:foo, :locale => 'en') assert_equal 'rab', @I18n.t!(:foo, :locale => 'en') end test "make sure modules can overwrite I18n signature" do exception = catch(:exception) do @I18n.t('Hello', :tokenize => true, :throw => true) end assert exception.message @I18n.extend OverrideSignature assert_equal 'HelloWelcome message on home page', @I18n.translate('Hello', 'Welcome message on home page', :tokenize => true) # tr8n example end end i18n-1.14.1/test/api/pluralization_test.rb000066400000000000000000000014761443717167300204130ustar00rootroot00000000000000require 'test_helper' class I18nPluralizationApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization end def setup I18n.backend = Backend.new super end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use a backend with Pluralization included" do assert_equal Backend, I18n.backend.class end # links: test that keys stored on one backend can link to keys stored on another backend end i18n-1.14.1/test/api/simple_test.rb000066400000000000000000000013531443717167300170010ustar00rootroot00000000000000require 'test_helper' class I18nSimpleBackendApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization end def setup I18n.backend = I18n::Backend::Simple.new super end include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation include I18n::Tests::Link include I18n::Tests::Lookup include I18n::Tests::Pluralization include I18n::Tests::Procs include I18n::Tests::Localization::Date include I18n::Tests::Localization::DateTime include I18n::Tests::Localization::Time include I18n::Tests::Localization::Procs test "make sure we use the Simple backend" do assert_equal I18n::Backend::Simple, I18n.backend.class end end i18n-1.14.1/test/backend/000077500000000000000000000000001443717167300147405ustar00rootroot00000000000000i18n-1.14.1/test/backend/cache_file_test.rb000066400000000000000000000046501443717167300203730ustar00rootroot00000000000000require 'test_helper' require 'fileutils' require 'tempfile' module CountWrites attr_reader :writes def initialize(*args) super @writes = [] end def store_translations(*args) super.tap { @writes << args } end end module CacheFileTest test 'load_translations caches loaded file contents' do setup_backend! I18n.load_path = [locales_dir + '/en.yml'] assert_equal 0, @backend.writes.count @backend.load_translations unless @backend.is_a?(I18n::Backend::Simple) assert_equal('baz', I18n.t('foo.bar')) assert_equal 2, @backend.writes.count @backend.load_translations assert_equal('baz', I18n.t('foo.bar')) assert_equal 2, @backend.writes.count end test 'setting path_roots normalizes write key' do setup_backend! I18n.load_path = [locales_dir + '/en.yml'] @backend.path_roots = [locales_dir] @backend.load_translations refute_nil I18n.t("0/en\x01yml", scope: :load_file, locale: :i18n, default: nil) end test 'load_translations caches file through updated modification time' do setup_backend! Tempfile.open(['test', '.yml']) do |file| I18n.load_path = [file.path] File.write(file, { :en => { :foo => { :bar => 'baz' } } }.to_yaml) assert_equal 0, @backend.writes.count @backend.load_translations unless @backend.is_a?(I18n::Backend::Simple) assert_equal('baz', I18n.t('foo.bar')) assert_equal 2, @backend.writes.count FileUtils.touch(file, :mtime => Time.now + 1) @backend.load_translations assert_equal('baz', I18n.t('foo.bar')) assert_equal 2, @backend.writes.count File.write(file, { :en => { :foo => { :bar => 'baa' } } }.to_yaml) FileUtils.touch(file, :mtime => Time.now + 1) @backend.load_translations assert_equal('baa', I18n.t('foo.bar')) assert_equal 4, @backend.writes.count end end end class SimpleCacheFileTest < I18n::TestCase include CacheFileTest class Backend < I18n::Backend::Simple include CountWrites include I18n::Backend::CacheFile end def setup_backend! @backend = I18n.backend = Backend.new end end class KeyValueCacheFileTest < I18n::TestCase include CacheFileTest class Backend < I18n::Backend::KeyValue include CountWrites include I18n::Backend::CacheFile def initialize super({}) end end def setup_backend! @backend = I18n.backend = Backend.new end end if I18n::TestCase.key_value? i18n-1.14.1/test/backend/cache_test.rb000066400000000000000000000070531443717167300173740ustar00rootroot00000000000000require 'test_helper' require 'openssl' begin require 'active_support' rescue LoadError $stderr.puts "Skipping cache tests using ActiveSupport" else class I18nBackendCacheTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Cache end def setup I18n.backend = Backend.new super I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) I18n.cache_store.clear I18n.cache_key_digest = nil end def teardown super I18n.cache_store.clear I18n.cache_store = nil end test "it uses the cache" do assert I18n.cache_store.is_a?(ActiveSupport::Cache::MemoryStore) end test "translate hits the backend and caches the response" do I18n.backend.expects(:lookup).returns('Foo') assert_equal 'Foo', I18n.t(:foo) I18n.backend.expects(:lookup).never assert_equal 'Foo', I18n.t(:foo) I18n.backend.expects(:lookup).returns('Bar') assert_equal 'Bar', I18n.t(:bar) end test "translate returns a cached false response" do I18n.backend.expects(:lookup).never I18n.cache_store.expects(:read).returns(false) assert_equal false, I18n.t(:foo) end test "still raises MissingTranslationData but also caches it" do assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } assert_equal 1, I18n.cache_store.instance_variable_get(:@data).size # I18n.backend.expects(:lookup).returns(nil) # assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } # I18n.backend.expects(:lookup).never # assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } end test "MissingTranslationData does not cache custom options" do I18n.t(:missing, :scope => :foo, :extra => true) assert_equal 1, I18n.cache_store.instance_variable_get(:@data).size value = I18n.cache_store.read(I18n.cache_store.instance_variable_get(:@data).keys.first) assert_equal({ scope: :foo }, value.options) end test "uses 'i18n' as a cache key namespace by default" do assert_equal 0, I18n.backend.send(:cache_key, :en, :foo, {}).index('i18n') end test "adds a custom cache key namespace" do with_cache_namespace('bar') do assert_equal 0, I18n.backend.send(:cache_key, :en, :foo, {}).index('i18n/bar/') end end test "adds locale and hash of key and hash of options" do options = { :bar => 1 } assert_equal "i18n//en/#{:foo.to_s.hash}/#{options.to_s.hash}", I18n.backend.send(:cache_key, :en, :foo, options) end test "cache_key uses configured digest method" do digest = OpenSSL::Digest::SHA256.new options = { :bar => 1 } options_hash = options.inspect with_cache_key_digest(digest) do assert_equal "i18n//en/#{digest.hexdigest(:foo.to_s)}/#{digest.hexdigest(options_hash)}", I18n.backend.send(:cache_key, :en, :foo, options) end end test "keys should not be equal" do interpolation_values1 = { :foo => 1, :bar => 2 } interpolation_values2 = { :foo => 2, :bar => 1 } key1 = I18n.backend.send(:cache_key, :en, :some_key, interpolation_values1) key2 = I18n.backend.send(:cache_key, :en, :some_key, interpolation_values2) assert key1 != key2 end protected def with_cache_namespace(namespace) I18n.cache_namespace = namespace yield I18n.cache_namespace = nil end def with_cache_key_digest(digest) I18n.cache_key_digest = digest yield I18n.cache_key_digest = nil end end end # AS cache check i18n-1.14.1/test/backend/cascade_test.rb000066400000000000000000000070531443717167300177140ustar00rootroot00000000000000require 'test_helper' class I18nBackendCascadeTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Cascade end def setup super I18n.backend = Backend.new store_translations(:en, :foo => 'foo', :bar => { :baz => 'baz' }) @cascade_options = { :step => 1, :offset => 1, :skip_root => false } end def lookup(key, options = {}) I18n.t(key, **options.merge(:cascade => @cascade_options)) end test "still returns an existing translation as usual" do assert_equal 'foo', lookup(:foo) assert_equal 'baz', lookup(:'bar.baz') end test "falls back by cutting keys off the end of the scope" do assert_equal 'foo', lookup(:foo, :scope => :'missing') assert_equal 'foo', lookup(:foo, :scope => :'missing.missing') assert_equal 'baz', lookup(:baz, :scope => :'bar.missing') assert_equal 'baz', lookup(:baz, :scope => :'bar.missing.missing') end test "raises I18n::MissingTranslationData exception when no translation was found" do assert_raises(I18n::MissingTranslationData) { lookup(:'foo.missing', :raise => true) } assert_raises(I18n::MissingTranslationData) { lookup(:'bar.baz.missing', :raise => true) } assert_raises(I18n::MissingTranslationData) { lookup(:'missing.bar.baz', :raise => true) } end test "cascades before evaluating the default" do assert_equal 'foo', lookup(:foo, :scope => :missing, :default => 'default') end test "cascades defaults, too" do assert_equal 'foo', lookup(nil, :default => [:'missing.missing', :'missing.foo']) end test "works with :offset => 2 and a single key" do @cascade_options[:offset] = 2 lookup(:foo) end test "assemble required fallbacks for ActiveRecord validation messages" do store_translations(:en, :errors => { :odd => 'errors.odd', :reply => { :title => { :blank => 'errors.reply.title.blank' }, :taken => 'errors.reply.taken' }, :topic => { :title => { :format => 'errors.topic.title.format' }, :length => 'errors.topic.length' } } ) assert_equal 'errors.reply.title.blank', lookup(:'errors.reply.title.blank', :default => :'errors.topic.title.blank') assert_equal 'errors.reply.taken', lookup(:'errors.reply.title.taken', :default => :'errors.topic.title.taken') assert_equal 'errors.topic.title.format', lookup(:'errors.reply.title.format', :default => :'errors.topic.title.format') assert_equal 'errors.topic.length', lookup(:'errors.reply.title.length', :default => :'errors.topic.title.length') assert_equal 'errors.odd', lookup(:'errors.reply.title.odd', :default => :'errors.topic.title.odd') end test "assemble action view translation helper lookup cascade" do @cascade_options[:offset] = 2 store_translations(:en, :menu => { :show => 'menu.show' }, :namespace => { :menu => { :new => 'namespace.menu.new' }, :controller => { :menu => { :edit => 'namespace.controller.menu.edit' }, :action => { :menu => { :destroy => 'namespace.controller.action.menu.destroy' } } } } ) assert_equal 'menu.show', lookup(:'namespace.controller.action.menu.show') assert_equal 'namespace.menu.new', lookup(:'namespace.controller.action.menu.new') assert_equal 'namespace.controller.menu.edit', lookup(:'namespace.controller.action.menu.edit') assert_equal 'namespace.controller.action.menu.destroy', lookup(:'namespace.controller.action.menu.destroy') end end i18n-1.14.1/test/backend/chain_test.rb000066400000000000000000000134121443717167300174070ustar00rootroot00000000000000require 'test_helper' class I18nBackendChainTest < I18n::TestCase def setup super @first = backend(:en => { :foo => 'Foo', :formats => { :short => 'short', :subformats => {:short => 'short'}, }, :plural_1 => { :one => '%{count}' }, :dates => {:a => "A"}, :fallback_bar => nil, }) @second = backend(:en => { :bar => 'Bar', :formats => { :long => 'long', :subformats => {:long => 'long'}, }, :plural_2 => { :one => 'one' }, :dates => {:a => "B", :b => "B"}, :fallback_bar => 'Bar', }) @chain = I18n.backend = I18n::Backend::Chain.new(@first, @second) end test "looks up translations from the first chained backend" do assert_equal 'Foo', @first.send(:translations)[:en][:foo] assert_equal 'Foo', I18n.t(:foo) end test "looks up translations from the second chained backend" do assert_equal 'Bar', @second.send(:translations)[:en][:bar] assert_equal 'Bar', I18n.t(:bar) end test "defaults only apply to lookups on the last backend in the chain" do assert_equal 'Foo', I18n.t(:foo, :default => 'Bah') assert_equal 'Bar', I18n.t(:bar, :default => 'Bah') assert_equal 'Bah', I18n.t(:bah, :default => 'Bah') # default kicks in only here end test "default" do assert_equal 'Fuh', I18n.t(:default => 'Fuh') assert_equal 'Zero', I18n.t(:default => { :zero => 'Zero' }, :count => 0) assert_equal({ :zero => 'Zero' }, I18n.t(:default => { :zero => 'Zero' })) assert_equal 'Foo', I18n.t(:default => :foo) end test 'default is returned if translation is missing' do assert_equal({}, I18n.t(:'i18n.transliterate.rule', :locale => 'en', :default => {})) end test "namespace lookup collects results from all backends and merges deep hashes" do assert_equal({:long=>"long", :subformats=>{:long=>"long", :short=>"short"}, :short=>"short"}, I18n.t(:formats)) end test "namespace lookup collects results from all backends and lets leftmost backend take priority" do assert_equal({ :a => "A", :b => "B" }, I18n.t(:dates)) end test "namespace lookup with only the first backend returning a result" do assert_equal({ :one => '%{count}' }, I18n.t(:plural_1)) end test "pluralization still works" do assert_equal '1', I18n.t(:plural_1, :count => 1) assert_equal 'one', I18n.t(:plural_2, :count => 1) end test "bulk lookup collects results from all backends" do assert_equal ['Foo', 'Bar'], I18n.t([:foo, :bar]) assert_equal ['Foo', 'Bar', 'Bah'], I18n.t([:foo, :bar, :bah], :default => 'Bah') assert_equal [{ :long=>"long", :subformats=>{:long=>"long", :short=>"short"}, :short=>"short"}, {:one=>"one"}, "Bah"], I18n.t([:formats, :plural_2, :bah], :default => 'Bah') end test "store_translations options are not dropped while transferring to backend" do @first.expects(:store_translations).with(:foo, {:bar => :baz}, {:option => 'persists'}) I18n.backend.store_translations :foo, {:bar => :baz}, {:option => 'persists'} end test 'store should call initialize on all backends and return true if all initialized' do @first.send :init_translations @second.send :init_translations assert I18n.backend.initialized? end test 'store should call initialize on all backends and return false if one not initialized' do @first.reload! @second.send :init_translations assert !I18n.backend.initialized? end test 'should reload all backends' do @first.send :init_translations @second.send :init_translations I18n.backend.reload! assert !@first.initialized? assert !@second.initialized? end test 'should eager load all backends' do I18n.backend.eager_load! assert @first.initialized? assert @second.initialized? end test "falls back to other backends for nil values" do assert_nil @first.send(:translations)[:en][:fallback_bar] assert_equal 'Bar', @second.send(:translations)[:en][:fallback_bar] assert_equal 'Bar', I18n.t(:fallback_bar) end test 'should be able to get all translations of all backends merged together' do expected = { en: { foo: 'Foo', bar: 'Bar', formats: { short: 'short', long: 'long', subformats: { short: 'short', long: 'long' } }, plural_1: { one: "%{count}" }, plural_2: { one: 'one' }, dates: { a: 'A', b: 'B' }, fallback_bar: 'Bar' } } assert_equal expected, I18n.backend.send(:translations) end protected def backend(translations) backend = I18n::Backend::Simple.new translations.each { |locale, data| backend.store_translations(locale, data) } backend end end class I18nBackendChainWithKeyValueTest < I18n::TestCase def setup_backend!(subtrees = true) first = I18n::Backend::KeyValue.new({}, subtrees) first.store_translations(:en, :plural_1 => { :one => '%{count}' }) second = I18n::Backend::Simple.new second.store_translations(:en, :plural_2 => { :one => 'one' }) I18n.backend = I18n::Backend::Chain.new(first, second) end test "subtrees enabled: looks up pluralization translations from the first chained backend" do setup_backend! assert_equal '1', I18n.t(:plural_1, count: 1) end test "subtrees disabled: looks up pluralization translations from the first chained backend" do setup_backend!(false) assert_equal '1', I18n.t(:plural_1, count: 1) end test "subtrees enabled: looks up translations from the second chained backend" do setup_backend! assert_equal 'one', I18n.t(:plural_2, count: 1) end test "subtrees disabled: looks up translations from the second chained backend" do setup_backend!(false) assert_equal 'one', I18n.t(:plural_2, count: 1) end end if I18n::TestCase.key_value? i18n-1.14.1/test/backend/exceptions_test.rb000066400000000000000000000026271443717167300205140ustar00rootroot00000000000000require 'test_helper' class I18nBackendExceptionsTest < I18n::TestCase def setup super I18n.backend = I18n::Backend::Simple.new end test "throw message: MissingTranslation message from #translate includes the given scope and full key" do exception = catch(:exception) do I18n.t(:'baz.missing', :scope => :'foo.bar', :throw => true) end assert_equal "Translation missing: en.foo.bar.baz.missing", exception.message end test "exceptions: MissingTranslationData message from #translate includes the given scope and full key" do begin I18n.t(:'baz.missing', :scope => :'foo.bar', :raise => true) rescue I18n::MissingTranslationData => exception end assert_equal "Translation missing: en.foo.bar.baz.missing", exception.message end test "exceptions: MissingTranslationData message from #localize includes the given scope and full key" do begin I18n.l(Time.now, :format => :foo) rescue I18n::MissingTranslationData => exception end assert_equal "Translation missing: en.time.formats.foo", exception.message end test "exceptions: MissingInterpolationArgument message includes missing key, provided keys and full string" do exception = I18n::MissingInterpolationArgument.new('key', {:this => 'was given'}, 'string') assert_equal 'missing interpolation argument "key" in "string" ({:this=>"was given"} given)', exception.message end end i18n-1.14.1/test/backend/fallbacks_test.rb000066400000000000000000000375241443717167300202610ustar00rootroot00000000000000require 'test_helper' class I18nBackendFallbacksTranslateTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en', :buz => 'Buz in :en', :interpolate => 'Interpolate %{value}', :interpolate_count => 'Interpolate %{value} %{count}') store_translations(:de, :bar => 'Bar in :de', :baz => 'Baz in :de') store_translations(:'de-DE', :baz => 'Baz in :de-DE') store_translations(:'pt-BR', :baz => 'Baz in :pt-BR') end test "still returns an existing translation as usual" do assert_equal 'Foo in :en', I18n.t(:foo, :locale => :en) assert_equal 'Bar in :de', I18n.t(:bar, :locale => :de) assert_equal 'Baz in :de-DE', I18n.t(:baz, :locale => :'de-DE') end test "returns interpolated value if no key provided" do assert_equal 'Interpolate %{value}', I18n.t(:interpolate) end test "returns the :de translation for a missing :'de-DE' translation" do assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE') end test "keeps the count option when defaulting to a different key" do assert_equal 'Interpolate 5 10', I18n.t(:non_existent, default: :interpolate_count, count: 10, value: 5) end test "returns the :de translation for a missing :'de-DE' when :default is a String" do assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE', :default => "Default Bar") assert_equal "Default Bar", I18n.t(:missing_bar, :locale => :'de-DE', :default => "Default Bar") end test "returns the :de translation for a missing :'de-DE' when defaults is a Symbol (which exists in :en)" do assert_equal "Bar in :de", I18n.t(:bar, :locale => :'de-DE', :default => [:buz]) end test "returns the :'de-DE' default :baz translation for a missing :'de-DE' (which exists in :de)" do assert_equal "Baz in :de-DE", I18n.t(:bar, :locale => :'de-DE', :default => [:baz]) end test "returns the :de translation for a missing :'de-DE' when :default is a Proc" do assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE', :default => Proc.new { "Default Bar" }) assert_equal "Default Bar", I18n.t(:missing_bar, :locale => :'de-DE', :default => Proc.new { "Default Bar" }) end test "returns the :de translation for a missing :'de-DE' when :default is a Hash" do assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE', :default => {}) assert_equal({}, I18n.t(:missing_bar, :locale => :'de-DE', :default => {})) end test "returns the :de translation for a missing :'de-DE' when :default is nil" do assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE', :default => nil) assert_nil I18n.t(:missing_bar, :locale => :'de-DE', :default => nil) end test "returns the Translation missing: message if the default is also missing" do translation_missing_message = <<~MSG Translation missing. Options considered were: - de-DE.missing_bar - de-DE.missing_baz MSG assert_equal translation_missing_message.chomp, I18n.t(:missing_bar, :locale => :'de-DE', :default => [:missing_baz]) end test "returns the simple Translation missing: message when default is an empty Array" do assert_equal "Translation missing: de-DE.missing_bar", I18n.t(:missing_bar, :locale => :'de-DE', :default => []) end test "returns the :'de-DE' default :baz translation for a missing :'de-DE' when defaults contains Symbol" do assert_equal 'Baz in :de-DE', I18n.t(:missing_foo, :locale => :'de-DE', :default => [:baz, "Default Bar"]) end test "returns the defaults translation for a missing :'de-DE' when defaults contains a String or Proc before Symbol" do assert_equal "Default Bar", I18n.t(:missing_foo, :locale => :'de-DE', :default => [:missing_bar, "Default Bar", :baz]) assert_equal "Default Bar", I18n.t(:missing_foo, :locale => :'de-DE', :default => [:missing_bar, Proc.new { "Default Bar" }, :baz]) end test "returns the default translation for a missing :'de-DE' and existing :de when default is a Hash" do assert_equal 'Default 6 Bars', I18n.t(:missing_foo, :locale => :'de-DE', :default => [:missing_bar, {:other => "Default %{count} Bars"}, "Default Bar"], :count => 6) end test "returns the default translation for a missing :de translation even when default is a String when fallback is disabled" do assert_equal 'Default String', I18n.t(:foo, :locale => :de, :default => 'Default String', :fallback => false) end test "raises I18n::MissingTranslationData exception when fallback is disabled even when fallback translation exists" do assert_raises(I18n::MissingTranslationData) { I18n.t(:foo, :locale => :de, :fallback => false, :raise => true) } end test "raises I18n::MissingTranslationData exception when no translation was found" do assert_raises(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :en, :raise => true) } assert_raises(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :de, :raise => true) } end test "should ensure that default is not splitted on new line char" do assert_equal "Default \n Bar", I18n.t(:missing_bar, :default => "Default \n Bar") end test "should not raise error when enforce_available_locales is true, :'pt' is missing and default is a Symbol" do I18n.enforce_available_locales = true begin assert_equal 'Foo', I18n.t(:'model.attrs.foo', :locale => :'pt-BR', :default => [:'attrs.foo', "Foo"]) ensure I18n.enforce_available_locales = false end end test "returns fallback default given missing pluralization data" do assert_equal 'default', I18n.t(:missing_bar, count: 1, default: 'default') assert_equal 'default', I18n.t(:missing_bar, count: 0, default: 'default') end test "multi-threaded fallbacks" do I18n.fallbacks = [:en] thread = Thread.new do I18n.fallbacks = [:de] end begin thread.join assert_equal 'Bar in :en', I18n.t(:bar, :locale => :'pt-BR') ensure thread.exit I18n.fallbacks = I18n::Locale::Fallbacks.new end end end # See Issue #534 class I18nBackendFallbacksLocalizeTestWithDefaultLocale < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new I18n.enforce_available_locales = false I18n.fallbacks = [I18n.default_locale] store_translations(:en, time: { formats: { fallback: 'en fallback' } }) end test "falls back to default locale - Issue #534" do assert_equal 'en fallback', I18n.l(Time.now, format: :fallback, locale: "un-supported") end end # See Issue #536 class I18nBackendFallbacksWithCustomClass < I18n::TestCase class BackendWithFallbacks < I18n::Backend::Simple include I18n::Backend::Fallbacks end # Quacks like a fallback class class MyDefaultFallback def [](key) [:my_language] end end def setup super I18n.backend = BackendWithFallbacks.new I18n.enforce_available_locales = false I18n.fallbacks = MyDefaultFallback.new store_translations(:my_language, foo: 'customer foo') store_translations(:en, foo: 'english foo') end test "can use a default fallback object that doesn't inherit from I18n::Locale::Fallbacks" do assert_equal 'customer foo', I18n.t(:foo, locale: :en) assert_equal 'customer foo', I18n.t(:foo, locale: :nothing) end end # See Issue #546 class I18nBackendFallbacksLocalizeTestWithMultipleThreads < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new I18n.enforce_available_locales = false I18n.fallbacks = [I18n.default_locale] store_translations(:en, time: { formats: { fallback: 'en fallback' } }) end test "falls back to default locale - Issue #546" do Thread.new { assert_equal 'en fallback', I18n.l(Time.now, format: :fallback, locale: "un-supported") }.join end end # See Issue #590 class I18nBackendFallbacksSymbolResolveRestartsLookupAtOriginalLocale < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new I18n.enforce_available_locales = false I18n.fallbacks = [:root] store_translations(:ak, 'calendars' => { 'gregorian' => { 'months' => { 'format' => { 'abbreviated' => { 1 => 'S-Ɔ' # Other months omitted for brevity } } } } }) store_translations(:root, 'calendars' => { 'gregorian' => { 'months' => { 'format' => { 'abbreviated' => :"calendars.gregorian.months.format.wide", 'wide' => { 1 => 'M01' # Other months omitted for brevity } }, 'stand-alone' => { 'abbreviated' => :"calendars.gregorian.months.format.abbreviated" } } } }) end test 'falls back to original locale when symbol resolved at fallback locale' do assert_equal({ 1 => 'S-Ɔ' }, I18n.t('calendars.gregorian.months.stand-alone.abbreviated', locale: :"ak-GH")) end end # See Issue #617 class RegressionTestFor617 < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new I18n.enforce_available_locales = false I18n.fallbacks = {:en=>[:en], :"en-US"=>[:"en-US", :en]} I18n.locale = :'en-US' store_translations(:"en-US", {}) store_translations(:en, :activerecord=>{:models=>{:product=>{:one=>"Product", :other=>"Products"}, :"product/ticket"=>{:one=>"Ticket", :other=>"Tickets"}}}) end test 'model scope resolution' do defaults = [:product, "Ticket"] options = {:scope=>[:activerecord, :models], :count=>1, :default=> defaults} assert_equal("Ticket", I18n.t(:"product/ticket", **options)) end end class I18nBackendFallbacksLocalizeTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new store_translations(:en, :date => { :formats => { :en => 'en' }, :day_names => %w(Sunday) }) store_translations(:de, :date => { :formats => { :de => 'de' }, :day_names => %w(Sunday) }) end test "still uses an existing format as usual" do assert_equal 'en', I18n.l(Date.today, :format => :en, :locale => :en) end test "looks up and uses a fallback locale's format for a key missing in the given locale" do assert_equal 'de', I18n.l(Date.today, :format => :de, :locale => :'de-DE') end test "still uses an existing day name translation as usual" do assert_equal 'Sunday', I18n.l(Date.new(2010, 1, 3), :format => '%A', :locale => :en) end test "uses a fallback locale's translation for a key missing in the given locale" do assert_equal 'Sunday', I18n.l(Date.new(2010, 1, 3), :format => '%A', :locale => :'de-DE') end end class I18nBackendFallbacksWithChainTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end class Chain < I18n::Backend::Chain include I18n::Backend::Fallbacks end def setup super backend = Backend.new backend.store_translations(:de, :foo => 'FOO') backend.store_translations(:'pt-BR', :foo => 'Baz in :pt-BR') I18n.backend = Chain.new(I18n::Backend::Simple.new, backend) end test "falls back from de-DE to de when there is no translation for de-DE available" do assert_equal 'FOO', I18n.t(:foo, :locale => :'de-DE') end test "exists? falls back from de-DE to de given a key missing from the given locale" do assert_equal true, I18n.exists?(:foo, :locale => :'de-DE') end test "exists? should return false when fallback disabled given a key missing from the given locale" do assert_equal false, I18n.exists?(:foo, :locale => :'de-DE', fallback: false) end test "falls back from de-DE to de when there is no translation for de-DE available when using arrays, too" do assert_equal ['FOO', 'FOO'], I18n.t([:foo, :foo], :locale => :'de-DE') end test "should not raise error when enforce_available_locales is true, :'pt' is missing and default is a Symbol" do I18n.enforce_available_locales = true begin assert_equal 'Foo', I18n.t(:'model.attrs.foo', :locale => :'pt-BR', :default => [:'attrs.foo', "Foo"]) ensure I18n.enforce_available_locales = false end end end class I18nBackendFallbacksExistsTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en') store_translations(:de, :bar => 'Bar in :de') store_translations(:'de-DE', :baz => 'Baz in :de-DE') end test "exists? given an existing key will return true" do assert_equal true, I18n.exists?(:foo) end test "exists? given a non-existing key will return false" do assert_equal false, I18n.exists?(:bogus) end test "exists? given an existing key and an existing locale will return true" do assert_equal true, I18n.exists?(:foo, :en) assert_equal true, I18n.exists?(:bar, :de) end test "exists? given a non-existing key and an existing locale will return false" do assert_equal false, I18n.exists?(:bogus, :en) assert_equal false, I18n.exists?(:bogus, :de) end test "exists? should return true given a key which is missing from the given locale and exists in a fallback locale" do assert_equal true, I18n.exists?(:bar, :de) assert_equal true, I18n.exists?(:bar, :'de-DE') end test "exists? should return false given a key which is missing from the given locale and all its fallback locales" do assert_equal false, I18n.exists?(:baz, :de) assert_equal false, I18n.exists?(:bogus, :'de-DE') end test "exists? should return false when fallback is disabled given a key which is missing from the given locale" do assert_equal true, I18n.exists?(:bar, :'de-DE') assert_equal false, I18n.exists?(:bar, :'de-DE', fallback: false) assert_equal false, I18n.exists?(:bar, :'de-DE-XX', fallback: false) end end class I18nBackendOnFallbackHookTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks attr :fallback_collector private def on_fallback(*args) @fallback_collector ||= [] @fallback_collector << args end end def setup super I18n.backend = Backend.new I18n.fallbacks = I18n::Locale::Fallbacks.new(de: :en) store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en') store_translations(:de, :bar => 'Bar in :de') store_translations(:"de-DE", :baz => 'Baz in :"de-DE"') end test "on_fallback should be called when fallback happens" do assert_equal [:"de-DE", :de, :en], I18n.fallbacks[:"de-DE"] assert_equal 'Baz in :"de-DE"', I18n.t(:baz, locale: :'de-DE') assert_equal 'Bar in :de', I18n.t(:bar, locale: :'de-DE') assert_equal 'Foo in :en', I18n.t(:foo, locale: :'de-DE') assert_equal [:'de-DE', :de, :bar, {}], I18n.backend.fallback_collector[0] assert_equal [:'de-DE', :en, :foo, {}], I18n.backend.fallback_collector[1] end test "on_fallback should not be called when use a String locale" do assert_equal 'Bar in :de', I18n.t("bar", locale: "de") assert I18n.backend.fallback_collector.nil? end end i18n-1.14.1/test/backend/interpolation_compiler_test.rb000066400000000000000000000107701443717167300231120ustar00rootroot00000000000000require 'test_helper' class InterpolationCompilerTest < I18n::TestCase Compiler = I18n::Backend::InterpolationCompiler::Compiler def compile_and_interpolate(str, values = {}) Compiler.compile_if_an_interpolation(str).i18n_interpolate(values) end def assert_escapes_interpolation_key(expected, malicious_str) assert_equal(expected, Compiler.send(:escape_key_sym, malicious_str)) end def test_escape_key_properly_escapes assert_escapes_interpolation_key ':"\""', '"' assert_escapes_interpolation_key ':"\\\\"', '\\' assert_escapes_interpolation_key ':"\\\\\""', '\\"' assert_escapes_interpolation_key ':"\#{}"', '#{}' assert_escapes_interpolation_key ':"\\\\\#{}"', '\#{}' end def assert_escapes_plain_string(expected, plain_str) assert_equal expected, Compiler.send(:escape_plain_str, plain_str) end def test_escape_plain_string_properly_escapes assert_escapes_plain_string '\\"', '"' assert_escapes_plain_string '\'', '\'' assert_escapes_plain_string '\\#', '#' assert_escapes_plain_string '\\#{}', '#{}' assert_escapes_plain_string '\\\\\\"','\\"' end def test_non_interpolated_strings_or_arrays_dont_get_compiled ['abc', '\\{a}}', '{a}}', []].each do |obj| Compiler.compile_if_an_interpolation(obj) assert_equal false, obj.respond_to?(:i18n_interpolate) end end def test_interpolated_string_gets_compiled assert_equal '-A-', compile_and_interpolate('-%{a}-', :a => 'A') end def assert_handles_key(str, key) assert_equal 'A', compile_and_interpolate(str, key => 'A') end def test_compiles_fancy_keys assert_handles_key('%{\}', :'\\' ) assert_handles_key('%{#}', :'#' ) assert_handles_key('%{#{}', :'#{' ) assert_handles_key('%{#$SAFE}', :'#$SAFE') assert_handles_key('%{\000}', :'\000' ) assert_handles_key('%{\'}', :'\'' ) assert_handles_key('%{\'\'}', :'\'\'' ) assert_handles_key('%{a.b}', :'a.b' ) assert_handles_key('%{ }', :' ' ) assert_handles_key('%{:}', :':' ) assert_handles_key("%{:''}", :":''" ) assert_handles_key('%{:"}', :':"' ) end def test_str_containing_only_escaped_interpolation_is_handled_correctly assert_equal 'abc %{x}', compile_and_interpolate('abc %%{x}') end def test_handles_weird_strings assert_equal '#{} a', compile_and_interpolate('#{} %{a}', :a => 'a') assert_equal '"#{abc}"', compile_and_interpolate('"#{ab%{a}c}"', :a => '' ) assert_equal 'a}', compile_and_interpolate('%{{a}}', :'{a' => 'a') assert_equal '"', compile_and_interpolate('"%{a}', :a => '' ) assert_equal 'a%{a}', compile_and_interpolate('%{a}%%{a}', :a => 'a') assert_equal '%%{a}', compile_and_interpolate('%%%{a}') assert_equal '\";eval("a")', compile_and_interpolate('\";eval("%{a}")', :a => 'a') assert_equal '\";eval("a")', compile_and_interpolate('\";eval("a")%{a}', :a => '' ) assert_equal "\na", compile_and_interpolate("\n%{a}", :a => 'a') end def test_raises_exception_when_argument_is_missing assert_raises(I18n::MissingInterpolationArgument) do compile_and_interpolate('%{first} %{last}', :first => 'first') end end def test_custom_missing_interpolation_argument_handler old_handler = I18n.config.missing_interpolation_argument_handler I18n.config.missing_interpolation_argument_handler = lambda do |key, values, string| "missing key is #{key}, values are #{values.inspect}, given string is '#{string}'" end assert_equal %|first missing key is last, values are {:first=>"first"}, given string is '%{first} %{last}'|, compile_and_interpolate('%{first} %{last}', :first => 'first') ensure I18n.config.missing_interpolation_argument_handler = old_handler end end class I18nBackendInterpolationCompilerTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::InterpolationCompiler end include I18n::Tests::Interpolation def setup I18n.backend = Backend.new super end # pre-compile default strings to make sure we are testing I18n::Backend::InterpolationCompiler def interpolate(*args) options = args.last.kind_of?(Hash) ? args.last : {} if default_str = options[:default] I18n::Backend::InterpolationCompiler::Compiler.compile_if_an_interpolation(default_str) end super end end i18n-1.14.1/test/backend/key_value_test.rb000066400000000000000000000075211443717167300203150ustar00rootroot00000000000000require 'test_helper' class I18nBackendKeyValueTest < I18n::TestCase def setup_backend!(subtree=true) I18n.backend = I18n::Backend::KeyValue.new({}, subtree) store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' }) end def assert_flattens(expected, nested, escape=true, subtree=true) assert_equal expected, I18n.backend.flatten_translations("en", nested, escape, subtree) end test "hash flattening works" do setup_backend! assert_flattens( {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}, :"b.f" => {:x=>"x"}, :"b.c"=>"c", :"b.f.x"=>"x", :"b.d"=>"d"}, {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}} ) assert_flattens({:a=>{:b =>['a', 'b']}, :"a.b"=>['a', 'b']}, {:a=>{:b =>['a', 'b']}}) assert_flattens({:"a\001b" => "c"}, {:"a.b" => "c"}) assert_flattens({:"a.b"=>['a', 'b']}, {:a=>{:b =>['a', 'b']}}, true, false) assert_flattens({:"a.b" => "c"}, {:"a.b" => "c"}, false) end test "store_translations supports numeric keys" do setup_backend! store_translations(:en, 1 => 'foo') assert_equal 'foo', I18n.t('1') assert_equal 'foo', I18n.t(1) assert_equal 'foo', I18n.t(:'1') end test "store_translations handle subtrees by default" do setup_backend! assert_equal({ :bar => 'bar', :baz => 'baz' }, I18n.t("foo")) end test "store_translations merge subtrees accordingly" do setup_backend! store_translations(:en, :foo => { :baz => "BAZ"}) assert_equal('BAZ', I18n.t("foo.baz")) assert_equal({ :bar => 'bar', :baz => 'BAZ' }, I18n.t("foo")) end test "store_translations does not handle subtrees if desired" do setup_backend!(false) assert_raises I18n::MissingTranslationData do I18n.t("foo", :raise => true) end end test 'initialized? checks that a store is available' do setup_backend! I18n.backend.reload! assert_equal I18n.backend.initialized?, true end test 'translations gets the translations from the store' do setup_backend! I18n.backend.send(:translations) expected = { :en => {:foo => { :bar => 'bar', :baz => 'baz' }} } assert_equal expected, translations end test "subtrees enabled: given incomplete pluralization data it raises I18n::InvalidPluralizationData" do setup_backend! store_translations(:en, :bar => { :one => "One" }) assert_raises(I18n::InvalidPluralizationData) { I18n.t(:bar, :count => 2) } end test "subtrees disabled: given incomplete pluralization data it returns an error message" do setup_backend!(false) store_translations(:en, :bar => { :one => "One" }) assert_equal "Translation missing: en.bar", I18n.t(:bar, :count => 2) end test "translate handles subtrees for pluralization" do setup_backend!(false) store_translations(:en, :bar => { :one => "One" }) assert_equal("One", I18n.t("bar", :count => 1)) end test "subtrees enabled: returns localized string given missing pluralization data" do setup_backend!(true) assert_equal 'bar', I18n.t("foo.bar", count: 1) end test "subtrees disabled: returns localized string given missing pluralization data" do setup_backend!(false) assert_equal 'bar', I18n.t("foo.bar", count: 1) end test "subtrees enabled: Returns fallback default given missing pluralization data" do setup_backend!(true) I18n.backend.extend I18n::Backend::Fallbacks assert_equal 'default', I18n.t(:missing_bar, count: 1, default: 'default') assert_equal 'default', I18n.t(:missing_bar, count: 0, default: 'default') end test "subtrees disabled: Returns fallback default given missing pluralization data" do setup_backend!(false) I18n.backend.extend I18n::Backend::Fallbacks assert_equal 'default', I18n.t(:missing_bar, count: 1, default: 'default') assert_equal 'default', I18n.t(:missing_bar, count: 0, default: 'default') end end if I18n::TestCase.key_value? i18n-1.14.1/test/backend/lazy_loadable_test.rb000066400000000000000000000163641443717167300211400ustar00rootroot00000000000000require 'test_helper' class I18nBackendLazyLoadableTest < I18n::TestCase def setup super @lazy_mode_backend = I18n::Backend::LazyLoadable.new(lazy_load: true) @eager_mode_backend = I18n::Backend::LazyLoadable.new(lazy_load: false) I18n.load_path = [File.join(locales_dir, '/en.yml'), File.join(locales_dir, '/en.yaml'), File.join(locales_dir, '/fr.yml')] end test "lazy mode: only loads translations for current locale" do with_lazy_mode do @backend.reload! assert_nil translations I18n.with_locale(:en) { I18n.t("foo.bar") } assert_equal({ en: { foo: { bar: "baz" }}}, translations) end end test "lazy mode: merges translations for current locale with translations already existing in memory" do with_lazy_mode do @backend.reload! I18n.with_locale(:en) { I18n.t("foo.bar") } assert_equal({ en: { foo: { bar: "baz" }}}, translations) I18n.with_locale(:fr) { I18n.t("animal.dog") } assert_equal({ en: { foo: { bar: "baz" } }, fr: { animal: { dog: "chien" } } }, translations) end end test "lazy mode: #initialized? responds based on whether current locale is initialized" do with_lazy_mode do @backend.reload! I18n.with_locale(:en) do refute_predicate @backend, :initialized? I18n.t("foo.bar") assert_predicate @backend, :initialized? end I18n.with_locale(:fr) do refute_predicate @backend, :initialized? end end end test "lazy mode: reload! uninitializes all locales" do with_lazy_mode do I18n.with_locale(:en) { I18n.t("foo.bar") } I18n.with_locale(:fr) { I18n.t("animal.dog") } @backend.reload! I18n.with_locale(:en) do refute_predicate @backend, :initialized? end I18n.with_locale(:fr) do refute_predicate @backend, :initialized? end end end test "lazy mode: eager_load! raises UnsupportedMethod exception" do with_lazy_mode do exception = assert_raises(I18n::UnsupportedMethod) { @backend.eager_load! } expected_msg = "I18n::Backend::LazyLoadable does not support the #eager_load! method. Cannot eager load translations because backend was configured with lazy_load: true." assert_equal expected_msg, exception.message end end test "lazy mode: loads translations from files that start with current locale identifier" do with_lazy_mode do file_contents = { en: { alice: "bob" } }.to_yaml invalid_files = [ { filename: ['translation', '.yml'] }, # No locale identifier { filename: ['translation', '.unsupported'] }, # No locale identifier and unsupported extension ] invalid_files.each do |file| with_translation_file_in_load_path(file[:filename], file[:dir], file_contents) do I18n.with_locale(:en) { I18n.t("foo.bar") } assert_equal({ en: { foo: { bar: "baz" }}}, translations) end end valid_files = [ { filename: ['en_translation', '.yml'] }, # Contains locale identifier with correct demarcation, and supported extension { filename: ['en_', '.yml'] }, # Path component matches locale identifier exactly ] valid_files.each do |file| with_translation_file_in_load_path(file[:filename], file[:dir], file_contents) do I18n.with_locale(:en) { I18n.t("foo.bar") } assert_equal({ en: { foo: { bar: "baz" }, alice: "bob" }}, translations) end end end end test "lazy mode: files with unsupported extensions raise UnknownFileType error" do with_lazy_mode do file_contents = { en: { alice: "bob" } }.to_yaml filename = ['en_translation', '.unsupported'] # Correct locale identifier, but unsupported extension with_translation_file_in_load_path(filename, nil, file_contents) do assert_raises(I18n::UnknownFileType) { I18n.t("foo.bar") } end end end test "lazy mode: #available_locales returns all locales available from load path irrespective of current locale" do with_lazy_mode do I18n.with_locale(:en) { assert_equal [:en, :fr], @backend.available_locales } I18n.with_locale(:fr) { assert_equal [:en, :fr], @backend.available_locales } end end test "lazy mode: raises error if translations loaded don't correspond to locale extracted from filename" do filename = ["en_", ".yml"] file_contents = { fr: { dog: "chien" } }.to_yaml with_lazy_mode do with_translation_file_in_load_path(filename, nil, file_contents) do |file_path| exception = assert_raises(I18n::InvalidFilenames) { I18n.t("foo.bar") } expected_message = /#{Regexp.escape(file_path)} can only load translations for "en"\. Found translations for: \[\:fr\]/ assert_match expected_message, exception.message end end end test "lazy mode: raises error if translations for more than one locale are loaded from a single file" do filename = ["en_", ".yml"] file_contents = { en: { alice: "bob" }, fr: { dog: "chien" }, de: { cat: 'katze' } }.to_yaml with_lazy_mode do with_translation_file_in_load_path(filename, nil, file_contents) do |file_path| exception = assert_raises(I18n::InvalidFilenames) { I18n.t("foo.bar") } expected_message = /#{Regexp.escape(file_path)} can only load translations for "en"\. Found translations for: \[\:fr\, \:de\]/ assert_match expected_message, exception.message end end end test "lazy mode: #lookup lazy loads translations for supplied locale" do with_lazy_mode do @backend.reload! assert_nil translations I18n.with_locale(:en) do assert_equal "chien", @backend.lookup(:fr, "animal.dog") end assert_equal({ fr: { animal: { dog: "chien" } } }, translations) end end test "eager mode: load all translations, irrespective of locale" do with_eager_mode do @backend.reload! assert_nil translations I18n.with_locale(:en) { I18n.t("foo.bar") } assert_equal({ en: { foo: { bar: "baz" } }, fr: { animal: { dog: "chien" } } }, translations) end end test "eager mode: raises error if locales loaded cannot be extracted from load path names" do with_eager_mode do @backend.reload! contents = { de: { cat: 'katze' } }.to_yaml with_translation_file_in_load_path(['fr_translation', '.yml'], nil, contents) do |file_path| exception = assert_raises(I18n::InvalidFilenames) { I18n.t("foo.bar") } expected_message = /#{Regexp.escape(file_path)} can only load translations for "fr"\. Found translations for: \[\:de\]/ assert_match expected_message, exception.message end end end private def with_lazy_mode @backend = I18n.backend = @lazy_mode_backend yield end def with_eager_mode @backend = I18n.backend = @eager_mode_backend yield end def with_translation_file_in_load_path(name, tmpdir, file_contents) @backend.reload! path_to_dir = FileUtils.mkdir_p(File.join(Dir.tmpdir, tmpdir)).first if tmpdir locale_file = Tempfile.new(name, path_to_dir) locale_file.write(file_contents) locale_file.rewind I18n.load_path << locale_file.path yield(locale_file.path) I18n.load_path.delete(locale_file.path) end end i18n-1.14.1/test/backend/memoize_test.rb000066400000000000000000000045271443717167300200010ustar00rootroot00000000000000require 'test_helper' require 'backend/simple_test' class I18nBackendMemoizeTest < I18nBackendSimpleTest module MemoizeSpy attr_accessor :spy_calls def available_locales self.spy_calls = (self.spy_calls || 0) + 1 super end end class MemoizeBackend < I18n::Backend::Simple include MemoizeSpy include I18n::Backend::Memoize end def setup super I18n.backend = MemoizeBackend.new end def test_memoizes_available_locales I18n.backend.spy_calls = 0 assert_equal I18n.available_locales, I18n.available_locales assert_equal 1, I18n.backend.spy_calls end def test_resets_available_locales_on_reload! I18n.available_locales I18n.backend.spy_calls = 0 I18n.reload! assert_equal I18n.available_locales, I18n.available_locales assert_equal 1, I18n.backend.spy_calls end def test_resets_available_locales_on_store_translations I18n.available_locales I18n.backend.spy_calls = 0 I18n.backend.store_translations(:copa, :ca => :bana) assert_equal I18n.available_locales, I18n.available_locales assert I18n.available_locales.include?(:copa) assert_equal 1, I18n.backend.spy_calls end def test_eager_load I18n.eager_load! I18n.backend.spy_calls = 0 assert_equal I18n.available_locales, I18n.available_locales assert_equal 0, I18n.backend.spy_calls end module TestLookup def lookup(locale, key, scope = [], options = {}) keys = I18n.normalize_keys(locale, key, scope, options[:separator]) keys.inspect end end def test_lookup_concurrent_consistency backend_impl = Class.new(I18n::Backend::Simple) do include TestLookup include I18n::Backend::Memoize end backend = backend_impl.new memoized_lookup = backend.send(:memoized_lookup) assert_equal "[:foo, :scoped, :sample]", backend.translate('foo', scope = [:scoped, :sample]) 30.times.inject([]) do |memo, i| memo << Thread.new do backend.translate('bar', scope); backend.translate(:baz, scope) end end.each(&:join) memoized_lookup = backend.send(:memoized_lookup) puts memoized_lookup.inspect if $VERBOSE assert_equal 3, memoized_lookup.size, "NON-THREAD-SAFE lookup memoization backend: #{memoized_lookup.class}" # if a plain Hash is used might eventually end up in a weird (inconsistent) state end end i18n-1.14.1/test/backend/metadata_test.rb000066400000000000000000000032021443717167300201010ustar00rootroot00000000000000require 'test_helper' class I18nBackendMetadataTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Metadata end def setup super I18n.backend = Backend.new store_translations(:en, :foo => 'Hi %{name}') end test "translation strings carry metadata" do translation = I18n.t(:foo, :name => 'David') assert translation.respond_to?(:translation_metadata) assert translation.translation_metadata.is_a?(Hash) end test "translate adds the locale to metadata on Strings" do assert_equal :en, I18n.t(:foo, :name => 'David', :locale => :en).translation_metadata[:locale] end test "translate adds the key to metadata on Strings" do assert_equal :foo, I18n.t(:foo, :name => 'David').translation_metadata[:key] end test "translate adds the default to metadata on Strings" do assert_equal 'bar', I18n.t(:foo, :default => 'bar', :name => '').translation_metadata[:default] end test "translation adds the interpolation values to metadata on Strings" do assert_equal({:name => 'David'}, I18n.t(:foo, :name => 'David').translation_metadata[:values]) end test "interpolation adds the original string to metadata on Strings" do assert_equal('Hi %{name}', I18n.t(:foo, :name => 'David').translation_metadata[:original]) end test "pluralization adds the count to metadata on Strings" do assert_equal(1, I18n.t(:missing, :count => 1, :default => { :one => 'foo' }).translation_metadata[:count]) end test "metadata works with frozen values" do assert_equal(1, I18n.t(:missing, :count => 1, :default => 'foo'.freeze).translation_metadata[:count]) end end i18n-1.14.1/test/backend/pluralization_fallback_test.rb000066400000000000000000000047321443717167300230460ustar00rootroot00000000000000require 'test_helper' class I18nBackendPluralizationFallbackTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization include I18n::Backend::Fallbacks end def setup super I18n.default_locale = :'en' I18n.backend = Backend.new store_translations('en', cat: { zero: 'cat', one: 'cat', other: 'cats' }) store_translations('en-US', cat: { zero: 'no cat', one: nil, other: 'lots of cats' }) store_translations('ru', cat: { one: 'кот', few: 'кошек', many: 'кошка', other: 'кошек' }) # probably not a real locale but just to demonstrate store_translations('ru-US', cat: { one: nil, few: nil, many: nil, other: nil }) store_translations('ru', i18n: { plural: { rule: russian_rule }}) end test "fallbacks: nils are ignored and fallback is applied" do assert_equal "no cat", I18n.t("cat", count: 0, locale: "en-US") assert_equal "cat", I18n.t("cat", count: 0, locale: "en") assert_equal "cat", I18n.t("cat", count: 1, locale: "en-US") assert_equal "cat", I18n.t("cat", count: 1, locale: "en") assert_equal "lots of cats", I18n.t("cat", count: 2, locale: "en-US") assert_equal "cats", I18n.t("cat", count: 2, locale: "en") end test "fallbacks: nils are ignored and fallback is applied, with custom rule" do # more specs: https://github.com/svenfuchs/rails-i18n/blob/master/spec/unit/pluralization/east_slavic.rb assert_equal "кошка", I18n.t("cat", count: 0, locale: "ru") assert_equal "кошка", I18n.t("cat", count: 0, locale: "ru-US") assert_equal "кот", I18n.t("cat", count: 1, locale: "ru") assert_equal "кот", I18n.t("cat", count: 1, locale: "ru-US") assert_equal "кошек", I18n.t("cat", count: 2, locale: "ru") assert_equal "кошек", I18n.t("cat", count: 2, locale: "ru-US") assert_equal "кошек", I18n.t("cat", count: 1.5, locale: "ru") assert_equal "кошек", I18n.t("cat", count: 1.5, locale: "ru-US") end private # copied from https://github.com/svenfuchs/rails-i18n/blob/master/lib/rails_i18n/common_pluralizations/east_slavic.rb def russian_rule lambda do |n| n ||= 0 mod10 = n % 10 mod100 = n % 100 if mod10 == 1 && mod100 != 11 :one elsif (2..4).include?(mod10) && !(12..14).include?(mod100) :few elsif mod10 == 0 || (5..9).include?(mod10) || (11..14).include?(mod100) :many else :other end end end end i18n-1.14.1/test/backend/pluralization_scope_test.rb000066400000000000000000000022261443717167300224140ustar00rootroot00000000000000require 'test_helper' class I18nBackendPluralizationScopeTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization include I18n::Backend::Fallbacks end def setup super I18n.default_locale = :'en' I18n.backend = Backend.new translations = { i18n: { plural: { keys: [:one, :other], rule: lambda { |n| n == 1 ? :one : :other }, } }, activerecord: { models: { my_model: { one: 'one model', other: 'more models', some_other_key: { key: 'value' } } } } } store_translations('en', translations) end test "pluralization picks :other for 2" do args = { scope: [:activerecord, :models], count: 2, default: ["My model"] } assert_equal 'more models', I18n.translate(:my_model, **args) end test "pluralization picks :one for 1" do args = { scope: [:activerecord, :models], count: 1, default: ["My model"] } assert_equal 'one model', I18n.translate(:my_model, **args) end end i18n-1.14.1/test/backend/pluralization_test.rb000066400000000000000000000077141443717167300212320ustar00rootroot00000000000000require 'test_helper' class I18nBackendPluralizationTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization include I18n::Backend::Fallbacks end def setup super I18n.backend = Backend.new @rule = lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : n == 0 || (2..10).include?(n % 100) ? :few : (11..19).include?(n % 100) ? :many : :other } store_translations(:xx, :i18n => { :plural => { :rule => @rule } }) @entry = { :"0" => 'none', :"1" => 'single', :one => 'one', :few => 'few', :many => 'many', :other => 'other' } @entry_with_zero = @entry.merge( { :zero => 'zero' } ) end test "pluralization picks a pluralizer from :'i18n.pluralize'" do assert_equal @rule, I18n.backend.send(:pluralizer, :xx) end test "pluralization picks the explicit 1 rule for count == 1, the explicit rule takes priority over the matching :one rule" do assert_equal 'single', I18n.t(:count => 1, :default => @entry, :locale => :xx) assert_equal 'single', I18n.t(:count => 1.0, :default => @entry, :locale => :xx) end test "pluralization picks :one for 1, since in this case that is the matching rule for 1 (when there is no explicit 1 rule)" do @entry.delete(:"1") assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :xx) end test "pluralization picks :few for 2" do assert_equal 'few', I18n.t(:count => 2, :default => @entry, :locale => :xx) end test "pluralization picks :many for 11" do assert_equal 'many', I18n.t(:count => 11, :default => @entry, :locale => :xx) end test "pluralization picks zero for 0 if the key is contained in the data" do assert_equal 'zero', I18n.t(:count => 0, :default => @entry_with_zero, :locale => :xx) end test "pluralization picks explicit 0 rule for count == 0, since the explicit rule takes priority over the matching :few rule" do assert_equal 'none', I18n.t(:count => 0, :default => @entry, :locale => :xx) assert_equal 'none', I18n.t(:count => 0.0, :default => @entry, :locale => :xx) assert_equal 'none', I18n.t(:count => -0, :default => @entry, :locale => :xx) end test "pluralization picks :few for 0 (when there is no explicit 0 rule)" do @entry.delete(:"0") assert_equal 'few', I18n.t(:count => 0, :default => @entry, :locale => :xx) end test "pluralization does Lateral Inheritance to :other to cover missing data" do @entry.delete(:many) assert_equal 'other', I18n.t(:count => 11, :default => @entry, :locale => :xx) end test "pluralization picks one for 1 if the entry has attributes hash on unknown locale" do @entry[:attributes] = { :field => 'field', :second => 'second' } assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :pirate) end test "Nested keys within pluralization context" do store_translations(:xx, :stars => { one: "%{count} star", other: "%{count} stars", special: { one: "%{count} special star", other: "%{count} special stars", } } ) assert_equal "1 star", I18n.t('stars', count: 1, :locale => :xx) assert_equal "20 stars", I18n.t('stars', count: 20, :locale => :xx) assert_equal "1 special star", I18n.t('stars.special', count: 1, :locale => :xx) assert_equal "20 special stars", I18n.t('stars.special', count: 20, :locale => :xx) end test "Fallbacks can pick up rules from fallback locales, too" do assert_equal @rule, I18n.backend.send(:pluralizer, :'xx-XX') end test "linked lookup works with pluralization backend" do I18n.backend.store_translations(:xx, { :automobiles => :autos, :autos => :cars, :cars => { :porsche => { :one => "I have %{count} Porsche 🚗", :other => "I have %{count} Porsches 🚗" } } }) assert_equal "I have 1 Porsche 🚗", I18n.t(:'automobiles.porsche', count: 1, :locale => :xx) assert_equal "I have 20 Porsches 🚗", I18n.t(:'automobiles.porsche', count: 20, :locale => :xx) end end i18n-1.14.1/test/backend/simple_test.rb000066400000000000000000000217601443717167300176230ustar00rootroot00000000000000require 'test_helper' class I18nBackendSimpleTest < I18n::TestCase def setup super I18n.backend = I18n::Backend::Simple.new I18n.load_path = [locales_dir + '/en.yml'] end # useful because this way we can use the backend with no key for interpolation/pluralization test "simple backend translate: given nil as a key it still interpolations the default value" do assert_equal "Hi David", I18n.t(nil, :default => "Hi %{name}", :name => "David") end test "simple backend translate: given true as a key" do store_translations :en, available: { true => "Yes", false => "No" } assert_equal "Yes", I18n.t(:available)[true] assert_equal "No", I18n.t(:available)[false] end test "simple backend translate: given integer as a key" do store_translations :en, available: { -1 => "Possibly", 0 => "Maybe", 1 => "Yes", 2 => "No", 3 => "Never" } assert_equal "Possibly", I18n.t(:available)[-1] assert_equal "Possibly", I18n.t('available.-1') assert_equal "Maybe", I18n.t(:available)[0] assert_equal "Maybe", I18n.t('available.0') assert_equal "Yes", I18n.t(:available)[1] assert_equal "Yes", I18n.t('available.1') assert_equal "No", I18n.t(:available)[2] assert_equal "No", I18n.t('available.2') assert_equal "Never", I18n.t(:available)[3] assert_equal "Never", I18n.t('available.+3') end test 'simple backend translate: given integer with a leading positive/negative sign' do store_translations :en, available: { -1 => "No", 0 => "Maybe", 1 => "Yes" } assert_equal 'No', I18n.t(:available)[-1] assert_equal 'No', I18n.t('available.-1') assert_equal 'Maybe', I18n.t(:available)[+0] assert_equal 'Maybe', I18n.t(:available)[-0] assert_equal 'Maybe', I18n.t('available.-0') assert_equal 'Maybe', I18n.t('available.+0') assert_equal 'Yes', I18n.t(:available)[+1] assert_equal 'Yes', I18n.t('available.+1') end test 'simple backend translate: given integer with a lead zero as a key' do store_translations :en, available: { '01' => 'foo' } assert_equal 'foo', I18n.t(:available)[:'01'] assert_equal 'foo', I18n.t('available.01') end test "simple backend translate: symbolize keys in hash" do store_translations :en, nested_hashes_in_array: { hello: "world" } assert_equal "world", I18n.t('nested_hashes_in_array.hello') assert_equal "world", I18n.t('nested_hashes_in_array')[:hello] end test "simple backend translate: symbolize keys in array" do store_translations :en, nested_hashes_in_array: [ { hello: "world" } ] I18n.t('nested_hashes_in_array').each do |val| assert_equal "world", val[:hello] end end # loading translations test "simple load_translations: given an unknown file type it raises I18n::UnknownFileType" do assert_raises(I18n::UnknownFileType) { I18n.backend.load_translations("#{locales_dir}/en.xml") } end test "simple load_translations: given a YAML file name with yaml extension does not raise anything" do assert_nothing_raised { I18n.backend.load_translations("#{locales_dir}/en.yaml") } end test "simple load_translations: given a JSON file name with yaml extension does not raise anything" do assert_nothing_raised { I18n.backend.load_translations("#{locales_dir}/en.json") } end test "simple load_translations: given a Ruby file name it does not raise anything" do assert_nothing_raised { I18n.backend.load_translations("#{locales_dir}/en.rb") } end test "simple load_translations: given no argument, it uses I18n.load_path" do I18n.backend.load_translations assert_equal({ :en => { :foo => { :bar => 'baz' } } }, I18n.backend.send(:translations)) end test "simple load_rb: loads data from a Ruby file" do data, _ = I18n.backend.send(:load_rb, "#{locales_dir}/en.rb") assert_equal({ :en => { :fuh => { :bah => 'bas' } } }, data) end test "simple load_yml: loads data from a YAML file" do data, _ = I18n.backend.send(:load_yml, "#{locales_dir}/en.yml") if ::YAML.respond_to?(:unsafe_load_file) assert_equal({ :en => { :foo => { :bar => 'baz' } } }, data) assert_predicate data.dig(:en, :foo, :bar), :frozen? else assert_equal({ 'en' => { 'foo' => { 'bar' => 'baz' } } }, data) end end test "simple load_json: loads data from a JSON file" do data, _ = I18n.backend.send(:load_json, "#{locales_dir}/en.json") if JSON.respond_to?(:load_file) assert_equal({ :en => { :foo => { :bar => 'baz' } } }, data) assert_predicate data.dig(:en, :foo, :bar), :frozen? else assert_equal({ 'en' => { 'foo' => { 'bar' => 'baz' } } }, data) end end test "simple load_translations: loads data from known file formats" do I18n.backend = I18n::Backend::Simple.new I18n.backend.load_translations("#{locales_dir}/en.rb", "#{locales_dir}/en.yml") expected = { :en => { :fuh => { :bah => "bas" }, :foo => { :bar => "baz" } } } assert_equal expected, translations end test "simple load_translations: given file names as array it does not raise anything" do assert_nothing_raised { I18n.backend.load_translations(["#{locales_dir}/en.rb", "#{locales_dir}/en.yml"]) } end # storing translations test "simple store_translations: stores translations, ... no, really :-)" do store_translations :'en', :foo => 'bar' assert_equal Hash[:'en', {:foo => 'bar'}], translations end test "simple store_translations: deep_merges with existing translations" do store_translations :'en', :foo => {:bar => 'bar'} store_translations :'en', :foo => {:baz => 'baz'} assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], translations end test "simple store_translations: converts the given locale to a Symbol" do store_translations 'en', :foo => 'bar' assert_equal Hash[:'en', {:foo => 'bar'}], translations end test "simple store_translations: converts keys to Symbols" do store_translations 'en', 'foo' => {'bar' => 'bar', 'baz' => 'baz'} assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], translations end test "simple store_translations: do not store translations unavailable locales if enforce_available_locales is true" do begin I18n.enforce_available_locales = true I18n.available_locales = [:en, :es] store_translations(:fr, :foo => {:bar => 'barfr', :baz => 'bazfr'}) store_translations(:es, :foo => {:bar => 'bares', :baz => 'bazes'}) assert_equal translations[:fr], {} assert_equal Hash[:foo, {:bar => 'bares', :baz => 'bazes'}], translations[:es] ensure I18n.config.enforce_available_locales = false end end test "simple store_translations: store translations for unavailable locales if enforce_available_locales is false" do I18n.available_locales = [:en, :es] store_translations(:fr, :foo => {:bar => 'barfr', :baz => 'bazfr'}) assert_equal Hash[:foo, {:bar => 'barfr', :baz => 'bazfr'}], translations[:fr] end test "simple store_translations: supports numeric keys" do store_translations(:en, 1 => 'foo') assert_equal 'foo', I18n.t('1') assert_equal 'foo', I18n.t(1) assert_equal 'foo', I18n.t(:'1') end test "simple store_translations: store translations doesn't deep symbolize keys if skip_symbolize_keys is true" do data = { :foo => {'bar' => 'barfr', 'baz' => 'bazfr'} } # symbolized by default store_translations(:fr, data) assert_equal Hash[:foo, {:bar => 'barfr', :baz => 'bazfr'}], translations[:fr] I18n.backend.reload! # not deep symbolized when configured store_translations(:fr, data, skip_symbolize_keys: true) assert_equal Hash[:foo, {'bar' => 'barfr', 'baz' => 'bazfr'}], translations[:fr] end # reloading translations test "simple reload_translations: unloads translations" do I18n.backend.reload! assert_nil translations end test "simple reload_translations: uninitializes the backend" do I18n.backend.reload! assert_equal false, I18n.backend.initialized? end test "simple eager_load!: loads the translations" do assert_equal false, I18n.backend.initialized? I18n.backend.eager_load! assert_equal true, I18n.backend.initialized? end test "simple reload!: reinitialize the backend if it was previously eager loaded" do I18n.backend.eager_load! I18n.backend.reload! assert_equal true, I18n.backend.initialized? end test "Nested keys within pluralization context" do store_translations(:en, :stars => { one: "%{count} star", other: "%{count} stars", special: { one: "%{count} special star", other: "%{count} special stars", } } ) assert_equal "1 star", I18n.t('stars', count: 1, :locale => :en) assert_equal "20 stars", I18n.t('stars', count: 20, :locale => :en) assert_equal "1 special star", I18n.t('stars.special', count: 1, :locale => :en) assert_equal "20 special stars", I18n.t('stars.special', count: 20, :locale => :en) end test "returns localized string given missing pluralization data" do assert_equal 'baz', I18n.t('foo.bar', count: 1) end end i18n-1.14.1/test/backend/transliterator_test.rb000066400000000000000000000060311443717167300214010ustar00rootroot00000000000000# encoding: utf-8 require 'test_helper' class I18nBackendTransliterator < I18n::TestCase def setup super I18n.backend = I18n::Backend::Simple.new @proc = lambda { |n| n.upcase } @hash = { "ü" => "ue", "ö" => "oe", "a" => "a" } @transliterator = I18n::Backend::Transliterator.get end test "transliteration rule can be a proc" do store_translations(:xx, :i18n => {:transliterate => {:rule => @proc}}) assert_equal "HELLO", I18n.backend.transliterate(:xx, "hello") end test "transliteration rule can be a hash" do store_translations(:xx, :i18n => {:transliterate => {:rule => @hash}}) assert_equal "ue", I18n.backend.transliterate(:xx, "ü") end test "transliteration rule must be a proc or hash" do store_translations(:xx, :i18n => {:transliterate => {:rule => ""}}) assert_raises I18n::ArgumentError do I18n.backend.transliterate(:xx, "ü") end end test "transliterator defaults to latin => ascii when no rule is given" do assert_equal "AEroskobing", I18n.backend.transliterate(:xx, "Ærøskøbing") end test "default transliterator should not modify ascii characters" do (0..127).each do |byte| char = [byte].pack("U") assert_equal char, @transliterator.transliterate(char) end end test "default transliterator correctly transliterates latin characters" do # create string with range of Unicode's western characters with # diacritics, excluding the division and multiplication signs which for # some reason or other are floating in the middle of all the letters. string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include? c}.append(0x1E9E).pack("U*") string.split(//) do |char| assert_match %r{^[a-zA-Z']*$}, @transliterator.transliterate(string) end end test "should replace non-ASCII chars not in map with a replacement char" do assert_equal "abc?", @transliterator.transliterate("abcſ") end test "can replace non-ASCII chars not in map with a custom replacement string" do assert_equal "abc#", @transliterator.transliterate("abcſ", "#") end test "default transliterator raises errors for invalid UTF-8" do assert_raises ArgumentError do @transliterator.transliterate("a\x92b") end end test "I18n.transliterate should transliterate using a default transliterator" do assert_equal "aeo", I18n.transliterate("áèö") end test "I18n.transliterate should transliterate using a locale" do store_translations(:xx, :i18n => {:transliterate => {:rule => @hash}}) assert_equal "ue", I18n.transliterate("ü", :locale => :xx) end test "default transliterator fails with custom rules with uncomposed input" do char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS transliterator = I18n::Backend::Transliterator.get(@hash) refute_equal "ue", transliterator.transliterate(char) end test "DEFAULT_APPROXIMATIONS is frozen to prevent concurrency issues" do assert I18n::Backend::Transliterator::HashTransliterator::DEFAULT_APPROXIMATIONS.frozen? end end i18n-1.14.1/test/gettext/000077500000000000000000000000001443717167300150355ustar00rootroot00000000000000i18n-1.14.1/test/gettext/api_test.rb000066400000000000000000000210211443717167300171660ustar00rootroot00000000000000# encoding: utf-8 require 'test_helper' require 'i18n/gettext/helpers' include I18n::Gettext::Helpers class I18nGettextApiTest < I18n::TestCase def setup super I18n.locale = :en I18n.backend.store_translations :de, { 'Hi Gettext!' => 'Hallo Gettext!', 'Sentence 1. Sentence 2.' => 'Satz 1. Satz 2.', "An apple" => { :one => 'Ein Apfel', :other => '%{count} Äpfel' }, :special => { "A special apple" => { :one => 'Ein spezieller Apfel', :other => '%{count} spezielle Äpfel' } }, :foo => { :bar => 'bar-de' }, 'foo.bar' => 'Foo Bar' }, :separator => '|' end # N_ def test_N_returns_original_msg assert_equal 'foo|bar', N_('foo|bar') I18n.locale = :de assert_equal 'Hi Gettext!', N_('Hi Gettext!') end # gettext def test_gettext_uses_msg_as_default assert_equal 'Hi Gettext!', _('Hi Gettext!') end def test_gettext_uses_msg_as_key I18n.locale = :de assert_equal 'Hallo Gettext!', gettext('Hi Gettext!') assert_equal 'Hallo Gettext!', _('Hi Gettext!') end def test_gettext_uses_msg_containing_dots_as_default assert_equal 'Sentence 1. Sentence 2.', gettext('Sentence 1. Sentence 2.') assert_equal 'Sentence 1. Sentence 2.', _('Sentence 1. Sentence 2.') end def test_gettext_uses_msg_containing_dots_as_key I18n.locale = :de assert_equal 'Satz 1. Satz 2.', gettext('Sentence 1. Sentence 2.') assert_equal 'Satz 1. Satz 2.', _('Sentence 1. Sentence 2.') end # sgettext def test_sgettext_defaults_to_the_last_token_of_a_scoped_msgid assert_equal 'bar', sgettext('foo|bar') assert_equal 'bar', s_('foo|bar') end def test_sgettext_looks_up_a_scoped_translation I18n.locale = :de assert_equal 'bar-de', sgettext('foo|bar') assert_equal 'bar-de', s_('foo|bar') end def test_sgettext_ignores_dots I18n.locale = :de assert_equal 'Foo Bar', sgettext('foo.bar') assert_equal 'Foo Bar', s_('foo.bar') end # pgettext def test_pgettext_defaults_to_msgid assert_equal 'bar', pgettext('foo', 'bar') assert_equal 'bar', p_('foo', 'bar') end def test_pgettext_looks_up_a_scoped_translation I18n.locale = :de assert_equal 'bar-de', pgettext('foo', 'bar') assert_equal 'bar-de', p_('foo', 'bar') end # ngettext def test_ngettext_looks_up_msg_id_as_default_singular assert_equal 'An apple', ngettext('An apple', '%{count} apples', 1) assert_equal 'An apple', n_('An apple', '%{count} apples', 1) end def test_ngettext_looks_up_msg_id_plural_as_default_plural assert_equal '2 apples', ngettext('An apple', '%{count} apples', 2) assert_equal '2 apples', n_('An apple', '%{count} apples', 2) end def test_ngettext_looks_up_a_singular I18n.locale = :de assert_equal 'Ein Apfel', ngettext('An apple', '%{count} apples', 1) assert_equal 'Ein Apfel', n_('An apple', '%{count} apples', 1) end def test_ngettext_looks_up_a_plural I18n.locale = :de assert_equal '2 Äpfel', ngettext('An apple', '%{count} apples', 2) assert_equal '2 Äpfel', n_('An apple', '%{count} apples', 2) end def test_ngettext_looks_up_msg_id_as_default_singular_with_alternative_syntax assert_equal 'An apple', ngettext(['An apple', '%{count} apples'], 1) assert_equal 'An apple', n_(['An apple', '%{count} apples'], 1) end def test_ngettext_looks_up_msg_id_plural_as_default_plural_with_alternative_syntax assert_equal '2 apples', ngettext(['An apple', '%{count} apples'], 2) assert_equal '2 apples', n_(['An apple', '%{count} apples'], 2) end def test_ngettext_looks_up_a_singular_with_alternative_syntax I18n.locale = :de assert_equal 'Ein Apfel', ngettext(['An apple', '%{count} apples'], 1) assert_equal 'Ein Apfel', n_(['An apple', '%{count} apples'], 1) end def test_ngettext_looks_up_a_plural_with_alternative_syntax I18n.locale = :de assert_equal '2 Äpfel', ngettext(['An apple', '%{count} apples'], 2) assert_equal '2 Äpfel', n_(['An apple', '%{count} apples'], 2) end # nsgettext def test_nsgettext_looks_up_msg_id_as_default_singular assert_equal 'A special apple', nsgettext('special|A special apple', '%{count} special apples', 1) assert_equal 'A special apple', ns_('special|A special apple', '%{count} special apples', 1) end def test_nsgettext_looks_up_msg_id_plural_as_default_plural assert_equal '2 special apples', nsgettext('special|A special apple', '%{count} special apples', 2) assert_equal '2 special apples', ns_('special|A special apple', '%{count} special apples', 2) end def test_nsgettext_looks_up_a_singular I18n.locale = :de assert_equal 'Ein spezieller Apfel', nsgettext('special|A special apple', '%{count} special apples', 1) assert_equal 'Ein spezieller Apfel', ns_('special|A special apple', '%{count} special apples', 1) end def test_nsgettext_looks_up_a_plural I18n.locale = :de assert_equal '2 spezielle Äpfel', nsgettext('special|A special apple', '%{count} special apples', 2) assert_equal '2 spezielle Äpfel', ns_('special|A special apple', '%{count} special apples', 2) end def test_nsgettext_looks_up_msg_id_as_default_singular_with_alternative_syntax assert_equal 'A special apple', nsgettext(['special|A special apple', '%{count} special apples'], 1) assert_equal 'A special apple', ns_(['special|A special apple', '%{count} special apples'], 1) end def test_nsgettext_looks_up_msg_id_plural_as_default_plural_with_alternative_syntax assert_equal '2 special apples', nsgettext(['special|A special apple', '%{count} special apples'], 2) assert_equal '2 special apples', ns_(['special|A special apple', '%{count} special apples'], 2) end def test_nsgettext_looks_up_a_singular_with_alternative_syntax I18n.locale = :de assert_equal 'Ein spezieller Apfel', nsgettext(['special|A special apple', '%{count} special apples'], 1) assert_equal 'Ein spezieller Apfel', ns_(['special|A special apple', '%{count} special apples'], 1) end def test_nsgettext_looks_up_a_plural_with_alternative_syntax I18n.locale = :de assert_equal '2 spezielle Äpfel', nsgettext(['special|A special apple', '%{count} special apples'], 2) assert_equal '2 spezielle Äpfel', ns_(['special|A special apple', '%{count} special apples'], 2) end # npgettext def test_npgettext_looks_up_msg_id_as_default_singular assert_equal 'A special apple', npgettext('special', 'A special apple', '%{count} special apples', 1) assert_equal 'A special apple', np_('special', 'A special apple', '%{count} special apples', 1) end def test_npgettext_looks_up_msg_id_plural_as_default_plural assert_equal '2 special apples', npgettext('special', 'A special apple', '%{count} special apples', 2) assert_equal '2 special apples', np_('special', 'A special apple', '%{count} special apples', 2) end def test_npgettext_looks_up_a_singular I18n.locale = :de assert_equal 'Ein spezieller Apfel', npgettext('special', 'A special apple', '%{count} special apples', 1) assert_equal 'Ein spezieller Apfel', np_('special', 'A special apple', '%{count} special apples', 1) end def test_npgettext_looks_up_a_plural I18n.locale = :de assert_equal '2 spezielle Äpfel', npgettext('special', 'A special apple', '%{count} special apples', 2) assert_equal '2 spezielle Äpfel', np_('special', 'A special apple', '%{count} special apples', 2) end def test_npgettext_looks_up_msg_id_as_default_singular_with_alternative_syntax assert_equal 'A special apple', npgettext('special', ['A special apple', '%{count} special apples'], 1) assert_equal 'A special apple', np_('special', ['A special apple', '%{count} special apples'], 1) end def test_npgettext_looks_up_msg_id_plural_as_default_plural_with_alternative_syntax assert_equal '2 special apples', npgettext('special', ['A special apple', '%{count} special apples'], 2) assert_equal '2 special apples', np_('special', ['A special apple', '%{count} special apples'], 2) end def test_npgettext_looks_up_a_singular_with_alternative_syntax I18n.locale = :de assert_equal 'Ein spezieller Apfel', npgettext('special', ['A special apple', '%{count} special apples'], 1) assert_equal 'Ein spezieller Apfel', np_('special', ['A special apple', '%{count} special apples'], 1) end def test_npgettext_looks_up_a_plural_with_alternative_syntax I18n.locale = :de assert_equal '2 spezielle Äpfel', npgettext('special', ['A special apple', '%{count} special apples'], 2) assert_equal '2 spezielle Äpfel', np_('special', ['A special apple', '%{count} special apples'], 2) end end i18n-1.14.1/test/gettext/backend_test.rb000066400000000000000000000061541443717167300200160ustar00rootroot00000000000000# encoding: utf-8 require 'test_helper' class I18nGettextBackendTest < I18n::TestCase include I18n::Gettext::Helpers class Backend < I18n::Backend::Simple include I18n::Backend::Gettext end def setup super I18n.backend = Backend.new I18n.locale = :en I18n.load_path = ["#{locales_dir}/de.po"] I18n.default_separator = '|' end def test_backend_loads_po_file I18n.backend.send(:init_translations) assert I18n.backend.send(:translations)[:de][:"Axis"] end def test_looks_up_a_translation I18n.locale = :de assert_equal 'Auto', gettext('car') end def test_uses_default_translation assert_equal 'car', gettext('car') end def test_looks_up_a_namespaced_translation I18n.locale = :de assert_equal 'Räderzahl', sgettext('Car|Wheels count') assert_equal 'Räderzahl', pgettext('Car', 'Wheels count') assert_equal 'Räderzahl!', pgettext('New car', 'Wheels count') end def test_uses_namespaced_default_translation assert_equal 'Wheels count', sgettext('Car|Wheels count') assert_equal 'Wheels count', pgettext('Car', 'Wheels count') assert_equal 'Wheels count', pgettext('New car', 'Wheels count') end def test_pluralizes_entry I18n.locale = :de assert_equal 'Achse', ngettext('Axis', 'Axis', 1) assert_equal 'Achsen', ngettext('Axis', 'Axis', 2) end def test_pluralizes_default_entry assert_equal 'Axis', ngettext('Axis', 'Axis', 1) assert_equal 'Axis', ngettext('Axis', 'Axis', 2) end def test_pluralizes_namespaced_entry I18n.locale = :de assert_equal 'Rad', nsgettext('Car|wheel', 'wheels', 1) assert_equal 'Räder', nsgettext('Car|wheel', 'wheels', 2) assert_equal 'Rad', npgettext('Car', 'wheel', 'wheels', 1) assert_equal 'Räder', npgettext('Car', 'wheel', 'wheels', 2) assert_equal 'Rad!', npgettext('New car', 'wheel', 'wheels', 1) assert_equal 'Räder!', npgettext('New car', 'wheel', 'wheels', 2) end def test_pluralizes_namespaced_default_entry assert_equal 'wheel', nsgettext('Car|wheel', 'wheels', 1) assert_equal 'wheels', nsgettext('Car|wheel', 'wheels', 2) assert_equal 'wheel', npgettext('Car', 'wheel', 'wheels', 1) assert_equal 'wheels', npgettext('Car', 'wheel', 'wheels', 2) assert_equal 'wheel', npgettext('New car', 'wheel', 'wheels', 1) assert_equal 'wheels', npgettext('New car', 'wheel', 'wheels', 2) end def test_pluralizes_namespaced_entry_with_alternative_syntax I18n.locale = :de assert_equal 'Rad', nsgettext(['Car|wheel', 'wheels'], 1) assert_equal 'Räder', nsgettext(['Car|wheel', 'wheels'], 2) assert_equal 'Rad', npgettext('Car', ['wheel', 'wheels'], 1) assert_equal 'Räder', npgettext('Car', ['wheel', 'wheels'], 2) assert_equal 'Rad!', npgettext('New car', ['wheel', 'wheels'], 1) assert_equal 'Räder!', npgettext('New car', ['wheel', 'wheels'], 2) end def test_ngettextpluralizes_entry_with_dots I18n.locale = :de assert_equal 'Auf 1 Achse.', n_("On %{count} wheel.", "On %{count} wheels.", 1) assert_equal 'Auf 2 Achsen.', n_("On %{count} wheel.", "On %{count} wheels.", 2) end end i18n-1.14.1/test/i18n/000077500000000000000000000000001443717167300141305ustar00rootroot00000000000000i18n-1.14.1/test/i18n/exceptions_test.rb000066400000000000000000000075351443717167300177070ustar00rootroot00000000000000require 'test_helper' class I18nExceptionsTest < I18n::TestCase def test_invalid_locale_stores_locale force_invalid_locale rescue I18n::ArgumentError => exception assert_nil exception.locale end test "passing an invalid locale raises an InvalidLocale exception" do force_invalid_locale do |exception| assert_equal 'nil is not a valid locale', exception.message end end test "MissingTranslation can be initialized without options" do exception = I18n::MissingTranslation.new(:en, 'foo') assert_equal({}, exception.options) end test "MissingTranslationData exception stores locale, key and options" do force_missing_translation_data do |exception| assert_equal 'de', exception.locale assert_equal :foo, exception.key assert_equal({:scope => :bar}, exception.options) end end test "MissingTranslationData message contains the locale and scoped key" do force_missing_translation_data do |exception| assert_equal 'translation missing: de.bar.foo', exception.message end end test "InvalidPluralizationData stores entry, count and key" do force_invalid_pluralization_data do |exception| assert_equal({:other => "bar"}, exception.entry) assert_equal 1, exception.count assert_equal :one, exception.key end end test "InvalidPluralizationData message contains count, data and missing key" do force_invalid_pluralization_data do |exception| assert_match '1', exception.message assert_match '{:other=>"bar"}', exception.message assert_match 'one', exception.message end end test "MissingInterpolationArgument stores key and string" do assert_raises(I18n::MissingInterpolationArgument) { force_missing_interpolation_argument } force_missing_interpolation_argument do |exception| assert_equal :bar, exception.key assert_equal "%{bar}", exception.string end end test "MissingInterpolationArgument message contains the missing and given arguments" do force_missing_interpolation_argument do |exception| assert_equal 'missing interpolation argument :bar in "%{bar}" ({:baz=>"baz"} given)', exception.message end end test "ReservedInterpolationKey stores key and string" do force_reserved_interpolation_key do |exception| assert_equal :scope, exception.key assert_equal "%{scope}", exception.string end end test "ReservedInterpolationKey message contains the reserved key" do force_reserved_interpolation_key do |exception| assert_equal 'reserved key :scope used in "%{scope}"', exception.message end end test "MissingTranslationData#new can be initialized with just two arguments" do assert I18n::MissingTranslationData.new('en', 'key') end private def force_invalid_locale I18n.translate(:foo, :locale => nil) rescue I18n::ArgumentError => e block_given? ? yield(e) : raise(e) end def force_missing_translation_data(options = {}) store_translations('de', :bar => nil) I18n.translate(:foo, **options.merge(:scope => :bar, :locale => :de)) rescue I18n::ArgumentError => e block_given? ? yield(e) : raise(e) end def force_invalid_pluralization_data store_translations('de', :foo => { :other => 'bar' }) I18n.translate(:foo, :count => 1, :locale => :de) rescue I18n::ArgumentError => e block_given? ? yield(e) : raise(e) end def force_missing_interpolation_argument store_translations('de', :foo => "%{bar}") I18n.translate(:foo, :baz => 'baz', :locale => :de) rescue I18n::ArgumentError => e block_given? ? yield(e) : raise(e) end def force_reserved_interpolation_key store_translations('de', :foo => "%{scope}") I18n.translate(:foo, :baz => 'baz', :locale => :de) rescue I18n::ArgumentError => e block_given? ? yield(e) : raise(e) end end i18n-1.14.1/test/i18n/gettext_plural_keys_test.rb000066400000000000000000000011311443717167300216060ustar00rootroot00000000000000require 'test_helper' class I18nGettextPluralKeysTest < I18n::TestCase def setup super I18n::Gettext.plural_keys[:zz] = [:value1, :value2] end test "Returns the plural keys of the given locale if present" do assert_equal I18n::Gettext.plural_keys(:zz), [:value1, :value2] end test "Returns the plural keys of :en if given locale not present" do assert_equal I18n::Gettext.plural_keys(:yy), [:one, :other] end test "Returns the whole hash with no arguments" do assert_equal I18n::Gettext.plural_keys, { :en => [:one, :other], :zz => [:value1, :value2] } end end i18n-1.14.1/test/i18n/interpolate_test.rb000066400000000000000000000110551443717167300200440ustar00rootroot00000000000000require 'test_helper' # thanks to Masao's String extensions, some tests taken from Masao's tests # http://github.com/mutoh/gettext/blob/edbbe1fa8238fa12c7f26f2418403015f0270e47/test/test_string.rb class I18nInterpolateTest < I18n::TestCase test "String interpolates a hash argument w/ named placeholders" do assert_equal "Masao Mutoh", I18n.interpolate("%{first} %{last}", :first => 'Masao', :last => 'Mutoh' ) end test "String interpolates a hash argument w/ named placeholders (reverse order)" do assert_equal "Mutoh, Masao", I18n.interpolate("%{last}, %{first}", :first => 'Masao', :last => 'Mutoh' ) end test "String interpolates named placeholders with sprintf syntax" do assert_equal "10, 43.4", I18n.interpolate("%d, %.1f", :integer => 10, :float => 43.4) end test "String interpolates named placeholders with sprintf syntax, does not recurse" do assert_equal "%s", I18n.interpolate("%{msg}", :msg => '%s', :not_translated => 'should not happen' ) end test "String interpolation does not replace anything when no placeholders are given" do assert_equal "aaa", I18n.interpolate("aaa", :num => 1) end test "String interpolation sprintf behaviour equals Ruby 1.9 behaviour" do assert_equal "1", I18n.interpolate("%d", :num => 1) assert_equal "0b1", I18n.interpolate("%#b", :num => 1) assert_equal "foo", I18n.interpolate("%s", :msg => "foo") assert_equal "1.000000", I18n.interpolate("%f", :num => 1.0) assert_equal " 1", I18n.interpolate("%3.0f", :num => 1.0) assert_equal "100.00", I18n.interpolate("%2.2f", :num => 100.0) assert_equal "0x64", I18n.interpolate("%#x", :num => 100.0) assert_raises(ArgumentError) { I18n.interpolate("%,d", :num => 100) } assert_raises(ArgumentError) { I18n.interpolate("%/d", :num => 100) } end test "String interpolation raises an I18n::MissingInterpolationArgument when the string has extra placeholders" do assert_raises(I18n::MissingInterpolationArgument, "key not found") do I18n.interpolate("%{first} %{last}", :first => 'Masao') end end test "String interpolation does not raise when extra values were passed" do assert_nothing_raised do assert_equal "Masao Mutoh", I18n.interpolate("%{first} %{last}", :first => 'Masao', :last => 'Mutoh', :salutation => 'Mr.' ) end end test "% acts as escape character in String interpolation" do assert_equal "%{first}", I18n.interpolate("%%{first}", :first => 'Masao') assert_equal "% 1", I18n.interpolate("%% %d", :num => 1.0) assert_equal "%{num} %d", I18n.interpolate("%%{num} %%d", :num => 1) end def test_sprintf_mix_unformatted_and_formatted_named_placeholders assert_equal "foo 1.000000", I18n.interpolate("%{name} %f", :name => "foo", :num => 1.0) end class RailsSafeBuffer < String def gsub(*args, &block) to_str.gsub(*args, &block) end end test "with String subclass that redefined gsub method" do assert_equal "Hello mars world", I18n.interpolate(RailsSafeBuffer.new("Hello %{planet} world"), :planet => 'mars') end test "with String subclass that redefined gsub method returns same object if no interpolations" do string = RailsSafeBuffer.new("Hello world") assert_same string, I18n.interpolate(string, :planet => 'mars') end end class I18nMissingInterpolationCustomHandlerTest < I18n::TestCase def setup super @old_handler = I18n.config.missing_interpolation_argument_handler I18n.config.missing_interpolation_argument_handler = lambda do |key, values, string| "missing key is #{key}, values are #{values.inspect}, given string is '#{string}'" end end def teardown I18n.config.missing_interpolation_argument_handler = @old_handler super end test "String interpolation can use custom missing interpolation handler" do assert_equal %|Masao missing key is last, values are {:first=>"Masao"}, given string is '%{first} %{last}'|, I18n.interpolate("%{first} %{last}", :first => 'Masao') end end class I18nCustomInterpolationPatternTest < I18n::TestCase def setup super @old_interpolation_patterns = I18n.config.interpolation_patterns I18n.config.interpolation_patterns << /\{\{(\w+)\}\}/ end def teardown I18n.config.interpolation_patterns = @old_interpolation_patterns super end test "String interpolation can use custom interpolation pattern" do assert_equal "Masao Mutoh", I18n.interpolate("{{first}} {{last}}", :first => "Masao", :last => "Mutoh") end end i18n-1.14.1/test/i18n/load_path_test.rb000066400000000000000000000020511443717167300174450ustar00rootroot00000000000000require 'test_helper' class I18nLoadPathTest < I18n::TestCase def setup super I18n.locale = :en I18n.backend = I18n::Backend::Simple.new store_translations(:en, :foo => {:bar => 'bar', :baz => 'baz'}) end test "nested load paths do not break locale loading" do I18n.load_path = [[locales_dir + '/en.yml']] assert_equal "baz", I18n.t(:'foo.bar') end test "loading an empty yml file raises an InvalidLocaleData exception" do assert_raises I18n::InvalidLocaleData do I18n.load_path = [[locales_dir + '/invalid/empty.yml']] I18n.t(:'foo.bar', :default => "baz") end end test "loading an invalid yml file raises an InvalidLocaleData exception" do assert_raises I18n::InvalidLocaleData do I18n.load_path = [[locales_dir + '/invalid/syntax.yml']] I18n.t(:'foo.bar', :default => "baz") end end test "adding arrays of filenames to the load path does not break locale loading" do I18n.load_path << Dir[locales_dir + '/*.{rb,yml}'] assert_equal "baz", I18n.t(:'foo.bar') end end i18n-1.14.1/test/i18n/middleware_test.rb000066400000000000000000000012401443717167300176260ustar00rootroot00000000000000require 'test_helper' class I18nMiddlewareTest < I18n::TestCase def setup super I18n.default_locale = :fr @app = DummyRackApp.new @middleware = I18n::Middleware.new(@app) end test "middleware initializes new config object after request" do old_i18n_config_object_id = Thread.current[:i18n_config].object_id @middleware.call({}) updated_i18n_config_object_id = Thread.current[:i18n_config].object_id refute_equal updated_i18n_config_object_id, old_i18n_config_object_id end test "successfully resets i18n locale to default locale by defining new config" do @middleware.call({}) assert_equal :fr, I18n.locale end end i18n-1.14.1/test/i18n_test.rb000066400000000000000000000421711443717167300155210ustar00rootroot00000000000000# encoding: utf-8 require 'test_helper' class I18nTest < I18n::TestCase def setup super store_translations(:en, :currency => { :format => { :separator => '.', :delimiter => ',', } }) store_translations(:nl, :currency => { :format => { :separator => ',', :delimiter => '.', } }) store_translations(:en, "true" => "Yes", "false" => "No") end test "exposes its VERSION constant" do assert I18n::VERSION end test "uses the simple backend by default" do assert I18n.backend.is_a?(I18n::Backend::Simple) end test "can set the backend" do begin assert_nothing_raised { I18n.backend = self } assert_equal self, I18n.backend ensure I18n.backend = I18n::Backend::Simple.new end end test "uses :en as a default_locale by default" do assert_equal :en, I18n.default_locale end test "can set the default locale" do begin assert_nothing_raised { I18n.default_locale = 'de' } assert_equal :de, I18n.default_locale ensure I18n.default_locale = :en end end test "default_locale= doesn't ignore junk" do assert_raises(NoMethodError) { I18n.default_locale = Class } end test "raises an I18n::InvalidLocale exception when setting an unavailable default locale" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.default_locale = :klingon } ensure I18n.config.enforce_available_locales = false end end test "uses the default locale as a locale by default" do assert_equal I18n.default_locale, I18n.locale end test "sets the current locale to Thread.current" do assert_nothing_raised { I18n.locale = 'de' } assert_equal :de, I18n.locale assert_equal :de, Thread.current[:i18n_config].locale I18n.locale = :en end test "locale= doesn't ignore junk" do assert_raises(NoMethodError) { I18n.locale = Class } end test "raises an I18n::InvalidLocale exception when setting an unavailable locale" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.locale = :klingon } ensure I18n.config.enforce_available_locales = false end end test "can set the configuration object" do begin I18n.config = self assert_equal self, I18n.config assert_equal self, Thread.current[:i18n_config] ensure I18n.config = ::I18n::Config.new end end test "locale is not shared between configurations" do a = I18n::Config.new b = I18n::Config.new a.locale = :fr b.locale = :es assert_equal :fr, a.locale assert_equal :es, b.locale assert_equal :en, I18n.locale end test "other options are shared between configurations" do begin a = I18n::Config.new b = I18n::Config.new a.default_locale = :fr b.default_locale = :es assert_equal :es, a.default_locale assert_equal :es, b.default_locale assert_equal :es, I18n.default_locale ensure I18n.default_locale = :en end end test "uses a dot as a default_separator by default" do assert_equal '.', I18n.default_separator end test "can set the default_separator" do begin assert_nothing_raised { I18n.default_separator = "\001" } ensure I18n.default_separator = '.' end end test "normalize_keys normalizes given locale, keys and scope to an array of single-key symbols" do assert_equal [:en, :foo, :bar], I18n.normalize_keys(:en, :bar, :foo) assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, :'baz.buz', :'foo.bar') assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, 'baz.buz', 'foo.bar') assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, %w(baz buz), %w(foo bar)) assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, [:baz, :buz], [:foo, :bar]) end test "normalize_keys discards empty keys" do assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, :'baz..buz', :'foo..bar') assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, :'baz......buz', :'foo......bar') assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, ['baz', nil, '', 'buz'], ['foo', nil, '', 'bar']) end test "normalize_keys uses a given separator" do assert_equal [:en, :foo, :bar, :baz, :buz], I18n.normalize_keys(:en, :'baz|buz', :'foo|bar', '|') end test "normalize_keys normalizes given locale with separator" do assert_equal [:en, :foo, :bar, :baz], I18n.normalize_keys(:"en.foo", :baz, :bar) end test "can set the exception_handler" do begin previous_exception_handler = I18n.exception_handler assert_nothing_raised { I18n.exception_handler = :custom_exception_handler } ensure I18n.exception_handler = previous_exception_handler end end test "uses a custom exception handler set to I18n.exception_handler" do begin previous_exception_handler = I18n.exception_handler I18n.exception_handler = :custom_exception_handler I18n.expects(:custom_exception_handler) I18n.translate :bogus ensure I18n.exception_handler = previous_exception_handler end end test "uses a custom exception handler passed as an option" do I18n.expects(:custom_exception_handler) I18n.translate(:bogus, :exception_handler => :custom_exception_handler) end test "delegates translate calls to the backend" do I18n.backend.expects(:translate).with('de', :foo, {}) I18n.translate :foo, :locale => 'de' end test "delegates localize calls to the backend" do I18n.backend.expects(:localize).with('de', :whatever, :default, {}) I18n.localize :whatever, :locale => 'de' end test "translate given no locale uses the current locale" do I18n.backend.expects(:translate).with(:en, :foo, {}) I18n.translate :foo end test "translate works with nested symbol keys" do assert_equal ".", I18n.t(:'currency.format.separator') end test "translate works with nested string keys" do assert_equal ".", I18n.t('currency.format.separator') end test "translate with an array as a scope works" do assert_equal ".", I18n.t(:separator, :scope => %w(currency format)) end test "translate with an array containing dot separated strings as a scope works" do assert_equal ".", I18n.t(:separator, :scope => ['currency.format']) end test "translate with an array of keys and a dot separated string as a scope works" do assert_equal [".", ","], I18n.t(%w(separator delimiter), :scope => 'currency.format') end test "translate with an array of dot separated keys and a scope works" do assert_equal [".", ","], I18n.t(%w(format.separator format.delimiter), :scope => 'currency') end # def test_translate_given_no_args_raises_missing_translation_data # assert_equal "Translation missing: en, no key", I18n.t # end test "translate given a bogus key returns an error message" do assert_equal "Translation missing: en.bogus", I18n.t(:bogus) end test "translate given multiple bogus keys returns an array of error messages" do assert_equal( ["Translation missing: en.bogus", "Translation missing: en.also_bogus"], I18n.t([:bogus, :also_bogus]), ) end test "translate given an empty string as a key raises an I18n::ArgumentError" do assert_raises(I18n::ArgumentError) { I18n.t("") } end test "translate given an empty symbol as a key raises an I18n::ArgumentError" do assert_raises(I18n::ArgumentError) { I18n.t(:"") } end test "translate given an array with empty string as a key raises an I18n::ArgumentError" do assert_raises(I18n::ArgumentError) { I18n.t(["", :foo]) } end test "translate given an empty array as a key returns empty array" do assert_equal [], I18n.t([]) end test "translate given nil returns nil" do assert_nil I18n.t(nil) end test "translate given an unavailable locale rases an I18n::InvalidLocale" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.t(:foo, :locale => 'klingon') } ensure I18n.config.enforce_available_locales = false end end test "translate given true as a key works" do assert_equal "Yes", I18n.t(true) end test "translate given false as a key works" do assert_equal "No", I18n.t(false) end test "translate raises Disabled if locale is false" do I18n.with_locale(false) do assert_raises I18n::Disabled do I18n.t('foo') end assert_equal 'Translation missing: en.foo', I18n.t('foo', locale: :en) end end test "available_locales can be replaced at runtime" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.t(:foo, :locale => 'klingon') } old_locales, I18n.config.available_locales = I18n.config.available_locales, [:klingon] I18n.t(:foo, :locale => 'klingon') ensure I18n.config.enforce_available_locales = false I18n.config.available_locales = old_locales end end test "available_locales_set should return a set" do assert_equal Set, I18n.config.available_locales_set.class assert_equal I18n.config.available_locales.size * 2, I18n.config.available_locales_set.size end test "exists? given an existing key will return true" do assert_equal true, I18n.exists?(:currency) end test "exists? given a non-existing key will return false" do assert_equal false, I18n.exists?(:bogus) end test "exists? given an existing dot-separated key will return true" do assert_equal true, I18n.exists?('currency.format.delimiter') end test "exists? given a non-existing dot-separated key will return false" do assert_equal false, I18n.exists?('currency.format.bogus') end test "exists? given an existing key and an existing locale will return true" do assert_equal true, I18n.exists?(:currency, :nl) end test "exists? given an existing key and a scope will return true" do assert_equal true, I18n.exists?(:delimiter, scope: [:currency, :format]) end test "exists? given a non-existing key and an existing locale will return false" do assert_equal false, I18n.exists?(:bogus, :nl) end test "exists? raises Disabled if locale is false" do I18n.with_locale(false) do assert_raises I18n::Disabled do I18n.exists?(:bogus) end assert_equal false, I18n.exists?(:bogus, :nl) end end test "localize given nil raises an I18n::ArgumentError" do assert_raises(I18n::ArgumentError) { I18n.l nil } end test "localize given nil and default returns default" do assert_nil I18n.l(nil, :default => nil) end test "localize given an Object raises an I18n::ArgumentError" do assert_raises(I18n::ArgumentError) { I18n.l Object.new } end test "localize given an unavailable locale rases an I18n::InvalidLocale" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.l(Time.now, :locale => 'klingon') } ensure I18n.config.enforce_available_locales = false end end test "localize raises Disabled if locale is false" do I18n.with_locale(false) do assert_raises I18n::Disabled do I18n.l(Time.now) end end end test "can use a lambda as an exception handler" do begin previous_exception_handler = I18n.exception_handler I18n.exception_handler = Proc.new { |exception, locale, key, options| key } assert_equal :test_proc_handler, I18n.translate(:test_proc_handler) ensure I18n.exception_handler = previous_exception_handler end end test "can use an object responding to #call as an exception handler" do begin previous_exception_handler = I18n.exception_handler I18n.exception_handler = Class.new do def call(exception, locale, key, options); key; end end.new assert_equal :test_proc_handler, I18n.translate(:test_proc_handler) ensure I18n.exception_handler = previous_exception_handler end end test "I18n.with_locale temporarily sets the given locale" do store_translations(:en, :foo => 'Foo in :en') store_translations(:de, :foo => 'Foo in :de') store_translations(:pl, :foo => 'Foo in :pl') I18n.with_locale { assert_equal [:en, 'Foo in :en'], [I18n.locale, I18n.t(:foo)] } I18n.with_locale(:de) { assert_equal [:de, 'Foo in :de'], [I18n.locale, I18n.t(:foo)] } I18n.with_locale(:pl) { assert_equal [:pl, 'Foo in :pl'], [I18n.locale, I18n.t(:foo)] } I18n.with_locale(:en) { assert_equal [:en, 'Foo in :en'], [I18n.locale, I18n.t(:foo)] } assert_equal I18n.default_locale, I18n.locale end test "I18n.with_locale resets the locale in case of errors" do assert_raises(I18n::ArgumentError) { I18n.with_locale(:pl) { raise I18n::ArgumentError } } assert_equal I18n.default_locale, I18n.locale end test "I18n.transliterate handles I18n::ArgumentError exception" do I18n::Backend::Transliterator.stubs(:get).raises(I18n::ArgumentError) I18n.exception_handler.expects(:call).raises(I18n::ArgumentError) assert_raises(I18n::ArgumentError) { I18n.transliterate("ąćó") } end test "I18n.transliterate raises I18n::ArgumentError exception" do I18n::Backend::Transliterator.stubs(:get).raises(I18n::ArgumentError) I18n.exception_handler.expects(:call).never assert_raises(I18n::ArgumentError) { I18n.transliterate("ąćó", :raise => true) } end test "transliterate given an unavailable locale rases an I18n::InvalidLocale" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.transliterate('string', :locale => 'klingon') } ensure I18n.config.enforce_available_locales = false end end test "transliterate non-ASCII chars not in map with default replacement char" do assert_equal "???", I18n.transliterate("日本語") end test "I18n.locale_available? returns true when the passed locale is available" do I18n.available_locales = [:en, :de] assert_equal true, I18n.locale_available?(:de) end test "I18n.locale_available? returns true when the passed locale is a string and is available" do I18n.available_locales = [:en, :de] assert_equal true, I18n.locale_available?('de') end test "I18n.locale_available? returns false when the passed locale is unavailable" do assert_equal false, I18n.locale_available?(:klingon) end test "I18n.enforce_available_locales! raises an I18n::InvalidLocale when the passed locale is unavailable" do begin I18n.config.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.enforce_available_locales!(:klingon) } ensure I18n.config.enforce_available_locales = false end end test "I18n.enforce_available_locales! does nothing when the passed locale is available" do I18n.available_locales = [:en, :de] begin I18n.config.enforce_available_locales = true assert_nothing_raised { I18n.enforce_available_locales!(:en) } ensure I18n.config.enforce_available_locales = false end end test "I18n.enforce_available_locales config can be set to false" do begin I18n.config.enforce_available_locales = false assert_equal false, I18n.config.enforce_available_locales ensure I18n.config.enforce_available_locales = false end end test 'I18n.reload! reloads the set of locales that are enforced' do begin # Clear the backend that affects the available locales and somehow can remain # set from the last running test. # For instance, it contains enough translations to cause a false positive with # this test when ran with --seed=50992 I18n.backend = I18n::Backend::Simple.new assert !I18n.available_locales.include?(:de), "Available locales should not include :de at this point" I18n.enforce_available_locales = true assert_raises(I18n::InvalidLocale) { I18n.default_locale = :de } assert_raises(I18n::InvalidLocale) { I18n.locale = :de } store_translations(:de, :foo => 'Foo in :de') assert_raises(I18n::InvalidLocale) { I18n.default_locale = :de } assert_raises(I18n::InvalidLocale) { I18n.locale = :de } I18n.reload! store_translations(:en, :foo => 'Foo in :en') store_translations(:de, :foo => 'Foo in :de') store_translations(:pl, :foo => 'Foo in :pl') assert I18n.available_locales.include?(:de), ":de should now be allowed" assert I18n.available_locales.include?(:en), ":en should now be allowed" assert I18n.available_locales.include?(:pl), ":pl should now be allowed" assert_nothing_raised { I18n.default_locale = I18n.locale = :en } assert_nothing_raised { I18n.default_locale = I18n.locale = :de } assert_nothing_raised { I18n.default_locale = I18n.locale = :pl } ensure I18n.enforce_available_locales = false end end test "can reserve a key" do begin stub_const(I18n, :RESERVED_KEYS, []) do I18n.reserve_key(:foo) I18n.reserve_key("bar") assert I18n::RESERVED_KEYS.include?(:foo) assert I18n::RESERVED_KEYS.include?(:bar) end ensure I18n.instance_variable_set(:@reserved_keys_pattern, nil) end end end i18n-1.14.1/test/locale/000077500000000000000000000000001443717167300146105ustar00rootroot00000000000000i18n-1.14.1/test/locale/fallbacks_test.rb000066400000000000000000000145401443717167300201220ustar00rootroot00000000000000require 'test_helper' include I18n::Locale class I18nFallbacksDefaultsTest < I18n::TestCase test "defaults to an empty array if no default has been set manually" do I18n.default_locale = :'en-US' fallbacks = Fallbacks.new assert_equal [], fallbacks.defaults end test "documentation example #1 - does not use default locale in fallbacks - See Issues #413 & #415" do I18n.default_locale = :"en-US" fallbacks = Fallbacks.new(:"de-AT" => :"de-DE") assert_equal [:"de-AT", :de, :"de-DE"], fallbacks[:"de-AT"] end test "documentation example #2 - does not use default locale in fallbacks - Uses custom locale - See Issues #413 & #415" do I18n.default_locale = :"en-US" fallbacks = Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de) assert_equal [:"de-AT", :de, :"en-GB", :en], fallbacks[:"de-AT"] assert_equal [:"de-CH", :de, :"en-GB", :en], fallbacks[:"de-CH"] end test "explicit fallback to default locale" do I18n.default_locale = :"en-US" fallbacks = Fallbacks.new([:"en-US"]) assert_equal [:"de-AT", :de, :"en-US", :en], fallbacks[:"de-AT"] assert_equal [:"de-CH", :de, :"en-US", :en], fallbacks[:"de-CH"] end test "defaults reflect a manually passed default locale if any" do fallbacks = Fallbacks.new(:'fi-FI') assert_equal [:'fi-FI', :fi], fallbacks.defaults I18n.default_locale = :'de-DE' assert_equal [:'fi-FI', :fi], fallbacks.defaults end test "defaults allows to set multiple defaults" do fallbacks = Fallbacks.new(:'fi-FI', :'se-FI') assert_equal [:'fi-FI', :fi, :'se-FI', :se], fallbacks.defaults end end class I18nFallbacksComputationTest < I18n::TestCase def setup super @fallbacks = Fallbacks.new(:'en-US') end test "with no mappings defined it returns [:es, :en-US] for :es" do assert_equal [:es, :"en-US", :en], @fallbacks[:es] end test "with no mappings defined it returns [:es-ES, :es, :en-US] for :es-ES" do assert_equal [:"es-ES", :es, :"en-US", :en], @fallbacks[:"es-ES"] end test "with no mappings defined it returns [:es-MX, :es, :en-US] for :es-MX" do assert_equal [:"es-MX", :es, :"en-US", :en], @fallbacks[:"es-MX"] end test "with no mappings defined it returns [:es-Latn-ES, :es-Latn, :es, :en-US] for :es-Latn-ES" do assert_equal [:"es-Latn-ES", :"es-Latn", :es, :"en-US", :en], @fallbacks[:'es-Latn-ES'] end test "with no mappings defined it returns [:en, :en-US] for :en" do assert_equal [:en, :"en-US"], @fallbacks[:en] end test "with no mappings defined it returns [:en-US, :en] for :en-US (special case: locale == default)" do assert_equal [:"en-US", :en], @fallbacks[:"en-US"] end # Most people who speak Catalan also live in Spain, so it is safe to assume # that they also speak Spanish as spoken in Spain. test "with a Catalan mapping defined it returns [:ca, :es-ES, :es, :en-US] for :ca" do @fallbacks.map(:ca => :"es-ES") assert_equal [:ca, :"es-ES", :es, :"en-US", :en], @fallbacks[:ca] end test "with a Catalan mapping defined it returns [:ca-ES, :ca, :es-ES, :es, :en-US] for :ca-ES" do @fallbacks.map(:ca => :"es-ES") assert_equal [:"ca-ES", :ca, :"es-ES", :es, :"en-US", :en], @fallbacks[:"ca-ES"] end # People who speak Arabic as spoken in Palestine often times also speak # Hebrew as spoken in Israel. However it is in no way safe to assume that # everybody who speaks Arabic also speaks Hebrew. test "with a Hebrew mapping defined it returns [:ar, :en-US] for :ar" do @fallbacks.map(:"ar-PS" => :"he-IL") assert_equal [:ar, :"en-US", :en], @fallbacks[:ar] end test "with a Hebrew mapping defined it returns [:ar-EG, :ar, :en-US] for :ar-EG" do @fallbacks.map(:"ar-PS" => :"he-IL") assert_equal [:"ar-EG", :ar, :"en-US", :en], @fallbacks[:"ar-EG"] end test "with a Hebrew mapping defined it returns [:ar-PS, :ar, :he-IL, :he, :en-US] for :ar-PS" do @fallbacks.map(:"ar-PS" => :"he-IL") assert_equal [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en], @fallbacks[:"ar-PS"] end # Sami people live in several scandinavian countries. In Finnland many people # know Swedish and Finnish. Thus, it can be assumed that Sami living in # Finnland also speak Swedish and Finnish. test "with a Sami mapping defined it returns [:sms-FI, :sms, :se-FI, :se, :fi-FI, :fi, :en-US] for :sms-FI" do @fallbacks.map(:sms => [:"se-FI", :"fi-FI"]) assert_equal [:"sms-FI", :sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en], @fallbacks[:"sms-FI"] end # Austrian people understand German as spoken in Germany test "with a German mapping defined it returns [:de, :en-US] for de" do @fallbacks.map(:"de-AT" => :"de-DE") assert_equal [:de, :"en-US", :en], @fallbacks[:"de"] end test "with a German mapping defined it returns [:de-DE, :de, :en-US] for de-DE" do @fallbacks.map(:"de-AT" => :"de-DE") assert_equal [:"de-DE", :de, :"en-US", :en], @fallbacks[:"de-DE"] end test "with a German mapping defined it returns [:de-AT, :de, :de-DE, :en-US] for de-AT" do @fallbacks.map(:"de-AT" => :"de-DE") assert_equal [:"de-AT", :de, :"de-DE", :"en-US", :en], @fallbacks[:"de-AT"] end # Mapping :de => :en, :he => :en test "with a mapping :de => :en, :he => :en defined it returns [:de, :en] for :de" do assert_equal [:de, :"en-US", :en], @fallbacks[:de] end test "with a mapping :de => :en, :he => :en defined it [:he, :en] for :de" do assert_equal [:he, :"en-US", :en], @fallbacks[:he] end # Test allowing mappings that fallback to each other test "with :no => :nb, :nb => :no defined :no returns [:no, :nb, :en-US, :en]" do @fallbacks.map(:no => :nb, :nb => :no) assert_equal [:no, :nb, :"en-US", :en], @fallbacks[:no] end test "with :no => :nb, :nb => :no defined :nb returns [:nb, :no, :en-US, :en]" do @fallbacks.map(:no => :nb, :nb => :no) assert_equal [:nb, :no, :"en-US", :en], @fallbacks[:nb] end # Test I18n::Disabled is raised correctly when locale is false during fallback test "with locale equals false" do assert_raises I18n::Disabled do @fallbacks[false] end end end class I18nFallbacksHashCompatibilityTest < I18n::TestCase def setup super @fallbacks = Fallbacks.new(:'en-US', :"de-AT" => :"de-DE") end test "map is compatible with Hash#map" do result = @fallbacks.map { |key, value| [key, value] } assert_equal([[:"de-AT", [:"de-DE"]]], result) end end i18n-1.14.1/test/locale/tag/000077500000000000000000000000001443717167300153635ustar00rootroot00000000000000i18n-1.14.1/test/locale/tag/rfc4646_test.rb000066400000000000000000000121161443717167300200460ustar00rootroot00000000000000# encoding: utf-8 require 'test_helper' class I18nLocaleTagRfc4646ParserTest < I18n::TestCase include I18n::Locale test "Rfc4646::Parser given a valid tag 'de' returns an array of subtags" do assert_equal ['de', nil, nil, nil, nil, nil, nil], Tag::Rfc4646::Parser.match('de') end test "Rfc4646::Parser given a valid tag 'de-DE' returns an array of subtags" do assert_equal ['de', nil, 'DE', nil, nil, nil, nil], Tag::Rfc4646::Parser.match('de-DE') end test "Rfc4646::Parser given a valid lowercase tag 'de-latn-de-variant-x-phonebk' returns an array of subtags" do assert_equal ['de', 'latn', 'de', 'variant', nil, 'x-phonebk', nil], Tag::Rfc4646::Parser.match('de-latn-de-variant-x-phonebk') end test "Rfc4646::Parser given a valid uppercase tag 'DE-LATN-DE-VARIANT-X-PHONEBK' returns an array of subtags" do assert_equal ['DE', 'LATN', 'DE', 'VARIANT', nil, 'X-PHONEBK', nil], Tag::Rfc4646::Parser.match('DE-LATN-DE-VARIANT-X-PHONEBK') end test "Rfc4646::Parser given an invalid tag 'a-DE' it returns false" do assert_equal false, Tag::Rfc4646::Parser.match('a-DE') end test "Rfc4646::Parser given an invalid tag 'de-419-DE' it returns false" do assert_equal false, Tag::Rfc4646::Parser.match('de-419-DE') end end # Tag for the locale 'de-Latn-DE-Variant-a-ext-x-phonebk-i-klingon' class I18nLocaleTagSubtagsTest < I18n::TestCase include I18n::Locale def setup super subtags = %w(de Latn DE variant a-ext x-phonebk i-klingon) @tag = Tag::Rfc4646.new(*subtags) end test "returns 'de' as the language subtag in lowercase" do assert_equal 'de', @tag.language end test "returns 'Latn' as the script subtag in titlecase" do assert_equal 'Latn', @tag.script end test "returns 'DE' as the region subtag in uppercase" do assert_equal 'DE', @tag.region end test "returns 'variant' as the variant subtag in lowercase" do assert_equal 'variant', @tag.variant end test "returns 'a-ext' as the extension subtag" do assert_equal 'a-ext', @tag.extension end test "returns 'x-phonebk' as the privateuse subtag" do assert_equal 'x-phonebk', @tag.privateuse end test "returns 'i-klingon' as the grandfathered subtag" do assert_equal 'i-klingon', @tag.grandfathered end test "returns a formatted tag string from #to_s" do assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon', @tag.to_s end test "returns an array containing the formatted subtags from #to_a" do assert_equal %w(de Latn DE variant a-ext x-phonebk i-klingon), @tag.to_a end end # Tag inheritance class I18nLocaleTagSubtagsTest < I18n::TestCase test "#parent returns 'de-Latn-DE-variant-a-ext-x-phonebk' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk i-klingon)) assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk', tag.parent.to_s end test "#parent returns 'de-Latn-DE-variant-a-ext' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk'" do tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk)) assert_equal 'de-Latn-DE-variant-a-ext', tag.parent.to_s end test "#parent returns 'de-Latn-DE-variant' as the parent of 'de-Latn-DE-variant-a-ext'" do tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext)) assert_equal 'de-Latn-DE-variant', tag.parent.to_s end test "#parent returns 'de-Latn-DE' as the parent of 'de-Latn-DE-variant'" do tag = Tag::Rfc4646.new(*%w(de Latn DE variant)) assert_equal 'de-Latn-DE', tag.parent.to_s end test "#parent returns 'de-Latn' as the parent of 'de-Latn-DE'" do tag = Tag::Rfc4646.new(*%w(de Latn DE)) assert_equal 'de-Latn', tag.parent.to_s end test "#parent returns 'de' as the parent of 'de-Latn'" do tag = Tag::Rfc4646.new(*%w(de Latn)) assert_equal 'de', tag.parent.to_s end # TODO RFC4647 says: "If no language tag matches the request, the "default" value is returned." # where should we set the default language? # test "#parent returns '' as the parent of 'de'" do # tag = Tag::Rfc4646.new *%w(de) # assert_equal '', tag.parent.to_s # end test "#parent returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon de-Latn-DE-variant-a-ext-x-phonebk de-Latn-DE-variant-a-ext de-Latn-DE-variant de-Latn-DE de-Latn de) tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk i-klingon)) assert_equal parents, tag.self_and_parents.map(&:to_s) end test "returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon de-Latn-DE-variant-a-ext-x-phonebk de-Latn-DE-variant-a-ext de-Latn-DE-variant de-Latn-DE de-Latn de) tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk i-klingon)) assert_equal parents, tag.self_and_parents.map(&:to_s) end end i18n-1.14.1/test/locale/tag/simple_test.rb000066400000000000000000000017721443717167300202470ustar00rootroot00000000000000# encoding: utf-8 require 'test_helper' class I18nLocaleTagSimpleTest < I18n::TestCase include I18n::Locale test "returns 'de' as the language subtag in lowercase" do assert_equal %w(de Latn DE), Tag::Simple.new('de-Latn-DE').subtags end test "returns a formatted tag string from #to_s" do assert_equal 'de-Latn-DE', Tag::Simple.new('de-Latn-DE').to_s end test "returns an array containing the formatted subtags from #to_a" do assert_equal %w(de Latn DE), Tag::Simple.new('de-Latn-DE').to_a end # Tag inheritance test "#parent returns 'de-Latn' as the parent of 'de-Latn-DE'" do assert_equal 'de-Latn', Tag::Simple.new('de-Latn-DE').parent.to_s end test "#parent returns 'de' as the parent of 'de-Latn'" do assert_equal 'de', Tag::Simple.new('de-Latn').parent.to_s end test "#self_and_parents returns an array of 3 tags for 'de-Latn-DE'" do assert_equal %w(de-Latn-DE de-Latn de), Tag::Simple.new('de-Latn-DE').self_and_parents.map { |tag| tag.to_s} end end i18n-1.14.1/test/run_all.rb000066400000000000000000000007761443717167300153440ustar00rootroot00000000000000def bundle_check `bundle check` == "Resolving dependencies...\nThe Gemfile's dependencies are satisfied\n" end def execute(command) puts command system command end gemfiles = %w(Gemfile) + Dir['gemfiles/Gemfile*'].reject { |f| f.end_with?('.lock') } results = gemfiles.map do |gemfile| puts "\nBUNDLE_GEMFILE=#{gemfile}" ENV['BUNDLE_GEMFILE'] = File.expand_path("../../#{gemfile}", __FILE__) execute 'bundle install' unless bundle_check execute 'bundle exec rake test' end exit results.all? i18n-1.14.1/test/run_one.rb000066400000000000000000000004171443717167300153450ustar00rootroot00000000000000def bundle_check `bundle check` == "Resolving dependencies...\nThe Gemfile's dependencies are satisfied\n" end def execute(command) puts command system command end execute 'bundle install' unless bundle_check execute "bundle exec ruby -w -I'lib:test' #{ARGV[0]}" i18n-1.14.1/test/test_data/000077500000000000000000000000001443717167300153215ustar00rootroot00000000000000i18n-1.14.1/test/test_data/locales/000077500000000000000000000000001443717167300167435ustar00rootroot00000000000000i18n-1.14.1/test/test_data/locales/de.po000066400000000000000000000040041443717167300176710ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: version 0.0.1\n" "POT-Creation-Date: 2009-02-26 19:50+0100\n" "PO-Revision-Date: 2009-02-18 14:53+0100\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" # #: app/helpers/translation_helper.rb:3 # msgid "%{relative_time} ago" # msgstr "vor %{relative_time}" #: app/views/cars/show.html.erb:5 msgid "Axis" msgid_plural "Axis" msgstr[0] "Achse" msgstr[1] "Achsen" #: app/controllers/cars_controller.rb:47 msgid "Car was successfully created." msgstr "Auto wurde erfolgreich gespeichert" #: app/controllers/cars_controller.rb:64 msgid "Car was successfully updated." msgstr "Auto wurde erfolgreich aktualisiert" #: app/views/cars/show.html.erb:1 locale/model_attributes.rb:3 msgid "Car|Model" msgstr "Modell" #: app/views/cars/show.html.erb:3 locale/model_attributes.rb:4 msgid "Car|Wheels count" msgstr "Räderzahl" msgctxt "New car" msgid "Wheels count" msgstr "Räderzahl!" #: app/views/cars/show.html.erb:7 msgid "Created" msgstr "Erstellt" #: app/views/cars/show.html.erb:9 msgid "Month" msgstr "Monat" #: locale/model_attributes.rb:2 msgid "car" msgstr "Auto" #: locale/testlog_phrases.rb:2 msgid "this is a dynamic translation which was found thorugh gettext_test_log!" msgstr "" "Dies ist eine dynamische Übersetzung, die durch gettext_test_log " "gefunden wurde!" #: app/views/cars/nowhere_really msgid "Car|wheel" msgid_plural "Car|wheels" msgstr[0] "Rad" msgstr[1] "Räder" msgctxt "New car" msgid "wheel" msgid_plural "wheels" msgstr[0] "Rad!" msgstr[1] "Räder!" msgid "On %{count} wheel." msgid_plural "On %{count} wheels." msgstr[0] "Auf %{count} Achse." msgstr[1] "Auf %{count} Achsen." i18n-1.14.1/test/test_data/locales/en.json000066400000000000000000000000701443717167300202350ustar00rootroot00000000000000{ "en": { "foo": { "bar": "baz" } } } i18n-1.14.1/test/test_data/locales/en.rb000066400000000000000000000000731443717167300176720ustar00rootroot00000000000000# encoding: utf-8 { :en => { :fuh => { :bah => "bas" } } }i18n-1.14.1/test/test_data/locales/en.yaml000066400000000000000000000000271443717167300202300ustar00rootroot00000000000000en: foo: bar: bazi18n-1.14.1/test/test_data/locales/en.yml000066400000000000000000000000301443717167300200610ustar00rootroot00000000000000en: foo: bar: baz i18n-1.14.1/test/test_data/locales/fr.yml000066400000000000000000000000351443717167300200730ustar00rootroot00000000000000fr: animal: dog: chien i18n-1.14.1/test/test_data/locales/invalid/000077500000000000000000000000001443717167300203715ustar00rootroot00000000000000i18n-1.14.1/test/test_data/locales/invalid/empty.yml000066400000000000000000000000001443717167300222400ustar00rootroot00000000000000i18n-1.14.1/test/test_data/locales/invalid/syntax.yml000066400000000000000000000000351443717167300224400ustar00rootroot00000000000000en: foo: foo bar: baz:i18n-1.14.1/test/test_data/locales/plurals.rb000066400000000000000000000353451443717167300207640ustar00rootroot00000000000000# encoding: utf-8 { :af => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :am => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :ar => { :i18n => { :plural => { :keys => [:zero, :one, :two, :few, :many, :other], :rule => lambda { |n| n == 0 ? :zero : n == 1 ? :one : n == 2 ? :two : [3, 4, 5, 6, 7, 8, 9, 10].include?(n % 100) ? :few : [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99].include?(n % 100) ? :many : :other } } } }, :az => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :be => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :bg => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :bh => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :bn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :bo => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :bs => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :ca => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :cs => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n) ? :few : :other } } } }, :cy => { :i18n => { :plural => { :keys => [:one, :two, :many, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : n == 8 || n == 11 ? :many : :other } } } }, :da => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :de => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :dz => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :el => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :en => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :eo => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :es => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :et => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :eu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :fa => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :fi => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :fil => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :fo => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :fr => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n.between?(0, 2) && n != 2 ? :one : :other } } } }, :fur => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :fy => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ga => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :gl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :gu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :guw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :ha => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :he => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :hi => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :hr => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :hu => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :id => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :is => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :it => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :iw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ja => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :jv => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :ka => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :km => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :kn => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :ko => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :ku => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :lb => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ln => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :lt => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n % 10 == 1 && ![11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :one : [2, 3, 4, 5, 6, 7, 8, 9].include?(n % 10) && ![11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :few : :other } } } }, :lv => { :i18n => { :plural => { :keys => [:zero, :one, :other], :rule => lambda { |n| n == 0 ? :zero : n % 10 == 1 && n % 100 != 11 ? :one : :other } } } }, :mg => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :mk => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n % 10 == 1 ? :one : :other } } } }, :ml => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :mn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :mo => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 ? :few : :other } } } }, :mr => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ms => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :mt => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 || [2, 3, 4, 5, 6, 7, 8, 9, 10].include?(n % 100) ? :few : [11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :many : :other } } } }, :my => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :nah => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :nb => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ne => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :nl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :nn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :no => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :nso => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :oc => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :om => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :or => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :pa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :pap => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :pl => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : (n != 1 && [0, 1].include?(n % 10)) || [5, 6, 7, 8, 9].include?(n % 10) || [12, 13, 14].include?(n % 100) ? :many : :other } } } }, :ps => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :pt => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :"pt-PT" => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ro => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 ? :few : :other } } } }, :ru => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :se => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :sh => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :sk => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n) ? :few : :other } } } }, :sl => { :i18n => { :plural => { :keys => [:one, :two, :few, :other], :rule => lambda { |n| n % 100 == 1 ? :one : n % 100 == 2 ? :two : [3, 4].include?(n % 100) ? :few : :other } } } }, :sma => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :smi => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :smj => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :smn => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :sms => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, :so => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :sq => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :sr => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :sv => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :sw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :ta => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :te => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :th => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :ti => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :tk => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :tl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :to => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :tr => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :uk => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, :ur => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, :vi => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :wa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, :yo => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :zh => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, :zu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } } } i18n-1.14.1/test/test_helper.rb000066400000000000000000000024321443717167300162150ustar00rootroot00000000000000require 'minitest/autorun' require 'bundler/setup' require 'i18n' require 'mocha/setup' require 'test_declarative' class I18n::TestCase < Minitest::Test def assert_nothing_raised(*args) yield end def self.key_value? defined?(ActiveSupport) end def setup super I18n.load_path = nil I18n.enforce_available_locales = false end def teardown I18n.locale = nil I18n.default_locale = nil I18n.load_path = nil I18n.available_locales = nil I18n.backend = nil I18n.default_separator = nil I18n.enforce_available_locales = true I18n.fallbacks = nil if I18n.respond_to?(:fallbacks=) super end protected def translations I18n.backend.instance_variable_get(:@translations) end def store_translations(locale, data, options = I18n::EMPTY_HASH) I18n.backend.store_translations(locale, data, options) end def locales_dir File.dirname(__FILE__) + '/test_data/locales' end def stub_const(klass, constant, new_value) old_value = klass.const_get(constant) klass.send(:remove_const, constant) klass.const_set(constant, new_value) yield ensure klass.send(:remove_const, constant) klass.const_set(constant, old_value) end end class DummyRackApp def call(env) I18n.locale = :es end end i18n-1.14.1/test/utils_test.rb000066400000000000000000000016521443717167300161010ustar00rootroot00000000000000require 'test_helper' class I18nUtilsTest < I18n::TestCase test ".deep_symbolize_keys" do hash = { 'foo' => { 'bar' => { 'baz' => 'bar' } } } expected = { :foo => { :bar => { :baz => 'bar' } } } assert_equal expected, I18n::Utils.deep_symbolize_keys(hash) end test "#deep_symbolize_keys with numeric keys" do hash = { 1 => { 2 => { 3 => 'bar' } } } expected = { 1 => { 2 => { 3 => 'bar' } } } assert_equal expected, I18n::Utils.deep_symbolize_keys(hash) end test "#except" do hash = { :foo => 'bar', :baz => 'bar' } expected = { :foo => 'bar' } assert_equal expected, I18n::Utils.except(hash, :baz) end test "#deep_merge!" do hash = { :foo => { :bar => { :baz => 'bar' } }, :baz => 'bar' } I18n::Utils.deep_merge!(hash, :foo => { :bar => { :baz => 'foo' } }) expected = { :foo => { :bar => { :baz => 'foo' } }, :baz => 'bar' } assert_equal expected, hash end end