diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..9711239b --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,21 @@ +## 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 neccessary + +* Fill this out! + +---- + +Bonus points for providing an application or a small code example which reproduces the issue. + +Thanks! :heart: diff --git a/.travis.yml b/.travis.yml index 5b2a84e8..77aa4460 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,48 @@ +before_install: + - gem install bundler + rvm: - - 1.8.7 - - 1.9.2 - 1.9.3 - 2.0.0 - - ree + - 2.1.8 + - 2.2.4 + - 2.3.1 + - ruby-head - rbx - jruby gemfile: - - ci/Gemfile.no-rails - - ci/Gemfile.rails-2.3.x - - ci/Gemfile.rails-3.x + - Gemfile + - gemfiles/Gemfile.rails-3.2.x + - gemfiles/Gemfile.rails-4.0.x + - gemfiles/Gemfile.rails-4.1.x + - gemfiles/Gemfile.rails-4.2.x + - gemfiles/Gemfile.rails-5.0.x + - gemfiles/Gemfile.rails-master + +matrix: + exclude: + # Rails 5+ requires Ruby >= 2.2.2 + - rvm: 1.9.3 + gemfile: gemfiles/Gemfile.rails-master + - rvm: 2.0.0 + gemfile: gemfiles/Gemfile.rails-master + - rvm: 2.1.8 + gemfile: gemfiles/Gemfile.rails-master + - rvm: 1.9.3 + gemfile: gemfiles/Gemfile.rails-5.0.x + - rvm: 2.0.0 + gemfile: gemfiles/Gemfile.rails-5.0.x + - rvm: 2.1.8 + gemfile: gemfiles/Gemfile.rails-5.0.x + # activesupport has a dependency on json, which does not build on this version + - rvm: ruby-head + gemfile: gemfiles/Gemfile.rails-4.1.x + # activesupport has a dependency on json, which does not build on this version + - rvm: ruby-head + gemfile: gemfiles/Gemfile.rails-4.2.x + allow_failures: + - rvm: 2.2 + - rvm: rbx + - rvm: jruby + fast_finish: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..7608c5f9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# master + +* `Hash#slice` ignores non existing keys. + +# 0.7.0 + +* Drop support to Ruby 1.8.7 / REE +* Drop support to Rails 2.3 / 3.0 / 3.1 +* Remove deprecated stuff: + - Setting `:default_exception_hander` Symbol to `I18n.exception_handler`. + - `normalize_translation_keys` in favor of `normalize_keys`. + - `:rescue_format` option on the exception handler. + - `enforce_available_locales` now defaults to true with no deprecation message. diff --git a/Gemfile b/Gemfile deleted file mode 120000 index bcfd9a12..00000000 --- a/Gemfile +++ /dev/null @@ -1 +0,0 @@ -ci/Gemfile.no-rails \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..92f28620 --- /dev/null +++ b/Gemfile @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +gemspec + +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/Gemfile.lock b/Gemfile.lock index 6dde73ae..2ede6301 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,28 @@ +PATH + remote: . + specs: + i18n (0.8.0.beta1) + GEM remote: https://rubygems.org/ specs: - mocha (0.9.9) - rake - rake (0.8.7) - test_declarative (0.0.4) + metaclass (0.0.4) + minitest (5.10.1) + mocha (1.2.1) + metaclass (~> 0.0.1) + rake (11.3.0) + test_declarative (0.0.5) PLATFORMS java ruby DEPENDENCIES + i18n! + minitest mocha + rake test_declarative + +BUNDLED WITH + 1.13.6 diff --git a/README.textile b/README.md similarity index 50% rename from README.textile rename to README.md index 57b79c10..ae9faf7d 100644 --- a/README.textile +++ b/README.md @@ -1,6 +1,6 @@ -h1. Ruby I18n +# Ruby I18n -!https://secure.travis-ci.org/svenfuchs/i18n.png?branch=master(Build Status)!:http://travis-ci.org/svenfuchs/i18n +[![Build Status](https://api.travis-ci.org/svenfuchs/i18n.svg?branch=master)](https://travis-ci.org/svenfuchs/i18n) Ruby Internationalization and localization solution. @@ -22,7 +22,7 @@ Pluggable features: * Cache * Pluralization: lambda pluralizers stored as translation data * Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation) -* Gettext support +* [Gettext support](https://github.com/svenfuchs/i18n/wiki/Gettext) * Translation metadata Alternative backends: @@ -31,43 +31,20 @@ Alternative backends: * ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs) * KeyValue (uses active_support/json and cannot store procs) -For more information and lots of resources see: "http://ruby-i18n.org/wiki":http://ruby-i18n.org/wiki +For more information and lots of resources see [the 'Resources' page on the wiki](https://github.com/svenfuchs/i18n/wiki/Resources). -h2. Installation +## Installation +``` gem install i18n +``` -h4. Rails version warning - -On Rails < 2.3.6 the method I18n.localize will fail with MissingInterpolationArgument (issue "20":http://github.com/svenfuchs/i18n/issues/issue/20). Upgrade to Rails 2.3.6 or higher (2.3.8 preferably) is recommended. - -h3. Installation on Rails < 2.3.5 (deprecated) - -Up to version 2.3.4 Rails will not accept i18n gems > 0.1.3. There is an unpacked -gem inside of active_support/lib/vendor which gets loaded unless gem 'i18n', '~> 0.1.3'. -This requirement is relaxed in "6da03653":http://github.com/rails/rails/commit/6da03653 - -The new i18n gem can be loaded from vendor/plugins like this: - -
-  def reload_i18n!
-    raise "Move to i18n version 0.2.0 or greater" if Rails.version > "2.3.4"
-
-    $:.grep(/i18n/).each { |path| $:.delete(path) }
-    I18n::Backend.send :remove_const, "Simple"
-    $: << Rails.root.join('vendor', 'plugins', 'i18n', 'lib').to_s
-  end
-
- -Then you can `reload_i18n!` inside an i18n initializer. - -h2. Tests +## Tests You can run tests both with * `rake test` or just `rake` -* run any test file directly, e.g. `ruby -Ilib -Itest test/api/simple_test.rb` -* run all tests with `ruby -Ilib -Itest test/all.rb` +* run any test file directly, e.g. `ruby -Ilib:test test/api/simple_test.rb` You can run all tests against all Gemfiles with @@ -85,21 +62,21 @@ 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. +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. -h2. Authors +## Authors -* "Sven Fuchs":http://www.artweb-design.de -* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey -* "Stephan Soller":http://www.arkanis-development.de -* "Saimon Moore":http://saimonmoore.net -* "Matt Aimonetti":http://railsontherun.com +* [Sven Fuchs](http://www.artweb-design.de) +* [Joshua Harvey](http://www.workingwithrails.com/person/759-joshua-harvey) +* [Stephan Soller](http://www.arkanis-development.de) +* [Saimon Moore](http://saimonmoore.net) +* [Matt Aimonetti](https://matt.aimonetti.net/) -h2. Contributors +## Contributors -http://github.com/svenfuchs/i18n/contributors +https://github.com/svenfuchs/i18n/graphs/contributors -h2. License +## License MIT License. See the included MIT-LICENSE file. diff --git a/Rakefile b/Rakefile index 94036070..bdc6683c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,4 @@ +require 'bundler/gem_tasks' require 'rake/testtask' task :default => [:test] @@ -5,23 +6,8 @@ task :default => [:test] Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.libs << 'test' - t.pattern = "#{File.dirname(__FILE__)}/test/all.rb" + t.pattern = "test/**/*_test.rb" t.verbose = true t.warning = true end Rake::Task['test'].comment = "Run all i18n tests" - -# require "rake/gempackagetask" -# require "rake/clean" -# CLEAN << "pkg" << "doc" << "coverage" << ".yardoc" -# -# Rake::GemPackageTask.new(eval(File.read("i18n.gemspec"))) { |pkg| } -# -# begin -# require "yard" -# YARD::Rake::YardocTask.new do |t| -# t.options = ["--output-dir=doc"] -# t.options << "--files" << ["CHANGELOG.textile", "contributors.txt", "MIT-LICENSE"].join(",") -# end -# rescue LoadError -# end diff --git a/benchmark/example.yml b/benchmark/example.yml index da5f9fe7..650c3905 100644 --- a/benchmark/example.yml +++ b/benchmark/example.yml @@ -60,7 +60,10 @@ en: 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 ] + order: + - :year, + - :month, + - :day time: formats: @@ -131,7 +134,10 @@ en: 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 ] + order: + - :year + - :month + - :day time: formats: @@ -145,4 +151,4 @@ en: array: words_connector: ", " two_words_connector: " and " - last_word_connector: ", and " \ No newline at end of file + last_word_connector: ", and " diff --git a/benchmark/run.rb b/benchmark/run.rb index 0a59eeb1..b37c3b7f 100644 --- a/benchmark/run.rb +++ b/benchmark/run.rb @@ -1,11 +1,11 @@ -#! /usr/bin/ruby +#!/usr/bin/ruby $:.unshift File.expand_path('../../lib', __FILE__) +require 'bundler/setup' require 'i18n' require 'benchmark' require 'yaml' -DATA_STORES = ARGV.delete("-ds") N = (ARGV.shift || 1000).to_i YAML_HASH = YAML.load_file(File.expand_path("example.yml", File.dirname(__FILE__))) @@ -16,20 +16,16 @@ module Backends include I18n::Backend::InterpolationCompiler end.new - if DATA_STORES - require 'rubygems' - require File.expand_path('../../test/test_setup/active_record', __FILE__) - require File.expand_path('../../test/test_setup/rufus_tokyo', __FILE__) - - Test.setup_active_record - ActiveRecord = I18n::Backend::ActiveRecord.new if defined?(::ActiveRecord) - - Test.setup_rufus_tokyo - TokyoCabinet = I18n::Backend::KeyValue.new(Rufus::Tokyo::Cabinet.new("*"), true) if defined?(::Rufus::Tokyo) + 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 ActiveRecord TokyoCabinet) +ORDER = %w(Simple Interpolation KeyValue) ORDER.map!(&:to_sym) if RUBY_VERSION > '1.9' module Benchmark @@ -62,7 +58,7 @@ def self.measure_objects(n, &blk) puts "=> #{backend_name}\n\n" Benchmark.rt "store", 1 do - I18n.backend.store_translations *(YAML_HASH.to_a.first) + I18n.backend.store_translations(*YAML_HASH.to_a.first) end I18n.backend.translate :en, :first @@ -114,4 +110,4 @@ def self.measure_objects(n, &blk) end puts "Running memoized benchmarks with N = #{N}\n\n" -(ORDER & Backends.constants).each(&benchmarker) \ No newline at end of file +(ORDER & Backends.constants).each(&benchmarker) diff --git a/ci/Gemfile.no-rails b/ci/Gemfile.no-rails deleted file mode 100644 index 449613e8..00000000 --- a/ci/Gemfile.no-rails +++ /dev/null @@ -1,5 +0,0 @@ -source 'https://rubygems.org' - -gem 'mocha' -gem 'test_declarative' - diff --git a/ci/Gemfile.no-rails.lock b/ci/Gemfile.no-rails.lock deleted file mode 100644 index 569b32a7..00000000 --- a/ci/Gemfile.no-rails.lock +++ /dev/null @@ -1,14 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - mocha (0.9.9) - rake - rake (0.8.7) - test_declarative (0.0.4) - -PLATFORMS - ruby - -DEPENDENCIES - mocha - test_declarative diff --git a/ci/Gemfile.rails-2.3.x b/ci/Gemfile.rails-2.3.x deleted file mode 100644 index 3227b3ae..00000000 --- a/ci/Gemfile.rails-2.3.x +++ /dev/null @@ -1,9 +0,0 @@ -source 'https://rubygems.org' - -gem 'activesupport', '~> 2.3' -gem 'sqlite3-ruby' -gem 'mocha' -gem 'test_declarative' -gem 'rufus-tokyo' -gem 'ffi' - diff --git a/ci/Gemfile.rails-2.3.x.lock b/ci/Gemfile.rails-2.3.x.lock deleted file mode 100644 index 60106158..00000000 --- a/ci/Gemfile.rails-2.3.x.lock +++ /dev/null @@ -1,23 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - activesupport (2.3.10) - ffi (0.6.3) - rake (>= 0.8.7) - mocha (0.9.9) - rake - rake (0.8.7) - rufus-tokyo (1.0.7) - sqlite3-ruby (1.3.2) - test_declarative (0.0.4) - -PLATFORMS - ruby - -DEPENDENCIES - activesupport (~> 2.3) - ffi - mocha - rufus-tokyo - sqlite3-ruby - test_declarative diff --git a/ci/Gemfile.rails-3.x b/ci/Gemfile.rails-3.x deleted file mode 100644 index d0947e55..00000000 --- a/ci/Gemfile.rails-3.x +++ /dev/null @@ -1,9 +0,0 @@ -source 'https://rubygems.org' - -gem 'activesupport', '~> 3.0.0' -gem 'sqlite3-ruby' -gem 'mocha' -gem 'test_declarative' -gem 'rufus-tokyo' -gem 'ffi' - diff --git a/ci/Gemfile.rails-3.x.lock b/ci/Gemfile.rails-3.x.lock deleted file mode 100644 index de7aad6a..00000000 --- a/ci/Gemfile.rails-3.x.lock +++ /dev/null @@ -1,23 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - activesupport (3.0.3) - ffi (0.6.3) - rake (>= 0.8.7) - mocha (0.9.9) - rake - rake (0.8.7) - rufus-tokyo (1.0.7) - sqlite3-ruby (1.3.2) - test_declarative (0.0.4) - -PLATFORMS - ruby - -DEPENDENCIES - activesupport (~> 3.0.0) - ffi - mocha - rufus-tokyo - sqlite3-ruby - test_declarative diff --git a/gemfiles/Gemfile.rails-3.2.x b/gemfiles/Gemfile.rails-3.2.x new file mode 100644 index 00000000..50bd10ee --- /dev/null +++ b/gemfiles/Gemfile.rails-3.2.x @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'activesupport', '~> 3.2.0' +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/gemfiles/Gemfile.rails-3.2.x.lock b/gemfiles/Gemfile.rails-3.2.x.lock new file mode 100644 index 00000000..1aa3d4f4 --- /dev/null +++ b/gemfiles/Gemfile.rails-3.2.x.lock @@ -0,0 +1,32 @@ +PATH + remote: .. + specs: + i18n (0.7.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.21) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + metaclass (0.0.4) + minitest (5.9.1) + mocha (1.1.0) + metaclass (~> 0.0.1) + multi_json (1.11.0) + rake (10.4.2) + test_declarative (0.0.5) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (~> 3.2.0) + i18n! + minitest + mocha + rake + test_declarative + +BUNDLED WITH + 1.13.5 diff --git a/gemfiles/Gemfile.rails-4.0.x b/gemfiles/Gemfile.rails-4.0.x new file mode 100644 index 00000000..33e634b5 --- /dev/null +++ b/gemfiles/Gemfile.rails-4.0.x @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'activesupport', '~> 4.0.0' +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/gemfiles/Gemfile.rails-4.0.x.lock b/gemfiles/Gemfile.rails-4.0.x.lock new file mode 100644 index 00000000..d14c35f1 --- /dev/null +++ b/gemfiles/Gemfile.rails-4.0.x.lock @@ -0,0 +1,37 @@ +PATH + remote: .. + specs: + i18n (0.7.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.0.13) + i18n (~> 0.6, >= 0.6.9) + minitest (~> 4.2) + multi_json (~> 1.3) + thread_safe (~> 0.1) + tzinfo (~> 0.3.37) + metaclass (0.0.4) + minitest (4.7.5) + mocha (1.1.0) + metaclass (~> 0.0.1) + multi_json (1.11.0) + rake (10.4.2) + test_declarative (0.0.5) + thread_safe (0.3.5) + tzinfo (0.3.43) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (~> 4.0.0) + i18n! + minitest + mocha + rake + test_declarative + +BUNDLED WITH + 1.13.5 diff --git a/gemfiles/Gemfile.rails-4.1.x b/gemfiles/Gemfile.rails-4.1.x new file mode 100644 index 00000000..49093b07 --- /dev/null +++ b/gemfiles/Gemfile.rails-4.1.x @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'activesupport', '~> 4.1.0' +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/gemfiles/Gemfile.rails-4.1.x.lock b/gemfiles/Gemfile.rails-4.1.x.lock new file mode 100644 index 00000000..93dc7ae0 --- /dev/null +++ b/gemfiles/Gemfile.rails-4.1.x.lock @@ -0,0 +1,38 @@ +PATH + remote: .. + specs: + i18n (0.7.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.1.10) + i18n (~> 0.6, >= 0.6.9) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.1) + tzinfo (~> 1.1) + json (1.8.2) + metaclass (0.0.4) + minitest (5.5.1) + mocha (1.1.0) + metaclass (~> 0.0.1) + rake (10.4.2) + test_declarative (0.0.5) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (~> 4.1.0) + i18n! + minitest + mocha + rake + test_declarative + +BUNDLED WITH + 1.13.5 diff --git a/gemfiles/Gemfile.rails-4.2.x b/gemfiles/Gemfile.rails-4.2.x new file mode 100644 index 00000000..8d0dc309 --- /dev/null +++ b/gemfiles/Gemfile.rails-4.2.x @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'activesupport', '~> 4.2.0' +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/gemfiles/Gemfile.rails-4.2.x.lock b/gemfiles/Gemfile.rails-4.2.x.lock new file mode 100644 index 00000000..6459e822 --- /dev/null +++ b/gemfiles/Gemfile.rails-4.2.x.lock @@ -0,0 +1,38 @@ +PATH + remote: .. + specs: + i18n (0.7.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.2.1) + i18n (~> 0.7) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + json (1.8.2) + metaclass (0.0.4) + minitest (5.5.1) + mocha (1.1.0) + metaclass (~> 0.0.1) + rake (10.4.2) + test_declarative (0.0.5) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (~> 4.2.0) + i18n! + minitest + mocha + rake + test_declarative + +BUNDLED WITH + 1.13.5 diff --git a/gemfiles/Gemfile.rails-5.0.x b/gemfiles/Gemfile.rails-5.0.x new file mode 100644 index 00000000..bddfa474 --- /dev/null +++ b/gemfiles/Gemfile.rails-5.0.x @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'activesupport', '~> 5.0.0' +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/gemfiles/Gemfile.rails-5.0.x.lock b/gemfiles/Gemfile.rails-5.0.x.lock new file mode 100644 index 00000000..74442adf --- /dev/null +++ b/gemfiles/Gemfile.rails-5.0.x.lock @@ -0,0 +1,37 @@ +PATH + remote: .. + specs: + i18n (0.7.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (5.0.0.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + concurrent-ruby (1.0.2) + metaclass (0.0.4) + minitest (5.9.1) + mocha (1.2.1) + metaclass (~> 0.0.1) + rake (11.3.0) + test_declarative (0.0.5) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (~> 5.0.0) + i18n! + minitest + mocha + rake + test_declarative + +BUNDLED WITH + 1.13.6 diff --git a/gemfiles/Gemfile.rails-master b/gemfiles/Gemfile.rails-master new file mode 100644 index 00000000..0fef9099 --- /dev/null +++ b/gemfiles/Gemfile.rails-master @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'activesupport', github: 'rails/rails', branch: 'master' +gem 'mocha' +gem 'test_declarative' +gem 'rake' +gem 'minitest' diff --git a/gemfiles/Gemfile.rails-master.lock b/gemfiles/Gemfile.rails-master.lock new file mode 100644 index 00000000..f5b9f7bb --- /dev/null +++ b/gemfiles/Gemfile.rails-master.lock @@ -0,0 +1,43 @@ +GIT + remote: git://github.com/rails/rails.git + revision: da23e125f8d755917b08f5cca1f7fe1ff38c8b7e + branch: master + specs: + activesupport (5.1.0.alpha) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + +PATH + remote: .. + specs: + i18n (0.7.0) + +GEM + remote: https://rubygems.org/ + specs: + concurrent-ruby (1.0.2) + metaclass (0.0.4) + minitest (5.5.1) + mocha (1.1.0) + metaclass (~> 0.0.1) + rake (10.4.2) + test_declarative (0.0.5) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport! + i18n! + minitest + mocha + rake + test_declarative + +BUNDLED WITH + 1.13.6 diff --git a/i18n.gemspec b/i18n.gemspec index 4c22bb77..5f20c15d 100644 --- a/i18n.gemspec +++ b/i18n.gemspec @@ -13,14 +13,10 @@ Gem::Specification.new do |s| s.description = "New wave Internationalization support for Ruby." s.license = "MIT" - s.files = Dir.glob("{ci,lib,test}/**/**") + %w(README.textile MIT-LICENSE) + s.files = Dir.glob("{gemfiles,lib,test}/**/**") + %w(README.md MIT-LICENSE) s.platform = Gem::Platform::RUBY s.require_path = 'lib' s.rubyforge_project = '[none]' s.required_rubygems_version = '>= 1.3.5' - - s.add_development_dependency 'activesupport', '>= 3.0.0' - s.add_development_dependency 'sqlite3' - s.add_development_dependency 'mocha' - s.add_development_dependency 'test_declarative' + s.required_ruby_version = '>= 1.9.3' end diff --git a/lib/i18n.rb b/lib/i18n.rb old mode 100755 new mode 100644 index a97f7b85..f696d913 --- a/lib/i18n.rb +++ b/lib/i18n.rb @@ -9,10 +9,10 @@ module I18n autoload :Locale, 'i18n/locale' autoload :Tests, 'i18n/tests' - RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :rescue_format] + RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :deep_interpolation] RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/ - extend(Module.new { + module Base # Gets I18n configuration object. def config Thread.current[:i18n_config] ||= I18n::Config.new @@ -41,6 +41,7 @@ def #{method}=(value) # Rails development environment. Backends can implement whatever strategy # is useful. def reload! + config.clear_available_locales_set config.backend.reload! end @@ -271,26 +272,19 @@ def normalize_keys(locale, key, scope, separator = nil) keys end - # Returns true when the passed locale is in I18.available_locales. - # Returns false otherwise. - # Compare with Strings as `locale` may be coming from user input + # 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.available_locales.map(&:to_s).include?(locale.to_s) + I18n.config.available_locales_set.include?(locale) end - # Raises an InvalidLocale exception when the passed locale is not - # included in I18n.available_locales. - # Returns false otherwise + # Raises an InvalidLocale exception when the passed locale is not available. def enforce_available_locales!(locale) - handle_enforce_available_locales_deprecation - if config.enforce_available_locales raise I18n::InvalidLocale.new(locale) if !locale_available?(locale) end end - # making these private until Ruby 1.9.2 can send to protected methods again - # see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280 private # Any exceptions thrown in translate will be sent to the @@exception_handler @@ -303,18 +297,18 @@ def enforce_available_locales!(locale) # # Examples: # - # I18n.exception_handler = :default_exception_handler # this is the default - # I18n.default_exception_handler(exception, locale, key, options) # will be called like this + # 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 + # 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) + raise exception.respond_to?(:to_exception) ? exception.to_exception : exception when :throw throw :exception, exception else @@ -343,25 +337,7 @@ def normalize_key(key, separator) def normalized_key_cache @normalized_key_cache ||= Hash.new { |h,k| h[k] = {} } end + end - # DEPRECATED. Use I18n.normalize_keys instead. - def normalize_translation_keys(locale, key, scope, separator = nil) - puts "I18n.normalize_translation_keys is deprecated. Please use the class I18n.normalize_keys instead." - normalize_keys(locale, key, scope, separator) - end - - # DEPRECATED. Please use the I18n::ExceptionHandler class instead. - def default_exception_handler(exception, locale, key, options) - puts "I18n.default_exception_handler is deprecated. Please use the class I18n::ExceptionHandler instead " + - "(an instance of which is set to I18n.exception_handler by default)." - exception.is_a?(MissingTranslation) ? exception.message : raise(exception) - end - - def handle_enforce_available_locales_deprecation - if config.enforce_available_locales.nil? && !@unenforced_available_locales_deprecation - $stderr.puts "[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message." - @unenforced_available_locales_deprecation = true - end - end - }) + extend Base end diff --git a/lib/i18n/backend/base.rb b/lib/i18n/backend/base.rb index dddb145a..668a4221 100644 --- a/lib/i18n/backend/base.rb +++ b/lib/i18n/backend/base.rb @@ -1,6 +1,6 @@ require 'yaml' require 'i18n/core_ext/hash' -require 'i18n/core_ext/kernel/surpress_warnings' +require 'i18n/core_ext/kernel/suppress_warnings' module I18n module Backend @@ -25,20 +25,32 @@ def translate(locale, key, options = {}) raise InvalidLocale.new(locale) unless locale entry = key && lookup(locale, key, options[:scope], options) - if options.empty? - entry = resolve(locale, key, entry, options) + if entry.nil? && options.key?(:default) + entry = default(locale, key, options[:default], options) else - count, default = options.values_at(:count, :default) - values = options.except(*RESERVED_KEYS) - entry = entry.nil? && default ? - default(locale, key, default, options) : resolve(locale, key, entry, options) + entry = resolve(locale, key, entry, options) + end + + if entry.nil? + if (options.key?(:default) && !options[:default].nil?) || !options.key?(:default) + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) + end end - throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil? entry = entry.dup if entry.is_a?(String) + count = options[:count] entry = pluralize(locale, entry, count) if count - entry = interpolate(locale, entry, values) if values + + deep_interpolation = options[:deep_interpolation] + values = options.except(*RESERVED_KEYS) + if values + entry = if deep_interpolation + deep_interpolate(locale, entry, values) + else + interpolate(locale, entry, values) + end + end entry end @@ -50,6 +62,9 @@ def exists?(locale, key) # 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 = {}) + 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 @@ -59,18 +74,7 @@ def localize(locale, object, format = :default, options = {}) format = I18n.t(:"#{type}.formats.#{key}", options) end - # format = resolve(locale, object, format, options) - format = 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.day_names", :locale => locale, :format => format)[object.wday] - when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon] - when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon] - when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour - when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour - end - end - + format = translate_localization_format(locale, object, format, options) object.strftime(format) end @@ -81,7 +85,6 @@ def available_locales end def reload! - @skip_syntax_deprecation = false end protected @@ -156,6 +159,30 @@ def interpolate(locale, string, values = {}) 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 = {}) + 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 @@ -185,6 +212,19 @@ def load_yml(filename) 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.day_names", :locale => locale, :format => format)[object.wday] + when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon] + when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon] + when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour + when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour + end + end + end end end end diff --git a/lib/i18n/backend/cache.rb b/lib/i18n/backend/cache.rb index 3c456ff9..1aa64348 100644 --- a/lib/i18n/backend/cache.rb +++ b/lib/i18n/backend/cache.rb @@ -13,9 +13,13 @@ # 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 assumes that you only pass values to -# I18n.translate that return a valid key from #hash (see -# http://www.ruby-doc.org/core/classes/Object.html#M000337). +# The cache_key implementation by default assumes you pass values that return +# a valid key from #hash (see +# http://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can +# configure your own digest method via which responds to #hexdigest (see +# http://ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html): +# +# I18n.cache_key_digest = Digest::MD5.new # # If you use a lambda as a default value in your translation like this: # @@ -37,6 +41,7 @@ module I18n class << self @@cache_store = nil @@cache_namespace = nil + @@cache_key_digest = nil def cache_store @@cache_store @@ -54,6 +59,14 @@ 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 @@ -76,7 +89,8 @@ def fetch(cache_key, &block) end def _fetch(cache_key, &block) - result = I18n.cache_store.read(cache_key) and return result + 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 @@ -84,13 +98,17 @@ def _fetch(cache_key, &block) def cache_key(locale, key, options) # This assumes that only simple, native Ruby values are passed to I18n.translate. - "i18n/#{I18n.cache_namespace}/#{locale}/#{key.hash}/#{USE_INSPECT_HASH ? options.inspect.hash : options.hash}" + "i18n/#{I18n.cache_namespace}/#{locale}/#{digest_item(key)}/#{USE_INSPECT_HASH ? digest_item(options.inspect) : digest_item(options)}" end private # In Ruby < 1.9 the following is true: { :foo => 1, :bar => 2 }.hash == { :foo => 2, :bar => 1 }.hash # Therefore we must use the hash of the inspect string instead to avoid cache key colisions. USE_INSPECT_HASH = RUBY_VERSION <= "1.9" + + def digest_item(key) + I18n.cache_key_digest ? I18n.cache_key_digest.hexdigest(key.to_s) : key.hash + end end end end diff --git a/lib/i18n/backend/chain.rb b/lib/i18n/backend/chain.rb index cb6ad43b..2a45cad3 100644 --- a/lib/i18n/backend/chain.rb +++ b/lib/i18n/backend/chain.rb @@ -45,8 +45,8 @@ def translate(locale, key, default_options = {}) options = default_options if backend == backends.last translation = backend.translate(locale, key, options) if namespace_lookup?(translation, options) - namespace = translation.merge(namespace || {}) - elsif !translation.nil? + namespace = _deep_merge(translation, namespace || {}) + elsif !translation.nil? || (options.key?(:default) && options[:default].nil?) return translation end end @@ -75,6 +75,20 @@ def localize(locale, object, format = :default, options = {}) 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 diff --git a/lib/i18n/backend/fallbacks.rb b/lib/i18n/backend/fallbacks.rb index 6b41a752..7683b5e6 100644 --- a/lib/i18n/backend/fallbacks.rb +++ b/lib/i18n/backend/fallbacks.rb @@ -36,18 +36,27 @@ module Fallbacks # it is evaluated last after all the fallback locales have been tried. def translate(locale, key, options = {}) - return super if options[:fallback] + return super unless options.fetch(:fallback, true) + return super if (@fallback_locked ||= false) default = extract_non_symbol_default!(options) if options[:default] - options[:fallback] = true - I18n.fallbacks[locale].each do |fallback| - catch(:exception) do - result = super(fallback, key, options) - return result unless result.nil? + begin + options[:fallback] = true + I18n.fallbacks[locale].each do |fallback| + begin + catch(:exception) do + result = super(fallback, key, options) + return result if (result.nil? && options.key?(:default) && options[:default].nil?) || !result.nil? + end + rescue I18n::InvalidLocale + # we do nothing when the locale is invalid, as this is a fallback anyways. + end end + ensure + options.delete(:fallback) end - options.delete(:fallback) - return super(locale, key, options.merge(:default => default)) + + return super(locale, key, options.merge(:default => default)) if default throw(:exception, I18n::MissingTranslation.new(locale, key, options)) end @@ -60,6 +69,17 @@ def extract_non_symbol_default!(options) return first_non_symbol_default end + def exists?(locale, key) + 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 end end end diff --git a/lib/i18n/backend/gettext.rb b/lib/i18n/backend/gettext.rb index c357a6d4..f41df685 100644 --- a/lib/i18n/backend/gettext.rb +++ b/lib/i18n/backend/gettext.rb @@ -1,24 +1,33 @@ require 'i18n/gettext' require 'i18n/gettext/po_parser' -# 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. 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) diff --git a/lib/i18n/backend/interpolation_compiler.rb b/lib/i18n/backend/interpolation_compiler.rb index c5440704..0e3a603a 100644 --- a/lib/i18n/backend/interpolation_compiler.rb +++ b/lib/i18n/backend/interpolation_compiler.rb @@ -12,7 +12,7 @@ # # 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 and 1.8.7). +# (jRuby, Rubinius). module I18n module Backend module InterpolationCompiler diff --git a/lib/i18n/backend/key_value.rb b/lib/i18n/backend/key_value.rb index df80718b..a79fc1aa 100644 --- a/lib/i18n/backend/key_value.rb +++ b/lib/i18n/backend/key_value.rb @@ -1,6 +1,5 @@ require 'i18n/backend/base' require 'active_support/json' -require 'active_support/ordered_hash' # active_support/json/encoding uses ActiveSupport::OrderedHash but does not require it module I18n module Backend diff --git a/lib/i18n/backend/metadata.rb b/lib/i18n/backend/metadata.rb index 52c0a295..a8d922a0 100644 --- a/lib/i18n/backend/metadata.rb +++ b/lib/i18n/backend/metadata.rb @@ -21,11 +21,15 @@ class << self def included(base) Object.class_eval do def translation_metadata - @translation_metadata ||= {} + unless self.frozen? + @translation_metadata ||= {} + else + {} + end end def translation_metadata=(translation_metadata) - @translation_metadata = translation_metadata + @translation_metadata = translation_metadata unless self.frozen? end end unless Object.method_defined?(:translation_metadata) end diff --git a/lib/i18n/backend/simple.rb b/lib/i18n/backend/simple.rb index 95ffb6ab..00a887b4 100644 --- a/lib/i18n/backend/simple.rb +++ b/lib/i18n/backend/simple.rb @@ -63,7 +63,7 @@ def translations end # Looks up a translation from the translations hash. Returns nil if - # eiher key is nil, or locale, scope or key do not exist as a key in the + # 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). diff --git a/lib/i18n/backend/transliterator.rb b/lib/i18n/backend/transliterator.rb index 2ee983de..538dd600 100644 --- a/lib/i18n/backend/transliterator.rb +++ b/lib/i18n/backend/transliterator.rb @@ -71,13 +71,13 @@ class HashTransliterator def initialize(rule = nil) @rule = rule - add DEFAULT_APPROXIMATIONS.dup + add_default_approximations add rule if rule end - def transliterate(string, replacement = nil) + def transliterate(string, replacement = DEFAULT_REPLACEMENT_CHAR) string.gsub(/[^\x00-\x7f]/u) do |char| - approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR + approximations[char] || replacement end end @@ -87,6 +87,12 @@ 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| @@ -96,4 +102,4 @@ def add(hash) end end end -end \ No newline at end of file +end diff --git a/lib/i18n/config.rb b/lib/i18n/config.rb index 76293a1a..fa16667f 100644 --- a/lib/i18n/config.rb +++ b/lib/i18n/config.rb @@ -1,15 +1,17 @@ +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 - @locale ||= default_locale + defined?(@locale) && @locale ? @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.to_sym rescue nil + @locale = locale && locale.to_sym end # Returns the current backend. Defaults to +Backend::Simple+. @@ -30,7 +32,7 @@ def default_locale # Sets the current default locale. Used to set a custom default locale. def default_locale=(locale) I18n.enforce_available_locales!(locale) - @@default_locale = locale.to_sym rescue nil + @@default_locale = locale && locale.to_sym end # Returns an array of locales for which translations are available. @@ -41,10 +43,25 @@ def available_locales @@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 + + # 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 '.' @@ -57,7 +74,8 @@ def default_separator=(separator) @@default_separator = separator end - # Return the current exception handler. Defaults to :default_exception_handler. + # Returns the current exception handler. Defaults to an instance of + # I18n::ExceptionHandler. def exception_handler @@exception_handler ||= ExceptionHandler.new end @@ -106,12 +124,15 @@ def load_path # behave like a Ruby Array. def load_path=(load_path) @@load_path = load_path + @@available_locales_set = nil + backend.reload! end - # [Deprecated] this will default to true in the future - # Defaults to nil so that it triggers the deprecation warning + # 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 - defined?(@@enforce_available_locales) ? @@enforce_available_locales : nil + @@enforce_available_locales end def enforce_available_locales=(enforce_available_locales) diff --git a/lib/i18n/core_ext/hash.rb b/lib/i18n/core_ext/hash.rb index f2a2422b..0c254ecc 100644 --- a/lib/i18n/core_ext/hash.rb +++ b/lib/i18n/core_ext/hash.rb @@ -1,7 +1,7 @@ class Hash def slice(*keep_keys) - h = {} - keep_keys.each { |key| h[key] = fetch(key) } + h = self.class.new + keep_keys.each { |key| h[key] = fetch(key) if has_key?(key) } h end unless Hash.method_defined?(:slice) @@ -21,7 +21,7 @@ def deep_symbolize_keys MERGER = proc do |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2 end - + def deep_merge!(data) merge!(data, &MERGER) end unless Hash.method_defined?(:deep_merge!) diff --git a/lib/i18n/core_ext/kernel/suppress_warnings.rb b/lib/i18n/core_ext/kernel/suppress_warnings.rb new file mode 100644 index 00000000..eec1435a --- /dev/null +++ b/lib/i18n/core_ext/kernel/suppress_warnings.rb @@ -0,0 +1,8 @@ +module Kernel + def suppress_warnings + original_verbosity, $VERBOSE = $VERBOSE, nil + yield + ensure + $VERBOSE = original_verbosity + end +end diff --git a/lib/i18n/core_ext/kernel/surpress_warnings.rb b/lib/i18n/core_ext/kernel/surpress_warnings.rb deleted file mode 100644 index cc03b1c1..00000000 --- a/lib/i18n/core_ext/kernel/surpress_warnings.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Kernel - def suppress_warnings - original_verbosity = $VERBOSE - $VERBOSE = nil - result = yield - $VERBOSE = original_verbosity - result - end -end diff --git a/lib/i18n/core_ext/string/interpolate.rb b/lib/i18n/core_ext/string/interpolate.rb index 56de8c00..a353310b 100644 --- a/lib/i18n/core_ext/string/interpolate.rb +++ b/lib/i18n/core_ext/string/interpolate.rb @@ -1,105 +1,9 @@ -# This backports the Ruby 1.9 String interpolation syntax to Ruby 1.8. +# This file used to backport the Ruby 1.9 String interpolation syntax to Ruby 1.8. # -# This backport has been shipped with I18n for a number of versions. Meanwhile -# Rails has started to rely on it and we are going to move it to ActiveSupport. -# See https://rails.lighthouseapp.com/projects/8994/tickets/6013-move-19-string-interpolation-syntax-backport-from-i18n-to-activesupport +# Since I18n has dropped support to Ruby 1.8, this file is not required anymore, +# however, Rails 3.2 still requires it directly: # -# Once the above patch has been applied to Rails the following code will be -# removed from I18n. - -=begin - heavily based on Masao Mutoh's gettext String interpolation extension - http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb - Copyright (C) 2005-2009 Masao Mutoh - You may redistribute it and/or modify it under the same license terms as Ruby. -=end - -begin - raise ArgumentError if ("a %{x}" % {:x=>'b'}) != 'a b' -rescue ArgumentError - # KeyError is raised by String#% when the string contains a named placeholder - # that is not contained in the given arguments hash. Ruby 1.9 includes and - # raises this exception natively. We define it to mimic Ruby 1.9's behaviour - # in Ruby 1.8.x - class KeyError < IndexError - def initialize(message = nil) - super(message || "key not found") - end - end unless defined?(KeyError) - - # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError. - # - # String#% method which accept "named argument". The translator can know - # the meaning of the msgids using "named argument" instead of %s/%d style. - class String - # For older ruby versions, such as ruby-1.8.5 - alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'} - alias :interpolate_without_ruby_19_syntax :% # :nodoc: - - INTERPOLATION_PATTERN = Regexp.union( - /%\{(\w+)\}/, # matches placeholders like "%{foo}" - /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%.d" - ) - - INTERPOLATION_PATTERN_WITH_ESCAPE = Regexp.union( - /%%/, - INTERPOLATION_PATTERN - ) - - # % uses self (i.e. the String) as a format specification and returns the - # result of applying it to the given arguments. In other words it interpolates - # the given arguments to the string according to the formats the string - # defines. - # - # There are three ways to use it: - # - # * Using a single argument or Array of arguments. - # - # This is the default behaviour of the String class. See Kernel#sprintf for - # more details about the format string. - # - # Example: - # - # "%d %s" % [1, "message"] - # # => "1 message" - # - # * Using a Hash as an argument and unformatted, named placeholders. - # - # When you pass a Hash as an argument and specify placeholders with %{foo} - # it will interpret the hash values as named arguments. - # - # Example: - # - # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"} - # # => "Masao Mutoh" - # - # * Using a Hash as an argument and formatted, named placeholders. - # - # When you pass a Hash as an argument and specify placeholders with %d - # it will interpret the hash values as named arguments and format the value - # according to the formatting instruction appended to the closing >. - # - # Example: - # - # "%d, %.1f" % { :integer => 10, :float => 43.4 } - # # => "10, 43.3" - def %(args) - if args.kind_of?(Hash) - dup.gsub(INTERPOLATION_PATTERN_WITH_ESCAPE) do |match| - if match == '%%' - '%' - else - key = ($1 || $2).to_sym - raise KeyError unless args.has_key?(key) - $3 ? sprintf("%#{$3}", args[key]) : args[key] - end - end - elsif self =~ INTERPOLATION_PATTERN - raise ArgumentError.new('one hash required') - else - result = gsub(/%([{<])/, '%%\1') - result.send :'interpolate_without_ruby_19_syntax', args - end - end - end -end +# https://github.com/rails/rails/blob/3-2-stable/activesupport/lib/active_support/core_ext/string/interpolation.rb#L2 +# +# So we can't just drop the file entirely, which would then break Rails users +# under Ruby 1.9. This file can be removed once Rails 3.2 support is dropped. diff --git a/lib/i18n/exceptions.rb b/lib/i18n/exceptions.rb index 48d50b85..c0d7477c 100644 --- a/lib/i18n/exceptions.rb +++ b/lib/i18n/exceptions.rb @@ -8,21 +8,10 @@ module I18n class ExceptionHandler include Module.new { def call(exception, locale, key, options) - if exception.is_a?(MissingTranslation) - # - # TODO: this block is to be replaced by `exception.message` when - # rescue_format is removed - if options[:rescue_format] == :html - if @rescue_format_deprecation - $stderr.puts "[DEPRECATED] I18n's :recue_format option will be removed from a future release. All exception messages will be plain text. If you need the exception handler to return an html format please set or pass a custom exception handler." - @rescue_format_deprecation = true - end - exception.html_message - else - exception.message - end - - elsif exception.is_a?(Exception) + case exception + when MissingTranslation + exception.message + when Exception raise exception else throw :exception, exception @@ -49,21 +38,15 @@ def initialize(filename, exception_message) end end - class MissingTranslation + class MissingTranslation < ArgumentError module Base attr_reader :locale, :key, :options - def initialize(locale, key, options = nil) - @key, @locale, @options = key, locale, options.dup || {} + def initialize(locale, key, options = {}) + @key, @locale, @options = key, locale, options.dup options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) } end - def html_message - key = CGI.escapeHTML titleize(keys.last) - path = CGI.escapeHTML keys.join('.') - %(#{key}) - end - def keys @keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys| keys << 'no key' if keys.size < 2 @@ -78,13 +61,6 @@ def message def to_exception MissingTranslationData.new(locale, key, options) end - - protected - - # TODO : remove when #html_message is removed - def titleize(key) - key.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize } - end end include Base diff --git a/lib/i18n/gettext/helpers.rb b/lib/i18n/gettext/helpers.rb index ea07d052..c97fd349 100644 --- a/lib/i18n/gettext/helpers.rb +++ b/lib/i18n/gettext/helpers.rb @@ -7,6 +7,15 @@ module Gettext # # 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 = {}) I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options)) end diff --git a/lib/i18n/interpolate/ruby.rb b/lib/i18n/interpolate/ruby.rb index 442677f2..d2fdda75 100644 --- a/lib/i18n/interpolate/ruby.rb +++ b/lib/i18n/interpolate/ruby.rb @@ -22,7 +22,7 @@ def interpolate_hash(string, values) if match == '%%' '%' else - key = ($1 || $2).to_sym + key = ($1 || $2 || match.tr("%{}", "")).to_sym value = if values.key?(key) values[key] else diff --git a/lib/i18n/tests/defaults.rb b/lib/i18n/tests/defaults.rb index c89200b9..2ba590e9 100644 --- a/lib/i18n/tests/defaults.rb +++ b/lib/i18n/tests/defaults.rb @@ -24,6 +24,14 @@ def setup assert_equal 'bar', I18n.t(:does_not_exist, :default => [:does_not_exist_2, :'foo.bar']) 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_equal nil, I18n.t(:does_not_exist, :default => nil) + end + test "defaults: given an array of missing keys it raises a MissingTranslationData exception" do assert_raise I18n::MissingTranslationData do I18n.t(:does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3], :raise => true) diff --git a/lib/i18n/tests/interpolation.rb b/lib/i18n/tests/interpolation.rb index add8b90e..c457b3d7 100644 --- a/lib/i18n/tests/interpolation.rb +++ b/lib/i18n/tests/interpolation.rb @@ -104,6 +104,26 @@ module Interpolation assert_raise(I18n::ReservedInterpolationKey) { interpolate(:default => '%{separator}', :foo => :bar) } 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) diff --git a/lib/i18n/tests/localization/date.rb b/lib/i18n/tests/localization/date.rb index a7234752..fba5fe5c 100644 --- a/lib/i18n/tests/localization/date.rb +++ b/lib/i18n/tests/localization/date.rb @@ -51,6 +51,10 @@ def setup 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_raise(I18n::ArgumentError) { I18n.l(nil) } end diff --git a/lib/i18n/tests/localization/procs.rb b/lib/i18n/tests/localization/procs.rb index 7b7813e2..bdfa9480 100644 --- a/lib/i18n/tests/localization/procs.rb +++ b/lib/i18n/tests/localization/procs.rb @@ -52,19 +52,19 @@ module Procs 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 inspect_args([time, {}]), I18n.l(time, :format => :proc, :locale => :ru) + 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 inspect_args([time, options]), I18n.l(time, options.merge(:format => :proc, :locale => :ru)) + assert_equal I18n::Tests::Localization::Procs.inspect_args([time, options]), I18n.l(time, options.merge(:format => :proc, :locale => :ru)) end protected - def inspect_args(args) + def self.inspect_args(args) args = args.map do |arg| case arg when ::Time, ::DateTime @@ -85,12 +85,12 @@ def setup_time_proc_translations I18n.backend.store_translations :ru, { :time => { :formats => { - :proc => lambda { |*args| inspect_args(args) } + :proc => lambda { |*args| I18n::Tests::Localization::Procs.inspect_args(args) } } }, :date => { :formats => { - :proc => lambda { |*args| inspect_args(args) } + :proc => lambda { |*args| I18n::Tests::Localization::Procs.inspect_args(args) } }, :'day_names' => lambda { |key, options| (options[:format] =~ /^%A/) ? diff --git a/lib/i18n/tests/lookup.rb b/lib/i18n/tests/lookup.rb index 62c069d1..3b4c8434 100644 --- a/lib/i18n/tests/lookup.rb +++ b/lib/i18n/tests/lookup.rb @@ -17,7 +17,7 @@ def setup assert_equal({ :a => "b" }, I18n.t(:hash)) end - test "lookup: it returns a array" do + test "lookup: it returns an array" do assert_equal(%w(a b c), I18n.t(:array)) end diff --git a/lib/i18n/tests/procs.rb b/lib/i18n/tests/procs.rb index 345d8995..9ad2793f 100644 --- a/lib/i18n/tests/procs.rb +++ b/lib/i18n/tests/procs.rb @@ -4,29 +4,30 @@ 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| filter_args(*args) }) + 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 "defaults: given a default is a Proc it calls it with the key and interpolation values" do - proc = lambda { |*args| filter_args(*args) } + proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) } assert_equal '[:does_not_exist, {:foo=>"foo"}]', I18n.t(:does_not_exist, :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 - I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| filter_args(*args) }) + 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(:does_not_exist, :default => :a_lambda, :foo => 'foo') assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(:does_not_exist, :default => [nil, :a_lambda], :foo => 'foo') end - test "interpolation: given an interpolation value is a Proc it calls it with key and values before interpolating it" do - proc = lambda { |*args| filter_args(*args) } + 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(:does_not_exist, :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}: " + filter_args(*args) } - assert_equal "foo: [:does_not_exist, {:foo=>\"foo\"}]", I18n.t(:does_not_exist, :default => proc, :foo => 'foo') + proc = lambda { |*args| "%{foo}: " + I18n::Tests::Procs.filter_args(*args) } + assert_equal 'foo: [:does_not_exist, {:foo=>"foo"}]', I18n.t(:does_not_exist, :default => proc, :foo => 'foo') end test "pluralization: given a key resolves to a Proc that returns valid data then pluralization still works" do @@ -37,17 +38,16 @@ module Procs 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| filter_args(*args) }) + 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(:does_not_exist, :default => lambda { |*args| filter_args(*args) }, :resolve => false).class + assert_equal Proc, I18n.t(:does_not_exist, :default => lambda { |*args| I18n::Tests::Procs.filter_args(*args) }, :resolve => false).class end - protected - def filter_args(*args) + def self.filter_args(*args) args.map {|arg| arg.delete(:fallback) if arg.is_a?(Hash) ; arg }.inspect end end diff --git a/lib/i18n/version.rb b/lib/i18n/version.rb index 01f274a3..31178899 100644 --- a/lib/i18n/version.rb +++ b/lib/i18n/version.rb @@ -1,3 +1,3 @@ module I18n - VERSION = "0.6.9" + VERSION = "0.8.0.beta1" end diff --git a/test/all.rb b/test/all.rb deleted file mode 100644 index e846143a..00000000 --- a/test/all.rb +++ /dev/null @@ -1,8 +0,0 @@ -# encoding: utf-8 - -dir = File.dirname(__FILE__) -$LOAD_PATH.unshift(dir) - -Dir["#{dir}/**/*_test.rb"].sort.each do |file| - require file.sub(/^#{dir}\/(.*)\.rb$/, '\1') -end diff --git a/test/api/all_features_test.rb b/test/api/all_features_test.rb index 74589b7f..a1799bd6 100644 --- a/test/api/all_features_test.rb +++ b/test/api/all_features_test.rb @@ -7,7 +7,7 @@ puts "not testing with Cache enabled because active_support can not be found" end -class I18nAllFeaturesApiTest < Test::Unit::TestCase +class I18nAllFeaturesApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Metadata include I18n::Backend::Cache diff --git a/test/api/cascade_test.rb b/test/api/cascade_test.rb index a40587b3..4d9516c4 100644 --- a/test/api/cascade_test.rb +++ b/test/api/cascade_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class I18nCascadeApiTest < Test::Unit::TestCase +class I18nCascadeApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Cascade end diff --git a/test/api/chain_test.rb b/test/api/chain_test.rb index 0a61cb0a..f3dff1b5 100644 --- a/test/api/chain_test.rb +++ b/test/api/chain_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class I18nApiChainTest < Test::Unit::TestCase +class I18nApiChainTest < I18n::TestCase def setup super I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, I18n.backend) diff --git a/test/api/fallbacks_test.rb b/test/api/fallbacks_test.rb index 31ebb003..e5ca5f20 100644 --- a/test/api/fallbacks_test.rb +++ b/test/api/fallbacks_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class I18nFallbacksApiTest < Test::Unit::TestCase +class I18nFallbacksApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Fallbacks end diff --git a/test/api/key_value_test.rb b/test/api/key_value_test.rb index 976ca6ba..4d6cdd8a 100644 --- a/test/api/key_value_test.rb +++ b/test/api/key_value_test.rb @@ -1,8 +1,6 @@ require 'test_helper' -I18n::Tests.setup_rufus_tokyo - -class I18nKeyValueApiTest < Test::Unit::TestCase +class I18nKeyValueApiTest < I18n::TestCase include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation @@ -15,14 +13,12 @@ class I18nKeyValueApiTest < Test::Unit::TestCase include I18n::Tests::Localization::Time # include Tests::Api::Localization::Procs - STORE = Rufus::Tokyo::Cabinet.new('*') - def setup - I18n.backend = I18n::Backend::KeyValue.new(STORE) + 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 defined?(Rufus::Tokyo::Cabinet) +end if I18n::TestCase.key_value? diff --git a/test/api/memoize_test.rb b/test/api/memoize_test.rb index 8839ec8c..ed938665 100644 --- a/test/api/memoize_test.rb +++ b/test/api/memoize_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class I18nMemoizeBackendWithSimpleApiTest < Test::Unit::TestCase +class I18nMemoizeBackendWithSimpleApiTest < I18n::TestCase include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation @@ -27,9 +27,7 @@ def setup end end -I18n::Tests.setup_rufus_tokyo - -class I18nMemoizeBackendWithKeyValueApiTest < Test::Unit::TestCase +class I18nMemoizeBackendWithKeyValueApiTest < I18n::TestCase include I18n::Tests::Basics include I18n::Tests::Defaults include I18n::Tests::Interpolation @@ -47,14 +45,12 @@ class MemoizeBackend < I18n::Backend::KeyValue include I18n::Backend::Memoize end - STORE = Rufus::Tokyo::Cabinet.new('*') - def setup - I18n.backend = MemoizeBackend.new(STORE) + I18n.backend = MemoizeBackend.new({}) super end test "make sure we use the MemoizeBackend backend" do assert_equal MemoizeBackend, I18n.backend.class end -end if defined?(Rufus::Tokyo::Cabinet) +end if I18n::TestCase.key_value? diff --git a/test/api/override_test.rb b/test/api/override_test.rb index 1f4e564e..6e1248ad 100644 --- a/test/api/override_test.rb +++ b/test/api/override_test.rb @@ -1,28 +1,24 @@ require 'test_helper' -class I18nOverrideTest < Test::Unit::TestCase +class I18nOverrideTest < I18n::TestCase module OverrideInverse - def translate(*args) super(*args).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 - super end test "make sure modules can overwrite I18n methods" do @@ -30,8 +26,7 @@ def setup @I18n.backend.store_translations('en', :foo => 'bar') assert_equal 'rab', @I18n.translate(:foo, :locale => 'en') - # FIXME: this fails under 1.8.7 - # assert_equal 'rab', @I18n.t(: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 @@ -44,5 +39,4 @@ def setup @I18n.extend OverrideSignature assert_equal 'HelloWelcome message on home page', @I18n.translate('Hello', 'Welcome message on home page', :tokenize => true) # tr8n example end - end diff --git a/test/api/pluralization_test.rb b/test/api/pluralization_test.rb index 5da3a5ef..53004beb 100644 --- a/test/api/pluralization_test.rb +++ b/test/api/pluralization_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class I18nPluralizationApiTest < Test::Unit::TestCase +class I18nPluralizationApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization end diff --git a/test/api/simple_test.rb b/test/api/simple_test.rb index 02d62058..3fd3a424 100644 --- a/test/api/simple_test.rb +++ b/test/api/simple_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class I18nSimpleBackendApiTest < Test::Unit::TestCase +class I18nSimpleBackendApiTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Pluralization end diff --git a/test/backend/cache_test.rb b/test/backend/cache_test.rb index 39df10d4..bda05860 100644 --- a/test/backend/cache_test.rb +++ b/test/backend/cache_test.rb @@ -6,7 +6,7 @@ $stderr.puts "Skipping cache tests using ActiveSupport" else -class I18nBackendCacheTest < Test::Unit::TestCase +class I18nBackendCacheTest < I18n::TestCase class Backend < I18n::Backend::Simple include I18n::Backend::Cache end @@ -15,9 +15,11 @@ def setup I18n.backend = Backend.new super I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) + I18n.cache_key_digest = nil end def teardown + super I18n.cache_store = nil end @@ -36,6 +38,12 @@ def teardown 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_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } @@ -59,10 +67,19 @@ def teardown test "adds locale and hash of key and hash of options" do options = { :bar=>1 } - options_hash = I18n::Backend::Cache::USE_INSPECT_HASH ? options.inspect.hash : options.hash + options_hash = RUBY_VERSION <= "1.9" ? options.inspect.hash : options.hash assert_equal "i18n//en/#{:foo.hash}/#{options_hash}", I18n.backend.send(:cache_key, :en, :foo, options) end + test "cache_key uses configured digest method" do + md5 = Digest::MD5.new + options = { :bar=>1 } + options_hash = options.inspect + with_cache_key_digest(md5) do + assert_equal "i18n//en/#{md5.hexdigest(:foo.to_s)}/#{md5.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 } @@ -80,6 +97,12 @@ def with_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 diff --git a/test/backend/cascade_test.rb b/test/backend/cascade_test.rb index 71fae50c..d1d0c4aa 100644 --- a/test/backend/cascade_test.rb +++ b/test/backend/cascade_test.rb @@ -1,11 +1,12 @@ require 'test_helper' -class I18nBackendCascadeTest < Test::Unit::TestCase +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 } diff --git a/test/backend/chain_test.rb b/test/backend/chain_test.rb index 67147be5..61f8ca36 100644 --- a/test/backend/chain_test.rb +++ b/test/backend/chain_test.rb @@ -1,12 +1,23 @@ require 'test_helper' -class I18nBackendChainTest < Test::Unit::TestCase +class I18nBackendChainTest < I18n::TestCase def setup + super @first = backend(:en => { - :foo => 'Foo', :formats => { :short => 'short' }, :plural_1 => { :one => '%{count}' }, :dates => {:a => "A"} + :foo => 'Foo', :formats => { + :short => 'short', + :subformats => {:short => 'short'}, + }, + :plural_1 => { :one => '%{count}' }, + :dates => {:a => "A"} }) @second = backend(:en => { - :bar => 'Bar', :formats => { :long => 'long' }, :plural_2 => { :one => 'one' }, :dates => {:a => "B", :b => "B"} + :bar => 'Bar', :formats => { + :long => 'long', + :subformats => {:long => 'long'}, + }, + :plural_2 => { :one => 'one' }, + :dates => {:a => "B", :b => "B"} }) @chain = I18n.backend = I18n::Backend::Chain.new(@first, @second) end @@ -38,11 +49,11 @@ def setup assert_equal({}, I18n.t(:'i18n.transliterate.rule', :locale => 'en', :default => {})) end - test "namespace lookup collects results from all backends" do - assert_equal({ :short => 'short', :long => 'long' }, I18n.t(:formats)) + 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 does not overwrite" do + 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 @@ -58,7 +69,11 @@ def setup 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 [{ :short => 'short', :long => 'long' }, { :one => 'one' }, 'Bah'], I18n.t([:formats, :plural_2, :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 transfering to backend" do diff --git a/test/backend/exceptions_test.rb b/test/backend/exceptions_test.rb index fc0f557c..cc221669 100644 --- a/test/backend/exceptions_test.rb +++ b/test/backend/exceptions_test.rb @@ -1,7 +1,8 @@ require 'test_helper' -class I18nBackendExceptionsTest < Test::Unit::TestCase +class I18nBackendExceptionsTest < I18n::TestCase def setup + super I18n.backend = I18n::Backend::Simple.new end diff --git a/test/backend/fallbacks_test.rb b/test/backend/fallbacks_test.rb index 2b17da94..a1d5a74a 100644 --- a/test/backend/fallbacks_test.rb +++ b/test/backend/fallbacks_test.rb @@ -1,15 +1,17 @@ require 'test_helper' -class I18nBackendFallbacksTranslateTest < Test::Unit::TestCase +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') 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 @@ -74,14 +76,24 @@ def setup 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 end -class I18nBackendFallbacksLocalizeTest < Test::Unit::TestCase +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' } }) @@ -108,18 +120,79 @@ def setup end end -class I18nBackendFallbacksWithChainTest < Test::Unit::TestCase +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') - I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, backend) + 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 "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?(:foo, :de) + assert_equal true, I18n.exists?(:foo, :'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 +end \ No newline at end of file diff --git a/test/backend/interpolation_compiler_test.rb b/test/backend/interpolation_compiler_test.rb index f4d287a9..cffe28f9 100644 --- a/test/backend/interpolation_compiler_test.rb +++ b/test/backend/interpolation_compiler_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class InterpolationCompilerTest < Test::Unit::TestCase +class InterpolationCompilerTest < I18n::TestCase Compiler = I18n::Backend::InterpolationCompiler::Compiler def compile_and_interpolate(str, values = {}) @@ -95,18 +95,18 @@ def test_custom_missing_interpolation_argument_handler end end -class I18nBackendInterpolationCompilerTest < Test::Unit::TestCase +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 : {} @@ -115,5 +115,4 @@ def interpolate(*args) end super end - end diff --git a/test/backend/key_value_test.rb b/test/backend/key_value_test.rb index 74c6b7d1..097780df 100644 --- a/test/backend/key_value_test.rb +++ b/test/backend/key_value_test.rb @@ -1,10 +1,8 @@ require 'test_helper' -I18n::Tests.setup_rufus_tokyo - -class I18nBackendKeyValueTest < Test::Unit::TestCase +class I18nBackendKeyValueTest < I18n::TestCase def setup_backend!(subtree=true) - I18n.backend = I18n::Backend::KeyValue.new(Rufus::Tokyo::Cabinet.new('*'), subtree) + I18n.backend = I18n::Backend::KeyValue.new({}, subtree) store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' }) end @@ -42,5 +40,4 @@ def assert_flattens(expected, nested, escape=true, subtree=true) I18n.t("foo", :raise => true) end end - -end if defined?(Rufus::Tokyo::Cabinet) +end if I18n::TestCase.key_value? diff --git a/test/backend/memoize_test.rb b/test/backend/memoize_test.rb index a8a2e11e..0fb9f904 100644 --- a/test/backend/memoize_test.rb +++ b/test/backend/memoize_test.rb @@ -1,6 +1,6 @@ require 'test_helper' - -require 'backend/simple_test' +# TODO: change back to "require 'backend/simple'" when dropping support to Ruby 1.8.7. +require File.expand_path('../simple_test', __FILE__) class I18nBackendMemoizeTest < I18nBackendSimpleTest module MemoizeSpy diff --git a/test/backend/metadata_test.rb b/test/backend/metadata_test.rb index 584c7f65..44612cf0 100644 --- a/test/backend/metadata_test.rb +++ b/test/backend/metadata_test.rb @@ -1,11 +1,12 @@ require 'test_helper' -class I18nBackendMetadataTest < Test::Unit::TestCase +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 diff --git a/test/backend/pluralization_test.rb b/test/backend/pluralization_test.rb index 9d883db7..12c5cbad 100644 --- a/test/backend/pluralization_test.rb +++ b/test/backend/pluralization_test.rb @@ -1,12 +1,13 @@ require 'test_helper' -class I18nBackendPluralizationTest < Test::Unit::TestCase +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 == 1 ? :one : n == 0 || (2..10).include?(n % 100) ? :few : (11..19).include?(n % 100) ? :many : :other } store_translations(:xx, :i18n => { :plural => { :rule => @rule } }) diff --git a/test/backend/simple_test.rb b/test/backend/simple_test.rb index 515a1b98..f6f7886f 100644 --- a/test/backend/simple_test.rb +++ b/test/backend/simple_test.rb @@ -1,7 +1,8 @@ require 'test_helper' -class I18nBackendSimpleTest < Test::Unit::TestCase +class I18nBackendSimpleTest < I18n::TestCase def setup + super I18n.backend = I18n::Backend::Simple.new I18n.load_path = [locales_dir + '/en.yml'] end @@ -49,23 +50,23 @@ def setup # storing translations test "simple store_translations: stores translations, ... no, really :-)" do - I18n.backend.store_translations :'en', :foo => 'bar' + store_translations :'en', :foo => 'bar' assert_equal Hash[:'en', {:foo => 'bar'}], translations end test "simple store_translations: deep_merges with existing translations" do - I18n.backend.store_translations :'en', :foo => {:bar => 'bar'} - I18n.backend.store_translations :'en', :foo => {:baz => 'baz'} + 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 - I18n.backend.store_translations 'en', :foo => 'bar' + store_translations 'en', :foo => 'bar' assert_equal Hash[:'en', {:foo => 'bar'}], translations end test "simple store_translations: converts keys to Symbols" do - I18n.backend.store_translations 'en', 'foo' => {'bar' => 'bar', 'baz' => 'baz'} + store_translations 'en', 'foo' => {'bar' => 'bar', 'baz' => 'baz'} assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], translations end @@ -78,6 +79,6 @@ def setup test "simple reload_translations: uninitializes the backend" do I18n.backend.reload! - assert_equal I18n.backend.initialized?, false + assert_equal false, I18n.backend.initialized? end end diff --git a/test/backend/transliterator_test.rb b/test/backend/transliterator_test.rb index a7f914e0..c40e2bbf 100644 --- a/test/backend/transliterator_test.rb +++ b/test/backend/transliterator_test.rb @@ -1,8 +1,9 @@ # encoding: utf-8 require 'test_helper' -class I18nBackendTransliterator < Test::Unit::TestCase +class I18nBackendTransliterator < I18n::TestCase def setup + super I18n.backend = I18n::Backend::Simple.new @proc = lambda { |n| n.upcase } @hash = { "ü" => "ue", "ö" => "oe", "a" => "a" } @@ -55,11 +56,9 @@ def setup assert_equal "abc#", @transliterator.transliterate("abcſ", "#") end - if RUBY_VERSION >= "1.9" - test "default transliterator raises errors for invalid UTF-8" do - assert_raise ArgumentError do - @transliterator.transliterate("a\x92b") - end + test "default transliterator raises errors for invalid UTF-8" do + assert_raise ArgumentError do + @transliterator.transliterate("a\x92b") end end diff --git a/test/core_ext/hash_test.rb b/test/core_ext/hash_test.rb index 25c57488..7325c50a 100644 --- a/test/core_ext/hash_test.rb +++ b/test/core_ext/hash_test.rb @@ -1,7 +1,7 @@ require 'test_helper' require 'i18n/core_ext/hash' -class I18nCoreExtHashInterpolationTest < Test::Unit::TestCase +class I18nCoreExtHashInterpolationTest < I18n::TestCase test "#deep_symbolize_keys" do hash = { 'foo' => { 'bar' => { 'baz' => 'bar' } } } expected = { :foo => { :bar => { :baz => 'bar' } } } @@ -14,6 +14,18 @@ class I18nCoreExtHashInterpolationTest < Test::Unit::TestCase assert_equal expected, hash.slice(:foo) end + test "#slice non-existent key" do + hash = { :foo => 'bar', :baz => 'bar' } + expected = { :foo => 'bar' } + assert_equal expected, hash.slice(:foo, :not_here) + end + + test "#slice maintains subclasses of Hash" do + klass = Class.new(Hash) + hash = klass[:foo, 'bar', :baz, 'bar'] + assert_instance_of klass, hash.slice(:foo) + end + test "#except" do hash = { :foo => 'bar', :baz => 'bar' } expected = { :foo => 'bar' } diff --git a/test/core_ext/string/interpolate_test.rb b/test/core_ext/string/interpolate_test.rb deleted file mode 100644 index 993d454d..00000000 --- a/test/core_ext/string/interpolate_test.rb +++ /dev/null @@ -1,99 +0,0 @@ -require 'test_helper' - -# thanks to Masao's String extensions these should work the same in -# Ruby 1.8 (patched) and Ruby 1.9 (native) -# some tests taken from Masao's tests -# http://github.com/mutoh/gettext/blob/edbbe1fa8238fa12c7f26f2418403015f0270e47/test/test_string.rb - -class I18nCoreExtStringInterpolationTest < Test::Unit::TestCase - test "String interpolates a single argument" do - assert_equal "Masao", "%s" % "Masao" - end - - test "String interpolates an array argument" do - assert_equal "1 message", "%d %s" % [1, 'message'] - end - - test "String interpolates a hash argument w/ named placeholders" do - assert_equal "Masao Mutoh", "%{first} %{last}" % { :first => 'Masao', :last => 'Mutoh' } - end - - test "String interpolates a hash argument w/ named placeholders (reverse order)" do - assert_equal "Mutoh, Masao", "%{last}, %{first}" % { :first => 'Masao', :last => 'Mutoh' } - end - - test "String interpolates named placeholders with sprintf syntax" do - assert_equal "10, 43.4", "%d, %.1f" % {:integer => 10, :float => 43.4} - end - - test "String interpolates named placeholders with sprintf syntax, does not recurse" do - assert_equal "%s", "%{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", "aaa" % {:num => 1}) - assert_equal("bbb", "bbb" % [1]) - end - - test "String interpolation sprintf behaviour equals Ruby 1.9 behaviour" do - assert_equal("1", "%d" % {:num => 1}) - assert_equal("0b1", "%#b" % {:num => 1}) - assert_equal("foo", "%s" % {:msg => "foo"}) - assert_equal("1.000000", "%f" % {:num => 1.0}) - assert_equal(" 1", "%3.0f" % {:num => 1.0}) - assert_equal("100.00", "%2.2f" % {:num => 100.0}) - assert_equal("0x64", "%#x" % {:num => 100.0}) - assert_raise(ArgumentError) { "%,d" % {:num => 100} } - assert_raise(ArgumentError) { "%/d" % {:num => 100} } - end - - test "String interpolation old-style sprintf still works" do - assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0]) - end - - test "String interpolation raises an ArgumentError when the string has extra placeholders (Array)" do - assert_raise(ArgumentError) do # Ruby 1.9 msg: "too few arguments" - "%s %s" % %w(Masao) - end - end - - test "String interpolation raises a KeyError when the string has extra placeholders (Hash)" do - assert_raise(KeyError) do # Ruby 1.9 msg: "key not found" - "%{first} %{last}" % { :first => 'Masao' } - end - end - - test "String interpolation does not raise when passed extra values (Array)" do - assert_nothing_raised do - assert_equal "Masao", "%s" % %w(Masao Mutoh) - end - end - - test "String interpolation does not raise when passed extra values (Hash)" do - assert_nothing_raised do - assert_equal "Masao Mutoh", "%{first} %{last}" % { :first => 'Masao', :last => 'Mutoh', :salutation => 'Mr.' } - end - end - - test "% acts as escape character in String interpolation" do - assert_equal "%{first}", "%%{first}" % { :first => 'Masao' } - assert_equal("% 1", "%% %d" % {:num => 1.0}) - assert_equal("%{num} %d", "%%{num} %%d" % {:num => 1}) - end - - test "% can be used in Ruby's own sprintf behavior" do - assert_equal "70%", "%d%%" % 70 - assert_equal "70-100%", "%d-%d%%" % [70, 100] - assert_equal "+2.30%", "%+.2f%%" % 2.3 - end - - def test_sprintf_mix_unformatted_and_formatted_named_placeholders - assert_equal("foo 1.000000", "%{name} %f" % {:name => "foo", :num => 1.0}) - end - - def test_string_interpolation_raises_an_argument_error_when_mixing_named_and_unnamed_placeholders - assert_raise(ArgumentError) { "%{name} %f" % [1.0] } - assert_raise(ArgumentError) { "%{name} %f" % [1.0, 2.0] } - end -end - diff --git a/test/gettext/api_test.rb b/test/gettext/api_test.rb index 947b5a23..2cd157c0 100644 --- a/test/gettext/api_test.rb +++ b/test/gettext/api_test.rb @@ -4,8 +4,9 @@ include I18n::Gettext::Helpers -class I18nGettextApiTest < Test::Unit::TestCase +class I18nGettextApiTest < I18n::TestCase def setup + super I18n.locale = :en I18n.backend.store_translations :de, { 'Hi Gettext!' => 'Hallo Gettext!', @@ -17,6 +18,13 @@ def setup }, :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!') diff --git a/test/gettext/backend_test.rb b/test/gettext/backend_test.rb index 39c64b35..30254e6b 100644 --- a/test/gettext/backend_test.rb +++ b/test/gettext/backend_test.rb @@ -2,100 +2,91 @@ require 'test_helper' -# apparently Ruby 1.9.1p129 has encoding problems with the gettext po parser -unless RUBY_VERSION == '1.9.1' && RUBY_PATCHLEVEL <= 129 +class I18nGettextBackendTest < I18n::TestCase + include I18n::Gettext::Helpers - class I18nGettextBackendTest < Test::Unit::TestCase - include I18n::Gettext::Helpers - - class Backend < I18n::Backend::Simple - include I18n::Backend::Gettext - end - - def setup - I18n.backend = Backend.new - I18n.locale = :en - I18n.load_path = ["#{locales_dir}/de.po"] - @old_separator, I18n.default_separator = I18n.default_separator, '|' - end + class Backend < I18n::Backend::Simple + include I18n::Backend::Gettext + end - def teardown - I18n.load_path = nil - I18n.backend = nil - I18n.default_separator = @old_separator - 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_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_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_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_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_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_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_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_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_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_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 + 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 diff --git a/test/i18n/exceptions_test.rb b/test/i18n/exceptions_test.rb index 098eefe6..3e528c64 100644 --- a/test/i18n/exceptions_test.rb +++ b/test/i18n/exceptions_test.rb @@ -1,9 +1,9 @@ require 'test_helper' -class I18nExceptionsTest < Test::Unit::TestCase +class I18nExceptionsTest < I18n::TestCase def test_invalid_locale_stores_locale force_invalid_locale - rescue I18n::ArgumentError => e + rescue I18n::ArgumentError => exception assert_nil exception.locale end @@ -13,6 +13,11 @@ def test_invalid_locale_stores_locale 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 @@ -27,21 +32,6 @@ def test_invalid_locale_stores_locale end end - test "MissingTranslationData html_message is a span with the titlelized last key token" do - exception = I18n::MissingTranslationData.new(:de, :foo, :scope => :bar) - assert_equal 'Foo', exception.html_message - end - - test "MissingTranslationData html_message html escapes key names" do - exception = I18n::MissingTranslationData.new(:de, '', :scope => '