.d"
11 | ].freeze
12 | INTERPOLATION_PATTERN = Regexp.union(DEFAULT_INTERPOLATION_PATTERNS)
13 | deprecate_constant :INTERPOLATION_PATTERN
14 |
15 | INTERPOLATION_PATTERNS_CACHE = Hash.new do |hash, patterns|
16 | hash[patterns] = Regexp.union(patterns)
17 | end
18 | private_constant :INTERPOLATION_PATTERNS_CACHE
19 |
20 | class << self
21 | # Return String or raises MissingInterpolationArgument exception.
22 | # Missing argument's logic is handled by I18n.config.missing_interpolation_argument_handler.
23 | def interpolate(string, values)
24 | raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ I18n.reserved_keys_pattern
25 | raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash)
26 | interpolate_hash(string, values)
27 | end
28 |
29 | def interpolate_hash(string, values)
30 | pattern = INTERPOLATION_PATTERNS_CACHE[config.interpolation_patterns]
31 | interpolated = false
32 |
33 | interpolated_string = string.gsub(pattern) do |match|
34 | interpolated = true
35 |
36 | if match == '%%'
37 | '%'
38 | else
39 | key = ($1 || $2 || match.tr("%{}", "")).to_sym
40 | value = if values.key?(key)
41 | values[key]
42 | else
43 | config.missing_interpolation_argument_handler.call(key, values, string)
44 | end
45 | value = value.call(values) if value.respond_to?(:call)
46 | $3 ? sprintf("%#{$3}", value) : value
47 | end
48 | end
49 |
50 | interpolated ? interpolated_string : string
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/i18n/locale.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module I18n
4 | module Locale
5 | autoload :Fallbacks, 'i18n/locale/fallbacks'
6 | autoload :Tag, 'i18n/locale/tag'
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/i18n/locale/fallbacks.rb:
--------------------------------------------------------------------------------
1 | # Locale Fallbacks
2 | #
3 | # Extends the I18n module to hold a fallbacks instance which is set to an
4 | # instance of I18n::Locale::Fallbacks by default but can be swapped with a
5 | # different implementation.
6 | #
7 | # Locale fallbacks will compute a number of fallback locales for a given locale.
8 | # For example:
9 | #
10 | #
11 | # I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en]
12 | #
13 | # Locale fallbacks always fall back to
14 | #
15 | # * all parent locales of a given locale (e.g. :es for :"es-MX") first,
16 | # * the current default locales and all of their parents second
17 | #
18 | # The default locales are set to [] by default but can be set to something else.
19 | #
20 | # One can additionally add any number of additional fallback locales manually.
21 | # These will be added before the default locales to the fallback chain. For
22 | # example:
23 | #
24 | # # using a custom locale as default fallback locale
25 | #
26 | # I18n.fallbacks = I18n::Locale::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de)
27 | # I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"en-GB", :en]
28 | # I18n.fallbacks[:"de-CH"] # => [:"de-CH", :de, :"en-GB", :en]
29 | #
30 | # # mapping fallbacks to an existing instance
31 | #
32 | # # people speaking Catalan also speak Spanish as spoken in Spain
33 | # fallbacks = I18n.fallbacks
34 | # fallbacks.map(:ca => :"es-ES")
35 | # fallbacks[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en]
36 | #
37 | # # people speaking Arabian as spoken in Palestine also speak Hebrew as spoken in Israel
38 | # fallbacks.map(:"ar-PS" => :"he-IL")
39 | # fallbacks[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en]
40 | # fallbacks[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en]
41 | #
42 | # # people speaking Sami as spoken in Finland also speak Swedish and Finnish as spoken in Finland
43 | # fallbacks.map(:sms => [:"se-FI", :"fi-FI"])
44 | # fallbacks[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en]
45 |
46 | module I18n
47 | module Locale
48 | class Fallbacks < Hash
49 | def initialize(*mappings)
50 | @map = {}
51 | map(mappings.pop) if mappings.last.is_a?(Hash)
52 | self.defaults = mappings.empty? ? [] : mappings
53 | end
54 |
55 | def defaults=(defaults)
56 | @defaults = defaults.flat_map { |default| compute(default, false) }
57 | end
58 | attr_reader :defaults
59 |
60 | def [](locale)
61 | raise InvalidLocale.new(locale) if locale.nil?
62 | raise Disabled.new('fallback#[]') if locale == false
63 | locale = locale.to_sym
64 | super || store(locale, compute(locale))
65 | end
66 |
67 | def map(*args, &block)
68 | if args.count == 1 && !block_given?
69 | mappings = args.first
70 | mappings.each do |from, to|
71 | from, to = from.to_sym, Array(to)
72 | to.each do |_to|
73 | @map[from] ||= []
74 | @map[from] << _to.to_sym
75 | end
76 | end
77 | else
78 | @map.map(*args, &block)
79 | end
80 | end
81 |
82 | def empty?
83 | @map.empty? && @defaults.empty?
84 | end
85 |
86 | def inspect
87 | "#<#{self.class.name} @map=#{@map.inspect} @defaults=#{@defaults.inspect}>"
88 | end
89 |
90 | protected
91 |
92 | def compute(tags, include_defaults = true, exclude = [])
93 | result = []
94 | Array(tags).each do |tag|
95 | tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym } - exclude
96 | result += tags
97 | tags.each { |_tag| result += compute(@map[_tag], false, exclude + result) if @map[_tag] }
98 | end
99 |
100 | result.push(*defaults) if include_defaults
101 | result.uniq!
102 | result.compact!
103 | result
104 | end
105 | end
106 | end
107 | end
108 |
--------------------------------------------------------------------------------
/lib/i18n/locale/tag.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Locale
5 | module Tag
6 | autoload :Parents, 'i18n/locale/tag/parents'
7 | autoload :Rfc4646, 'i18n/locale/tag/rfc4646'
8 | autoload :Simple, 'i18n/locale/tag/simple'
9 |
10 | class << self
11 | # Returns the current locale tag implementation. Defaults to +I18n::Locale::Tag::Simple+.
12 | def implementation
13 | @@implementation ||= Simple
14 | end
15 |
16 | # Sets the current locale tag implementation. Use this to set a different locale tag implementation.
17 | def implementation=(implementation)
18 | @@implementation = implementation
19 | end
20 |
21 | # Factory method for locale tags. Delegates to the current locale tag implementation.
22 | def tag(tag)
23 | implementation.tag(tag)
24 | end
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/i18n/locale/tag/parents.rb:
--------------------------------------------------------------------------------
1 | module I18n
2 | module Locale
3 | module Tag
4 | module Parents
5 | def parent
6 | @parent ||=
7 | begin
8 | segs = to_a
9 | segs.compact!
10 | segs.length > 1 ? self.class.tag(*segs[0..(segs.length - 2)].join('-')) : nil
11 | end
12 | end
13 |
14 | def self_and_parents
15 | @self_and_parents ||= [self].concat parents
16 | end
17 |
18 | def parents
19 | @parents ||= parent ? [parent].concat(parent.parents) : []
20 | end
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/i18n/locale/tag/rfc4646.rb:
--------------------------------------------------------------------------------
1 | # RFC 4646/47 compliant Locale tag implementation that parses locale tags to
2 | # subtags such as language, script, region, variant etc.
3 | #
4 | # For more information see by http://en.wikipedia.org/wiki/IETF_language_tag
5 | #
6 | # Rfc4646::Parser does not implement grandfathered tags.
7 |
8 | module I18n
9 | module Locale
10 | module Tag
11 | RFC4646_SUBTAGS = [ :language, :script, :region, :variant, :extension, :privateuse, :grandfathered ]
12 | RFC4646_FORMATS = { :language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase }
13 |
14 | class Rfc4646 < Struct.new(*RFC4646_SUBTAGS)
15 | class << self
16 | # Parses the given tag and returns a Tag instance if it is valid.
17 | # Returns false if the given tag is not valid according to RFC 4646.
18 | def tag(tag)
19 | matches = parser.match(tag)
20 | new(*matches) if matches
21 | end
22 |
23 | def parser
24 | @@parser ||= Rfc4646::Parser
25 | end
26 |
27 | def parser=(parser)
28 | @@parser = parser
29 | end
30 | end
31 |
32 | include Parents
33 |
34 | RFC4646_FORMATS.each do |name, format|
35 | define_method(name) { self[name].send(format) unless self[name].nil? }
36 | end
37 |
38 | def to_sym
39 | to_s.to_sym
40 | end
41 |
42 | def to_s
43 | @tag ||= to_a.compact.join("-")
44 | end
45 |
46 | def to_a
47 | members.collect { |attr| self.send(attr) }
48 | end
49 |
50 | module Parser
51 | PATTERN = %r{\A(?:
52 | ([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language
53 | (?:-([a-z]{4}))? # script
54 | (?:-([a-z]{2}|\d{3}))? # region
55 | (?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant
56 | (?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension
57 | (?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag
58 | (x(?:-[0-9a-z]{1,8})+)| # privateuse tag
59 | /* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered
60 | )\z}xi
61 |
62 | class << self
63 | def match(tag)
64 | c = PATTERN.match(tag.to_s).captures
65 | c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here?
66 | rescue
67 | false
68 | end
69 | end
70 | end
71 | end
72 | end
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/lib/i18n/locale/tag/simple.rb:
--------------------------------------------------------------------------------
1 | # Simple Locale tag implementation that computes subtags by simply splitting
2 | # the locale tag at '-' occurrences.
3 | module I18n
4 | module Locale
5 | module Tag
6 | class Simple
7 | class << self
8 | def tag(tag)
9 | new(tag)
10 | end
11 | end
12 |
13 | include Parents
14 |
15 | attr_reader :tag
16 |
17 | def initialize(*tag)
18 | @tag = tag.join('-').to_sym
19 | end
20 |
21 | def subtags
22 | @subtags = tag.to_s.split('-').map!(&:to_s)
23 | end
24 |
25 | def to_sym
26 | tag
27 | end
28 |
29 | def to_s
30 | tag.to_s
31 | end
32 |
33 | def to_a
34 | subtags
35 | end
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/i18n/middleware.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module I18n
4 | class Middleware
5 |
6 | def initialize(app)
7 | @app = app
8 | end
9 |
10 | def call(env)
11 | @app.call(env)
12 | ensure
13 | Thread.current[:i18n_config] = I18n::Config.new
14 | end
15 |
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/i18n/tests.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module I18n
4 | module Tests
5 | autoload :Basics, 'i18n/tests/basics'
6 | autoload :Defaults, 'i18n/tests/defaults'
7 | autoload :Interpolation, 'i18n/tests/interpolation'
8 | autoload :Link, 'i18n/tests/link'
9 | autoload :Localization, 'i18n/tests/localization'
10 | autoload :Lookup, 'i18n/tests/lookup'
11 | autoload :Pluralization, 'i18n/tests/pluralization'
12 | autoload :Procs, 'i18n/tests/procs'
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/i18n/tests/basics.rb:
--------------------------------------------------------------------------------
1 | module I18n
2 | module Tests
3 | module Basics
4 | def teardown
5 | I18n.available_locales = nil
6 | end
7 |
8 | test "available_locales returns the available_locales produced by the backend, by default" do
9 | I18n.backend.store_translations('de', :foo => 'bar')
10 | I18n.backend.store_translations('en', :foo => 'foo')
11 |
12 | assert_equal I18n.available_locales, I18n.backend.available_locales
13 | end
14 |
15 | test "available_locales can be set to something else independently from the actual locale data" do
16 | I18n.backend.store_translations('de', :foo => 'bar')
17 | I18n.backend.store_translations('en', :foo => 'foo')
18 |
19 | I18n.available_locales = :foo
20 | assert_equal [:foo], I18n.available_locales
21 |
22 | I18n.available_locales = [:foo, 'bar']
23 | assert_equal [:foo, :bar], I18n.available_locales
24 |
25 | I18n.available_locales = nil
26 | assert_equal I18n.available_locales, I18n.backend.available_locales
27 | end
28 |
29 | test "available_locales memoizes when set explicitly" do
30 | I18n.backend.expects(:available_locales).never
31 | I18n.available_locales = [:foo]
32 | I18n.backend.store_translations('de', :bar => 'baz')
33 | I18n.reload!
34 | assert_equal [:foo], I18n.available_locales
35 | end
36 |
37 | test "available_locales delegates to the backend when not set explicitly" do
38 | original_available_locales_value = I18n.backend.available_locales
39 | I18n.backend.expects(:available_locales).returns(original_available_locales_value).twice
40 | assert_equal I18n.backend.available_locales, I18n.available_locales
41 | end
42 |
43 | test "exists? is implemented by the backend" do
44 | I18n.backend.store_translations(:foo, :bar => 'baz')
45 | assert I18n.exists?(:bar, :foo)
46 | end
47 |
48 | test "storing a nil value as a translation removes it from the available locale data" do
49 | I18n.backend.store_translations(:en, :to_be_deleted => 'bar')
50 | assert_equal 'bar', I18n.t(:to_be_deleted, :default => 'baz')
51 |
52 | I18n.cache_store.clear if I18n.respond_to?(:cache_store) && I18n.cache_store
53 | I18n.backend.store_translations(:en, :to_be_deleted => nil)
54 | assert_equal 'baz', I18n.t(:to_be_deleted, :default => 'baz')
55 | end
56 | end
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/lib/i18n/tests/defaults.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Defaults
6 | def setup
7 | super
8 | I18n.backend.store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' })
9 | end
10 |
11 | test "defaults: given nil as a key it returns the given default" do
12 | assert_equal 'default', I18n.t(nil, :default => 'default')
13 | end
14 |
15 | test "defaults: given a symbol as a default it translates the symbol" do
16 | assert_equal 'bar', I18n.t(nil, :default => :'foo.bar')
17 | end
18 |
19 | test "defaults: given a symbol as a default and a scope it stays inside the scope when looking up the symbol" do
20 | assert_equal 'bar', I18n.t(:missing, :default => :bar, :scope => :foo)
21 | end
22 |
23 | test "defaults: given an array as a default it returns the first match" do
24 | assert_equal 'bar', I18n.t(:does_not_exist, :default => [:does_not_exist_2, :'foo.bar'])
25 | end
26 |
27 | test "defaults: given an array as a default with false it returns false" do
28 | assert_equal false, I18n.t(:does_not_exist, :default => [false])
29 | end
30 |
31 | test "defaults: given false it returns false" do
32 | assert_equal false, I18n.t(:does_not_exist, :default => false)
33 | end
34 |
35 | test "defaults: given nil it returns nil" do
36 | assert_nil I18n.t(:does_not_exist, :default => nil)
37 | end
38 |
39 | test "defaults: given an array of missing keys it raises a MissingTranslationData exception" do
40 | assert_raises I18n::MissingTranslationData do
41 | I18n.t(:does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3], :raise => true)
42 | end
43 | end
44 |
45 | test "defaults: using a custom scope separator" do
46 | # data must have been stored using the custom separator when using the ActiveRecord backend
47 | I18n.backend.store_translations(:en, { :foo => { :bar => 'bar' } }, { :separator => '|' })
48 | assert_equal 'bar', I18n.t(nil, :default => :'foo|bar', :separator => '|')
49 | end
50 |
51 | # Addresses issue: #599
52 | test "defaults: only interpolates once when resolving defaults" do
53 | I18n.backend.store_translations(:en, :greeting => 'hey %{name}')
54 | assert_equal 'hey %{dont_interpolate_me}',
55 | I18n.t(:does_not_exist, :name => '%{dont_interpolate_me}', default: [:greeting])
56 | end
57 | end
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/lib/i18n/tests/link.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Link
6 | test "linked lookup: if a key resolves to a symbol it looks up the symbol" do
7 | I18n.backend.store_translations 'en', {
8 | :link => :linked,
9 | :linked => 'linked'
10 | }
11 | assert_equal 'linked', I18n.backend.translate('en', :link)
12 | end
13 |
14 | test "linked lookup: if a key resolves to a dot-separated symbol it looks up the symbol" do
15 | I18n.backend.store_translations 'en', {
16 | :link => :"foo.linked",
17 | :foo => { :linked => 'linked' }
18 | }
19 | assert_equal('linked', I18n.backend.translate('en', :link))
20 | end
21 |
22 | test "linked lookup: if a dot-separated key resolves to a symbol it looks up the symbol" do
23 | I18n.backend.store_translations 'en', {
24 | :foo => { :link => :linked },
25 | :linked => 'linked'
26 | }
27 | assert_equal('linked', I18n.backend.translate('en', :'foo.link'))
28 | end
29 |
30 | test "linked lookup: if a dot-separated key resolves to a dot-separated symbol it looks up the symbol" do
31 | I18n.backend.store_translations 'en', {
32 | :foo => { :link => :"bar.linked" },
33 | :bar => { :linked => 'linked' }
34 | }
35 | assert_equal('linked', I18n.backend.translate('en', :'foo.link'))
36 | end
37 |
38 | test "linked lookup: links always refer to the absolute key" do
39 | I18n.backend.store_translations 'en', {
40 | :foo => { :link => :linked, :linked => 'linked in foo' },
41 | :linked => 'linked absolutely'
42 | }
43 | assert_equal 'linked absolutely', I18n.backend.translate('en', :link, :scope => :foo)
44 | end
45 |
46 | test "linked lookup: a link can resolve to a namespace in the middle of a dot-separated key" do
47 | I18n.backend.store_translations 'en', {
48 | :activemodel => { :errors => { :messages => { :blank => "can't be blank" } } },
49 | :activerecord => { :errors => { :messages => :"activemodel.errors.messages" } }
50 | }
51 | assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank")
52 | assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank")
53 | end
54 |
55 | test "linked lookup: a link can resolve with option :count" do
56 | I18n.backend.store_translations 'en', {
57 | :counter => :counted,
58 | :counted => { :foo => { :one => "one", :other => "other" }, :bar => "bar" }
59 | }
60 | assert_equal "one", I18n.t(:'counter.foo', count: 1)
61 | assert_equal "other", I18n.t(:'counter.foo', count: 2)
62 | assert_equal "bar", I18n.t(:'counter.bar', count: 3)
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/i18n/tests/localization.rb:
--------------------------------------------------------------------------------
1 | module I18n
2 | module Tests
3 | module Localization
4 | autoload :Date, 'i18n/tests/localization/date'
5 | autoload :DateTime, 'i18n/tests/localization/date_time'
6 | autoload :Time, 'i18n/tests/localization/time'
7 | autoload :Procs, 'i18n/tests/localization/procs'
8 |
9 | def self.included(base)
10 | base.class_eval do
11 | include I18n::Tests::Localization::Date
12 | include I18n::Tests::Localization::DateTime
13 | include I18n::Tests::Localization::Procs
14 | include I18n::Tests::Localization::Time
15 | end
16 | end
17 | end
18 | end
19 | end
--------------------------------------------------------------------------------
/lib/i18n/tests/localization/date.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Localization
6 | module Date
7 | def setup
8 | super
9 | setup_date_translations
10 | @date = ::Date.new(2008, 3, 1)
11 | end
12 |
13 | test "localize Date: given the short format it uses it" do
14 | assert_equal '01. Mär', I18n.l(@date, :format => :short, :locale => :de)
15 | end
16 |
17 | test "localize Date: given the long format it uses it" do
18 | assert_equal '01. März 2008', I18n.l(@date, :format => :long, :locale => :de)
19 | end
20 |
21 | test "localize Date: given the default format it uses it" do
22 | assert_equal '01.03.2008', I18n.l(@date, :format => :default, :locale => :de)
23 | end
24 |
25 | test "localize Date: given a day name format it returns the correct day name" do
26 | assert_equal 'Samstag', I18n.l(@date, :format => '%A', :locale => :de)
27 | end
28 |
29 | test "localize Date: given a uppercased day name format it returns the correct day name in upcase" do
30 | assert_equal 'samstag'.upcase, I18n.l(@date, :format => '%^A', :locale => :de)
31 | end
32 |
33 | test "localize Date: given an abbreviated day name format it returns the correct abbreviated day name" do
34 | assert_equal 'Sa', I18n.l(@date, :format => '%a', :locale => :de)
35 | end
36 |
37 | test "localize Date: given an meridian indicator format it returns the correct meridian indicator" do
38 | assert_equal 'AM', I18n.l(@date, :format => '%p', :locale => :de)
39 | assert_equal 'am', I18n.l(@date, :format => '%P', :locale => :de)
40 | end
41 |
42 | test "localize Date: given an abbreviated and uppercased day name format it returns the correct abbreviated day name in upcase" do
43 | assert_equal 'sa'.upcase, I18n.l(@date, :format => '%^a', :locale => :de)
44 | end
45 |
46 | test "localize Date: given a month name format it returns the correct month name" do
47 | assert_equal 'März', I18n.l(@date, :format => '%B', :locale => :de)
48 | end
49 |
50 | test "localize Date: given a uppercased month name format it returns the correct month name in upcase" do
51 | assert_equal 'märz'.upcase, I18n.l(@date, :format => '%^B', :locale => :de)
52 | end
53 |
54 | test "localize Date: given an abbreviated month name format it returns the correct abbreviated month name" do
55 | assert_equal 'Mär', I18n.l(@date, :format => '%b', :locale => :de)
56 | end
57 |
58 | test "localize Date: given an abbreviated and uppercased month name format it returns the correct abbreviated month name in upcase" do
59 | assert_equal 'mär'.upcase, I18n.l(@date, :format => '%^b', :locale => :de)
60 | end
61 |
62 | test "localize Date: given a date format with the month name upcased it returns the correct value" do
63 | assert_equal '1. FEBRUAR 2008', I18n.l(::Date.new(2008, 2, 1), :format => "%-d. %^B %Y", :locale => :de)
64 | end
65 |
66 | test "localize Date: given missing translations it returns the correct error message" do
67 | assert_equal 'Translation missing: fr.date.abbr_month_names', I18n.l(@date, :format => '%b', :locale => :fr)
68 | end
69 |
70 | test "localize Date: given an unknown format it does not fail" do
71 | assert_nothing_raised { I18n.l(@date, :format => '%x') }
72 | end
73 |
74 | test "localize Date: does not modify the options hash" do
75 | options = { :format => '%b', :locale => :de }
76 | assert_equal 'Mär', I18n.l(@date, **options)
77 | assert_equal({ :format => '%b', :locale => :de }, options)
78 | assert_nothing_raised { I18n.l(@date, **options.freeze) }
79 | end
80 |
81 | test "localize Date: given nil with default value it returns default" do
82 | assert_equal 'default', I18n.l(nil, :default => 'default')
83 | end
84 |
85 | test "localize Date: given nil it raises I18n::ArgumentError" do
86 | assert_raises(I18n::ArgumentError) { I18n.l(nil) }
87 | end
88 |
89 | test "localize Date: given a plain Object it raises I18n::ArgumentError" do
90 | assert_raises(I18n::ArgumentError) { I18n.l(Object.new) }
91 | end
92 |
93 | test "localize Date: given a format is missing it raises I18n::MissingTranslationData" do
94 | assert_raises(I18n::MissingTranslationData) { I18n.l(@date, :format => :missing) }
95 | end
96 |
97 | test "localize Date: it does not alter the format string" do
98 | assert_equal '01. Februar 2009', I18n.l(::Date.parse('2009-02-01'), :format => :long, :locale => :de)
99 | assert_equal '01. Oktober 2009', I18n.l(::Date.parse('2009-10-01'), :format => :long, :locale => :de)
100 | end
101 |
102 | protected
103 |
104 | def setup_date_translations
105 | I18n.backend.store_translations :de, {
106 | :date => {
107 | :formats => {
108 | :default => "%d.%m.%Y",
109 | :short => "%d. %b",
110 | :long => "%d. %B %Y",
111 | },
112 | :day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag),
113 | :abbr_day_names => %w(So Mo Di Mi Do Fr Sa),
114 | :month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil),
115 | :abbr_month_names => %w(Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil)
116 | }
117 | }
118 | end
119 | end
120 | end
121 | end
122 | end
123 |
--------------------------------------------------------------------------------
/lib/i18n/tests/localization/date_time.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Localization
6 | module DateTime
7 | def setup
8 | super
9 | setup_datetime_translations
10 | @datetime = ::DateTime.new(2008, 3, 1, 6)
11 | @other_datetime = ::DateTime.new(2008, 3, 1, 18)
12 | end
13 |
14 | test "localize DateTime: given the short format it uses it" do
15 | assert_equal '01. Mär 06:00', I18n.l(@datetime, :format => :short, :locale => :de)
16 | end
17 |
18 | test "localize DateTime: given the long format it uses it" do
19 | assert_equal '01. März 2008 06:00', I18n.l(@datetime, :format => :long, :locale => :de)
20 | end
21 |
22 | test "localize DateTime: given the default format it uses it" do
23 | assert_equal 'Sa, 01. Mär 2008 06:00:00 +0000', I18n.l(@datetime, :format => :default, :locale => :de)
24 | end
25 |
26 | test "localize DateTime: given a day name format it returns the correct day name" do
27 | assert_equal 'Samstag', I18n.l(@datetime, :format => '%A', :locale => :de)
28 | end
29 |
30 | test "localize DateTime: given a uppercased day name format it returns the correct day name in upcase" do
31 | assert_equal 'samstag'.upcase, I18n.l(@datetime, :format => '%^A', :locale => :de)
32 | end
33 |
34 | test "localize DateTime: given an abbreviated day name format it returns the correct abbreviated day name" do
35 | assert_equal 'Sa', I18n.l(@datetime, :format => '%a', :locale => :de)
36 | end
37 |
38 | test "localize DateTime: given an abbreviated and uppercased day name format it returns the correct abbreviated day name in upcase" do
39 | assert_equal 'sa'.upcase, I18n.l(@datetime, :format => '%^a', :locale => :de)
40 | end
41 |
42 | test "localize DateTime: given a month name format it returns the correct month name" do
43 | assert_equal 'März', I18n.l(@datetime, :format => '%B', :locale => :de)
44 | end
45 |
46 | test "localize DateTime: given a uppercased month name format it returns the correct month name in upcase" do
47 | assert_equal 'märz'.upcase, I18n.l(@datetime, :format => '%^B', :locale => :de)
48 | end
49 |
50 | test "localize DateTime: given an abbreviated month name format it returns the correct abbreviated month name" do
51 | assert_equal 'Mär', I18n.l(@datetime, :format => '%b', :locale => :de)
52 | end
53 |
54 | test "localize DateTime: given an abbreviated and uppercased month name format it returns the correct abbreviated month name in upcase" do
55 | assert_equal 'mär'.upcase, I18n.l(@datetime, :format => '%^b', :locale => :de)
56 | end
57 |
58 | test "localize DateTime: given a date format with the month name upcased it returns the correct value" do
59 | assert_equal '1. FEBRUAR 2008', I18n.l(::DateTime.new(2008, 2, 1, 6), :format => "%-d. %^B %Y", :locale => :de)
60 | end
61 |
62 | test "localize DateTime: given missing translations it returns the correct error message" do
63 | assert_equal 'Translation missing: fr.date.abbr_month_names', I18n.l(@datetime, :format => '%b', :locale => :fr)
64 | end
65 |
66 | test "localize DateTime: given a meridian indicator format it returns the correct meridian indicator" do
67 | assert_equal 'AM', I18n.l(@datetime, :format => '%p', :locale => :de)
68 | assert_equal 'PM', I18n.l(@other_datetime, :format => '%p', :locale => :de)
69 | end
70 |
71 | test "localize DateTime: given a meridian indicator format it returns the correct meridian indicator in downcase" do
72 | assert_equal 'am', I18n.l(@datetime, :format => '%P', :locale => :de)
73 | assert_equal 'pm', I18n.l(@other_datetime, :format => '%P', :locale => :de)
74 | end
75 |
76 | test "localize DateTime: given an unknown format it does not fail" do
77 | assert_nothing_raised { I18n.l(@datetime, :format => '%x') }
78 | end
79 |
80 | test "localize DateTime: given a format is missing it raises I18n::MissingTranslationData" do
81 | assert_raises(I18n::MissingTranslationData) { I18n.l(@datetime, :format => :missing) }
82 | end
83 |
84 | protected
85 |
86 | def setup_datetime_translations
87 | # time translations might have been set up in Tests::Api::Localization::Time
88 | I18n.backend.store_translations :de, {
89 | :time => {
90 | :formats => {
91 | :default => "%a, %d. %b %Y %H:%M:%S %z",
92 | :short => "%d. %b %H:%M",
93 | :long => "%d. %B %Y %H:%M"
94 | },
95 | :am => 'am',
96 | :pm => 'pm'
97 | }
98 | }
99 | end
100 | end
101 | end
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/lib/i18n/tests/localization/procs.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Localization
6 | module Procs
7 | test "localize: using day names from lambdas" do
8 | setup_time_proc_translations
9 | time = ::Time.utc(2008, 3, 1, 6, 0)
10 | assert_match(/Суббота/, I18n.l(time, :format => "%A, %d %B", :locale => :ru))
11 | assert_match(/суббота/, I18n.l(time, :format => "%d %B (%A)", :locale => :ru))
12 | end
13 |
14 | test "localize: using month names from lambdas" do
15 | setup_time_proc_translations
16 | time = ::Time.utc(2008, 3, 1, 6, 0)
17 | assert_match(/марта/, I18n.l(time, :format => "%d %B %Y", :locale => :ru))
18 | assert_match(/Март /, I18n.l(time, :format => "%B %Y", :locale => :ru))
19 | end
20 |
21 | test "localize: using abbreviated day names from lambdas" do
22 | setup_time_proc_translations
23 | time = ::Time.utc(2008, 3, 1, 6, 0)
24 | assert_match(/марта/, I18n.l(time, :format => "%d %b %Y", :locale => :ru))
25 | assert_match(/март /, I18n.l(time, :format => "%b %Y", :locale => :ru))
26 | end
27 |
28 | test "localize Date: given a format that resolves to a Proc it calls the Proc with the object" do
29 | setup_time_proc_translations
30 | date = ::Date.new(2008, 3, 1)
31 | assert_equal '[Sat, 01 Mar 2008, {}]', I18n.l(date, :format => :proc, :locale => :ru)
32 | end
33 |
34 | test "localize Date: given a format that resolves to a Proc it calls the Proc with the object and extra options" do
35 | setup_time_proc_translations
36 | date = ::Date.new(2008, 3, 1)
37 | assert_equal %|[Sat, 01 Mar 2008, #{{:foo=>"foo"}}]|, I18n.l(date, :format => :proc, :foo => 'foo', :locale => :ru)
38 | end
39 |
40 | test "localize DateTime: given a format that resolves to a Proc it calls the Proc with the object" do
41 | setup_time_proc_translations
42 | datetime = ::DateTime.new(2008, 3, 1, 6)
43 | assert_equal '[Sat, 01 Mar 2008 06:00:00 +00:00, {}]', I18n.l(datetime, :format => :proc, :locale => :ru)
44 | end
45 |
46 | test "localize DateTime: given a format that resolves to a Proc it calls the Proc with the object and extra options" do
47 | setup_time_proc_translations
48 | datetime = ::DateTime.new(2008, 3, 1, 6)
49 | assert_equal %|[Sat, 01 Mar 2008 06:00:00 +00:00, #{{:foo=>"foo"}}]|, I18n.l(datetime, :format => :proc, :foo => 'foo', :locale => :ru)
50 | end
51 |
52 | test "localize Time: given a format that resolves to a Proc it calls the Proc with the object" do
53 | setup_time_proc_translations
54 | time = ::Time.utc(2008, 3, 1, 6, 0)
55 | assert_equal I18n::Tests::Localization::Procs.inspect_args([time], {}), I18n.l(time, :format => :proc, :locale => :ru)
56 | end
57 |
58 | test "localize Time: given a format that resolves to a Proc it calls the Proc with the object and extra options" do
59 | setup_time_proc_translations
60 | time = ::Time.utc(2008, 3, 1, 6, 0)
61 | options = { :foo => 'foo' }
62 | assert_equal I18n::Tests::Localization::Procs.inspect_args([time], options), I18n.l(time, **options.merge(:format => :proc, :locale => :ru))
63 | end
64 |
65 | protected
66 |
67 | def self.inspect_args(args, kwargs)
68 | args << kwargs
69 | args = args.map do |arg|
70 | case arg
71 | when ::Time, ::DateTime
72 | arg.strftime('%a, %d %b %Y %H:%M:%S %Z').sub('+0000', '+00:00')
73 | when ::Date
74 | arg.strftime('%a, %d %b %Y')
75 | when Hash
76 | arg.delete(:fallback_in_progress)
77 | arg.delete(:fallback_original_locale)
78 | arg.inspect
79 | else
80 | arg.inspect
81 | end
82 | end
83 | "[#{args.join(', ')}]"
84 | end
85 |
86 | def setup_time_proc_translations
87 | I18n.backend.store_translations :ru, {
88 | :time => {
89 | :formats => {
90 | :proc => lambda { |*args, **kwargs| I18n::Tests::Localization::Procs.inspect_args(args, kwargs) }
91 | }
92 | },
93 | :date => {
94 | :formats => {
95 | :proc => lambda { |*args, **kwargs| I18n::Tests::Localization::Procs.inspect_args(args, kwargs) }
96 | },
97 | :'day_names' => lambda { |key, options|
98 | (options[:format] =~ /^%A/) ?
99 | %w(Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота) :
100 | %w(воскресенье понедельник вторник среда четверг пятница суббота)
101 | },
102 | :'month_names' => lambda { |key, options|
103 | (options[:format] =~ /(%d|%e)(\s*)?(%B)/) ?
104 | %w(января февраля марта апреля мая июня июля августа сентября октября ноября декабря).unshift(nil) :
105 | %w(Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь).unshift(nil)
106 | },
107 | :'abbr_month_names' => lambda { |key, options|
108 | (options[:format] =~ /(%d|%e)(\s*)(%b)/) ?
109 | %w(янв. февр. марта апр. мая июня июля авг. сент. окт. нояб. дек.).unshift(nil) :
110 | %w(янв. февр. март апр. май июнь июль авг. сент. окт. нояб. дек.).unshift(nil)
111 | },
112 | }
113 | }
114 | end
115 | end
116 | end
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/lib/i18n/tests/localization/time.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Localization
6 | module Time
7 | def setup
8 | super
9 | setup_time_translations
10 | @time = ::Time.utc(2008, 3, 1, 6, 0)
11 | @other_time = ::Time.utc(2008, 3, 1, 18, 0)
12 | end
13 |
14 | test "localize Time: given the short format it uses it" do
15 | assert_equal '01. Mär 06:00', I18n.l(@time, :format => :short, :locale => :de)
16 | end
17 |
18 | test "localize Time: given the long format it uses it" do
19 | assert_equal '01. März 2008 06:00', I18n.l(@time, :format => :long, :locale => :de)
20 | end
21 |
22 | # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this?
23 | # def test_localize_given_the_default_format_it_uses_it
24 | # assert_equal 'Sa, 01. Mar 2008 06:00:00 +0000', I18n.l(@time, :format => :default, :locale => :de)
25 | # end
26 |
27 | test "localize Time: given a day name format it returns the correct day name" do
28 | assert_equal 'Samstag', I18n.l(@time, :format => '%A', :locale => :de)
29 | end
30 |
31 | test "localize Time: given a uppercased day name format it returns the correct day name in upcase" do
32 | assert_equal 'samstag'.upcase, I18n.l(@time, :format => '%^A', :locale => :de)
33 | end
34 |
35 | test "localize Time: given an abbreviated day name format it returns the correct abbreviated day name" do
36 | assert_equal 'Sa', I18n.l(@time, :format => '%a', :locale => :de)
37 | end
38 |
39 | test "localize Time: given an abbreviated and uppercased day name format it returns the correct abbreviated day name in upcase" do
40 | assert_equal 'sa'.upcase, I18n.l(@time, :format => '%^a', :locale => :de)
41 | end
42 |
43 | test "localize Time: given a month name format it returns the correct month name" do
44 | assert_equal 'März', I18n.l(@time, :format => '%B', :locale => :de)
45 | end
46 |
47 | test "localize Time: given a uppercased month name format it returns the correct month name in upcase" do
48 | assert_equal 'märz'.upcase, I18n.l(@time, :format => '%^B', :locale => :de)
49 | end
50 |
51 | test "localize Time: given an abbreviated month name format it returns the correct abbreviated month name" do
52 | assert_equal 'Mär', I18n.l(@time, :format => '%b', :locale => :de)
53 | end
54 |
55 | test "localize Time: given an abbreviated and uppercased month name format it returns the correct abbreviated month name in upcase" do
56 | assert_equal 'mär'.upcase, I18n.l(@time, :format => '%^b', :locale => :de)
57 | end
58 |
59 | test "localize Time: given a date format with the month name upcased it returns the correct value" do
60 | assert_equal '1. FEBRUAR 2008', I18n.l(::Time.utc(2008, 2, 1, 6, 0), :format => "%-d. %^B %Y", :locale => :de)
61 | end
62 |
63 | test "localize Time: given missing translations it returns the correct error message" do
64 | assert_equal 'Translation missing: fr.date.abbr_month_names', I18n.l(@time, :format => '%b', :locale => :fr)
65 | end
66 |
67 | test "localize Time: given a meridian indicator format it returns the correct meridian indicator" do
68 | assert_equal 'AM', I18n.l(@time, :format => '%p', :locale => :de)
69 | assert_equal 'PM', I18n.l(@other_time, :format => '%p', :locale => :de)
70 | end
71 |
72 | test "localize Time: given a meridian indicator format it returns the correct meridian indicator in upcase" do
73 | assert_equal 'am', I18n.l(@time, :format => '%P', :locale => :de)
74 | assert_equal 'pm', I18n.l(@other_time, :format => '%P', :locale => :de)
75 | end
76 |
77 | test "localize Time: given an unknown format it does not fail" do
78 | assert_nothing_raised { I18n.l(@time, :format => '%x') }
79 | end
80 |
81 | test "localize Time: given a format is missing it raises I18n::MissingTranslationData" do
82 | assert_raises(I18n::MissingTranslationData) { I18n.l(@time, :format => :missing) }
83 | end
84 |
85 | protected
86 |
87 | def setup_time_translations
88 | I18n.backend.store_translations :de, {
89 | :time => {
90 | :formats => {
91 | :default => "%a, %d. %b %Y %H:%M:%S %z",
92 | :short => "%d. %b %H:%M",
93 | :long => "%d. %B %Y %H:%M",
94 | },
95 | :am => 'am',
96 | :pm => 'pm'
97 | }
98 | }
99 | end
100 | end
101 | end
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/lib/i18n/tests/lookup.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Lookup
6 | def setup
7 | super
8 | I18n.backend.store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' }, :falsy => false, :truthy => true,
9 | :string => "a", :array => %w(a b c), :hash => { "a" => "b" })
10 | end
11 |
12 | test "lookup: it returns a string" do
13 | assert_equal("a", I18n.t(:string))
14 | end
15 |
16 | test "lookup: it returns hash" do
17 | assert_equal({ :a => "b" }, I18n.t(:hash))
18 | end
19 |
20 | test "lookup: it returns an array" do
21 | assert_equal(%w(a b c), I18n.t(:array))
22 | end
23 |
24 | test "lookup: it returns a native true" do
25 | assert I18n.t(:truthy) === true
26 | end
27 |
28 | test "lookup: it returns a native false" do
29 | assert I18n.t(:falsy) === false
30 | end
31 |
32 | test "lookup: given a missing key, no default and no raise option it returns an error message" do
33 | assert_equal "Translation missing: en.missing", I18n.t(:missing)
34 | end
35 |
36 | test "lookup: given a missing key, no default and the raise option it raises MissingTranslationData" do
37 | assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
38 | end
39 |
40 | test "lookup: does not raise an exception if no translation data is present for the given locale" do
41 | assert_nothing_raised { I18n.t(:foo, :locale => :xx) }
42 | end
43 |
44 | test "lookup: does not modify the options hash" do
45 | options = {}
46 | assert_equal "a", I18n.t(:string, **options)
47 | assert_equal({}, options)
48 | assert_nothing_raised { I18n.t(:string, **options.freeze) }
49 | end
50 |
51 | test "lookup: given an array of keys it translates all of them" do
52 | assert_equal %w(bar baz), I18n.t([:bar, :baz], :scope => [:foo])
53 | end
54 |
55 | test "lookup: using a custom scope separator" do
56 | # data must have been stored using the custom separator when using the ActiveRecord backend
57 | I18n.backend.store_translations(:en, { :foo => { :bar => 'bar' } }, { :separator => '|' })
58 | assert_equal 'bar', I18n.t('foo|bar', :separator => '|')
59 | end
60 |
61 | # In fact it probably *should* fail but Rails currently relies on using the default locale instead.
62 | # So we'll stick to this for now until we get it fixed in Rails.
63 | test "lookup: given nil as a locale it does not raise but use the default locale" do
64 | # assert_raises(I18n::InvalidLocale) { I18n.t(:bar, :locale => nil) }
65 | assert_nothing_raised { I18n.t(:bar, :locale => nil) }
66 | end
67 |
68 | test "lookup: a resulting String is not frozen" do
69 | assert !I18n.t(:string).frozen?
70 | end
71 |
72 | test "lookup: a resulting Array is not frozen" do
73 | assert !I18n.t(:array).frozen?
74 | end
75 |
76 | test "lookup: a resulting Hash is not frozen" do
77 | assert !I18n.t(:hash).frozen?
78 | end
79 |
80 | # Addresses issue: #599
81 | test "lookup: only interpolates once when resolving symbols" do
82 | I18n.backend.store_translations(:en, foo: :bar, bar: '%{value}')
83 | assert_equal '%{dont_interpolate_me}', I18n.t(:foo, value: '%{dont_interpolate_me}')
84 | end
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/lib/i18n/tests/pluralization.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Pluralization
6 | test "pluralization: given 0 it returns the :zero translation if it is defined" do
7 | assert_equal 'zero', I18n.t(:default => { :zero => 'zero' }, :count => 0)
8 | end
9 |
10 | test "pluralization: given 0 it returns the :other translation if :zero is not defined" do
11 | assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 0)
12 | end
13 |
14 | test "pluralization: given 1 it returns the singular translation" do
15 | assert_equal 'bar', I18n.t(:default => { :one => 'bar' }, :count => 1)
16 | end
17 |
18 | test "pluralization: given 2 it returns the :other translation" do
19 | assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 2)
20 | end
21 |
22 | test "pluralization: given 3 it returns the :other translation" do
23 | assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 3)
24 | end
25 |
26 | test "pluralization: given nil it returns the whole entry" do
27 | assert_equal({ :one => 'bar' }, I18n.t(:default => { :one => 'bar' }, :count => nil))
28 | end
29 |
30 | test "pluralization: given incomplete pluralization data it raises I18n::InvalidPluralizationData" do
31 | assert_raises(I18n::InvalidPluralizationData) { I18n.t(:default => { :one => 'bar' }, :count => 2) }
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/i18n/tests/procs.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module I18n
4 | module Tests
5 | module Procs
6 | test "lookup: given a translation is a proc it calls the proc with the key and interpolation values" do
7 | I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| I18n::Tests::Procs.filter_args(*args) })
8 | assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(:a_lambda, :foo => 'foo')
9 | end
10 |
11 | test "lookup: given a translation is a proc it passes the interpolation values as keyword arguments" do
12 | I18n.backend.store_translations(:en, :a_lambda => lambda { |key, foo:, **| I18n::Tests::Procs.filter_args(key, foo: foo) })
13 | assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(:a_lambda, :foo => 'foo')
14 | end
15 |
16 | test "defaults: given a default is a Proc it calls it with the key and interpolation values" do
17 | proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
18 | assert_equal %|[nil, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => proc, :foo => 'foo')
19 | end
20 |
21 | test "defaults: given a default is a key that resolves to a Proc it calls it with the key and interpolation values" do
22 | the_lambda = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
23 | I18n.backend.store_translations(:en, :a_lambda => the_lambda)
24 | assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => :a_lambda, :foo => 'foo')
25 | assert_equal %|[:a_lambda, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => [nil, :a_lambda], :foo => 'foo')
26 | end
27 |
28 | test "interpolation: given an interpolation value is a lambda it calls it with key and values before interpolating it" do
29 | proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
30 | if RUBY_VERSION < "3.4"
31 | assert_match %r(\[\{:foo=>#\}\]), I18n.t(nil, :default => '%{foo}', :foo => proc)
32 | else
33 | assert_match %r(\[\{foo: #\}\]), I18n.t(nil, :default => '%{foo}', :foo => proc)
34 | end
35 | end
36 |
37 | test "interpolation: given a key resolves to a Proc that returns a string then interpolation still works" do
38 | proc = lambda { |*args| "%{foo}: " + I18n::Tests::Procs.filter_args(*args) }
39 | assert_equal %|foo: [nil, #{{:foo=>"foo"}}]|, I18n.t(nil, :default => proc, :foo => 'foo')
40 | end
41 |
42 | test "pluralization: given a key resolves to a Proc that returns valid data then pluralization still works" do
43 | proc = lambda { |*args| { :zero => 'zero', :one => 'one', :other => 'other' } }
44 | assert_equal 'zero', I18n.t(:default => proc, :count => 0)
45 | assert_equal 'one', I18n.t(:default => proc, :count => 1)
46 | assert_equal 'other', I18n.t(:default => proc, :count => 2)
47 | end
48 |
49 | test "lookup: given the option :resolve => false was passed it does not resolve proc translations" do
50 | I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| I18n::Tests::Procs.filter_args(*args) })
51 | assert_equal Proc, I18n.t(:a_lambda, :resolve => false).class
52 | end
53 |
54 | test "lookup: given the option :resolve => false was passed it does not resolve proc default" do
55 | assert_equal Proc, I18n.t(nil, :default => lambda { |*args| I18n::Tests::Procs.filter_args(*args) }, :resolve => false).class
56 | end
57 |
58 |
59 | def self.filter_args(*args)
60 | args.map do |arg|
61 | if arg.is_a?(Hash)
62 | arg.delete(:fallback_in_progress)
63 | arg.delete(:fallback_original_locale)
64 | arg.delete(:skip_interpolation)
65 | end
66 | arg
67 | end.inspect
68 | end
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/lib/i18n/utils.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module I18n
4 | module Utils
5 | class << self
6 | if Hash.method_defined?(:except)
7 | def except(hash, *keys)
8 | hash.except(*keys)
9 | end
10 | else
11 | def except(hash, *keys)
12 | hash = hash.dup
13 | keys.each { |k| hash.delete(k) }
14 | hash
15 | end
16 | end
17 |
18 | def deep_merge(hash, other_hash, &block)
19 | deep_merge!(hash.dup, other_hash, &block)
20 | end
21 |
22 | def deep_merge!(hash, other_hash, &block)
23 | hash.merge!(other_hash) do |key, this_val, other_val|
24 | if this_val.is_a?(Hash) && other_val.is_a?(Hash)
25 | deep_merge(this_val, other_val, &block)
26 | elsif block_given?
27 | yield key, this_val, other_val
28 | else
29 | other_val
30 | end
31 | end
32 | end
33 |
34 | def deep_symbolize_keys(hash)
35 | hash.each_with_object({}) do |(key, value), result|
36 | result[key.respond_to?(:to_sym) ? key.to_sym : key] = deep_symbolize_keys_in_object(value)
37 | result
38 | end
39 | end
40 |
41 | private
42 |
43 | def deep_symbolize_keys_in_object(value)
44 | case value
45 | when Hash
46 | deep_symbolize_keys(value)
47 | when Array
48 | value.map { |e| deep_symbolize_keys_in_object(e) }
49 | else
50 | value
51 | end
52 | end
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/lib/i18n/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module I18n
4 | VERSION = "1.14.7"
5 | end
6 |
--------------------------------------------------------------------------------
/test/api/all_features_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | begin
4 | require 'rubygems'
5 | require 'active_support'
6 | rescue LoadError
7 | puts "not testing with Cache enabled because active_support can not be found"
8 | end
9 |
10 | class I18nAllFeaturesApiTest < I18n::TestCase
11 | class Backend < I18n::Backend::Simple
12 | include I18n::Backend::Metadata
13 | include I18n::Backend::Cache
14 | include I18n::Backend::Cascade
15 | include I18n::Backend::Fallbacks
16 | include I18n::Backend::Pluralization
17 | include I18n::Backend::Memoize
18 | end
19 |
20 | def setup
21 | I18n.backend = I18n::Backend::Chain.new(Backend.new, I18n::Backend::Simple.new)
22 | I18n.cache_store = cache_store
23 | super
24 | end
25 |
26 | def teardown
27 | I18n.cache_store.clear if I18n.cache_store
28 | I18n.cache_store = nil
29 | super
30 | end
31 |
32 | def cache_store
33 | ActiveSupport::Cache.lookup_store(:memory_store) if cache_available?
34 | end
35 |
36 | def cache_available?
37 | defined?(ActiveSupport) && defined?(ActiveSupport::Cache)
38 | end
39 |
40 | include I18n::Tests::Basics
41 | include I18n::Tests::Defaults
42 | include I18n::Tests::Interpolation
43 | include I18n::Tests::Link
44 | include I18n::Tests::Lookup
45 | include I18n::Tests::Pluralization
46 | include I18n::Tests::Procs
47 | include I18n::Tests::Localization::Date
48 | include I18n::Tests::Localization::DateTime
49 | include I18n::Tests::Localization::Time
50 | include I18n::Tests::Localization::Procs
51 |
52 | test "make sure we use a Chain backend with an all features backend" do
53 | assert_equal I18n::Backend::Chain, I18n.backend.class
54 | assert_equal Backend, I18n.backend.backends.first.class
55 | end
56 |
57 | # links: test that keys stored on one backend can link to keys stored on another backend
58 | end
59 |
--------------------------------------------------------------------------------
/test/api/cascade_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nCascadeApiTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Cascade
6 | end
7 |
8 | def setup
9 | I18n.backend = Backend.new
10 | super
11 | end
12 |
13 | include I18n::Tests::Basics
14 | include I18n::Tests::Defaults
15 | include I18n::Tests::Interpolation
16 | include I18n::Tests::Link
17 | include I18n::Tests::Lookup
18 | include I18n::Tests::Pluralization
19 | include I18n::Tests::Procs
20 | include I18n::Tests::Localization::Date
21 | include I18n::Tests::Localization::DateTime
22 | include I18n::Tests::Localization::Time
23 | include I18n::Tests::Localization::Procs
24 |
25 | test "make sure we use a backend with Cascade included" do
26 | assert_equal Backend, I18n.backend.class
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/api/chain_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nApiChainTest < I18n::TestCase
4 | def setup
5 | super
6 | I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, I18n.backend)
7 | end
8 |
9 | include I18n::Tests::Basics
10 | include I18n::Tests::Defaults
11 | include I18n::Tests::Interpolation
12 | include I18n::Tests::Link
13 | include I18n::Tests::Lookup
14 | include I18n::Tests::Pluralization
15 | include I18n::Tests::Procs
16 | include I18n::Tests::Localization::Date
17 | include I18n::Tests::Localization::DateTime
18 | include I18n::Tests::Localization::Time
19 | include I18n::Tests::Localization::Procs
20 |
21 | test "make sure we use the Chain backend" do
22 | assert_equal I18n::Backend::Chain, I18n.backend.class
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/api/fallbacks_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nFallbacksApiTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Fallbacks
6 | end
7 |
8 | def setup
9 | I18n.backend = Backend.new
10 | super
11 | end
12 |
13 | include I18n::Tests::Basics
14 | include I18n::Tests::Defaults
15 | include I18n::Tests::Interpolation
16 | include I18n::Tests::Link
17 | include I18n::Tests::Lookup
18 | include I18n::Tests::Pluralization
19 | include I18n::Tests::Procs
20 | include I18n::Tests::Localization::Date
21 | include I18n::Tests::Localization::DateTime
22 | include I18n::Tests::Localization::Time
23 | include I18n::Tests::Localization::Procs
24 |
25 | test "make sure we use a backend with Fallbacks included" do
26 | assert_equal Backend, I18n.backend.class
27 | end
28 |
29 | # links: test that keys stored on one backend can link to keys stored on another backend
30 | end
31 |
--------------------------------------------------------------------------------
/test/api/key_value_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nKeyValueApiTest < I18n::TestCase
4 | include I18n::Tests::Basics
5 | include I18n::Tests::Defaults
6 | include I18n::Tests::Interpolation
7 | include I18n::Tests::Link
8 | include I18n::Tests::Lookup
9 | include I18n::Tests::Pluralization
10 | # include Tests::Api::Procs
11 | include I18n::Tests::Localization::Date
12 | include I18n::Tests::Localization::DateTime
13 | include I18n::Tests::Localization::Time
14 | # include Tests::Api::Localization::Procs
15 |
16 | def setup
17 | I18n.backend = I18n::Backend::KeyValue.new({})
18 | super
19 | end
20 |
21 | test "make sure we use the KeyValue backend" do
22 | assert_equal I18n::Backend::KeyValue, I18n.backend.class
23 | end
24 | end if I18n::TestCase.key_value?
25 |
--------------------------------------------------------------------------------
/test/api/lazy_loadable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nLazyLoadableBackendApiTest < I18n::TestCase
4 | def setup
5 | I18n.backend = I18n::Backend::LazyLoadable.new
6 | super
7 | end
8 |
9 | include I18n::Tests::Basics
10 | include I18n::Tests::Defaults
11 | include I18n::Tests::Interpolation
12 | include I18n::Tests::Link
13 | include I18n::Tests::Lookup
14 | include I18n::Tests::Pluralization
15 | include I18n::Tests::Procs
16 | include I18n::Tests::Localization::Date
17 | include I18n::Tests::Localization::DateTime
18 | include I18n::Tests::Localization::Time
19 | include I18n::Tests::Localization::Procs
20 |
21 | test "make sure we use the LazyLoadable backend" do
22 | assert_equal I18n::Backend::LazyLoadable, I18n.backend.class
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/api/memoize_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nMemoizeBackendWithSimpleApiTest < I18n::TestCase
4 | include I18n::Tests::Basics
5 | include I18n::Tests::Defaults
6 | include I18n::Tests::Interpolation
7 | include I18n::Tests::Link
8 | include I18n::Tests::Lookup
9 | include I18n::Tests::Pluralization
10 | include I18n::Tests::Procs
11 | include I18n::Tests::Localization::Date
12 | include I18n::Tests::Localization::DateTime
13 | include I18n::Tests::Localization::Time
14 | include I18n::Tests::Localization::Procs
15 |
16 | class MemoizeBackend < I18n::Backend::Simple
17 | include I18n::Backend::Memoize
18 | end
19 |
20 | def setup
21 | I18n.backend = MemoizeBackend.new
22 | super
23 | end
24 |
25 | test "make sure we use the MemoizeBackend backend" do
26 | assert_equal MemoizeBackend, I18n.backend.class
27 | end
28 | end
29 |
30 | class I18nMemoizeBackendWithKeyValueApiTest < I18n::TestCase
31 | include I18n::Tests::Basics
32 | include I18n::Tests::Defaults
33 | include I18n::Tests::Interpolation
34 | include I18n::Tests::Link
35 | include I18n::Tests::Lookup
36 | include I18n::Tests::Pluralization
37 | include I18n::Tests::Localization::Date
38 | include I18n::Tests::Localization::DateTime
39 | include I18n::Tests::Localization::Time
40 |
41 | # include I18n::Tests::Procs
42 | # include I18n::Tests::Localization::Procs
43 |
44 | class MemoizeBackend < I18n::Backend::KeyValue
45 | include I18n::Backend::Memoize
46 | end
47 |
48 | def setup
49 | I18n.backend = MemoizeBackend.new({})
50 | super
51 | end
52 |
53 | test "make sure we use the MemoizeBackend backend" do
54 | assert_equal MemoizeBackend, I18n.backend.class
55 | end
56 | end if I18n::TestCase.key_value?
57 |
--------------------------------------------------------------------------------
/test/api/override_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nOverrideTest < I18n::TestCase
4 | module OverrideInverse
5 | def translate(key, **options)
6 | super(key, **options).reverse
7 | end
8 | alias :t :translate
9 | end
10 |
11 | module OverrideSignature
12 | def translate(*args)
13 | args.first + args[1]
14 | end
15 | alias :t :translate
16 | end
17 |
18 | def setup
19 | super
20 | @I18n = I18n.dup
21 | @I18n.backend = I18n::Backend::Simple.new
22 | end
23 |
24 | test "make sure modules can overwrite I18n methods" do
25 | @I18n.extend OverrideInverse
26 | @I18n.backend.store_translations('en', :foo => 'bar')
27 |
28 | assert_equal 'rab', @I18n.translate(:foo, :locale => 'en')
29 | assert_equal 'rab', @I18n.t(:foo, :locale => 'en')
30 | assert_equal 'rab', @I18n.translate!(:foo, :locale => 'en')
31 | assert_equal 'rab', @I18n.t!(:foo, :locale => 'en')
32 | end
33 |
34 | test "make sure modules can overwrite I18n signature" do
35 | exception = catch(:exception) do
36 | @I18n.t('Hello', :tokenize => true, :throw => true)
37 | end
38 | assert exception.message
39 | @I18n.extend OverrideSignature
40 | assert_equal 'HelloWelcome message on home page', @I18n.translate('Hello', 'Welcome message on home page', :tokenize => true) # tr8n example
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/test/api/pluralization_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nPluralizationApiTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Pluralization
6 | end
7 |
8 | def setup
9 | I18n.backend = Backend.new
10 | super
11 | end
12 |
13 | include I18n::Tests::Basics
14 | include I18n::Tests::Defaults
15 | include I18n::Tests::Interpolation
16 | include I18n::Tests::Link
17 | include I18n::Tests::Lookup
18 | include I18n::Tests::Pluralization
19 | include I18n::Tests::Procs
20 | include I18n::Tests::Localization::Date
21 | include I18n::Tests::Localization::DateTime
22 | include I18n::Tests::Localization::Time
23 | include I18n::Tests::Localization::Procs
24 |
25 | test "make sure we use a backend with Pluralization included" do
26 | assert_equal Backend, I18n.backend.class
27 | end
28 |
29 | # links: test that keys stored on one backend can link to keys stored on another backend
30 | end
31 |
--------------------------------------------------------------------------------
/test/api/simple_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nSimpleBackendApiTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Pluralization
6 | end
7 |
8 | def setup
9 | I18n.backend = I18n::Backend::Simple.new
10 | super
11 | end
12 |
13 | include I18n::Tests::Basics
14 | include I18n::Tests::Defaults
15 | include I18n::Tests::Interpolation
16 | include I18n::Tests::Link
17 | include I18n::Tests::Lookup
18 | include I18n::Tests::Pluralization
19 | include I18n::Tests::Procs
20 | include I18n::Tests::Localization::Date
21 | include I18n::Tests::Localization::DateTime
22 | include I18n::Tests::Localization::Time
23 | include I18n::Tests::Localization::Procs
24 |
25 | test "make sure we use the Simple backend" do
26 | assert_equal I18n::Backend::Simple, I18n.backend.class
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/backend/cache_file_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 | require 'fileutils'
3 | require 'tempfile'
4 |
5 | module CountWrites
6 | attr_reader :writes
7 |
8 | def initialize(*args)
9 | super
10 | @writes = []
11 | end
12 |
13 | def store_translations(*args)
14 | super.tap { @writes << args }
15 | end
16 | end
17 |
18 | module CacheFileTest
19 | test 'load_translations caches loaded file contents' do
20 | setup_backend!
21 | I18n.load_path = [locales_dir + '/en.yml']
22 | assert_equal 0, @backend.writes.count
23 |
24 | @backend.load_translations unless @backend.is_a?(I18n::Backend::Simple)
25 | assert_equal('baz', I18n.t('foo.bar'))
26 | assert_equal 2, @backend.writes.count
27 |
28 | @backend.load_translations
29 | assert_equal('baz', I18n.t('foo.bar'))
30 | assert_equal 2, @backend.writes.count
31 | end
32 |
33 | test 'setting path_roots normalizes write key' do
34 | setup_backend!
35 | I18n.load_path = [locales_dir + '/en.yml']
36 | @backend.path_roots = [locales_dir]
37 | @backend.load_translations
38 | refute_nil I18n.t("0/en\x01yml", scope: :load_file, locale: :i18n, default: nil)
39 | end
40 |
41 | test 'load_translations caches file through updated modification time' do
42 | setup_backend!
43 | Tempfile.open(['test', '.yml']) do |file|
44 | I18n.load_path = [file.path]
45 |
46 | File.write(file, { :en => { :foo => { :bar => 'baz' } } }.to_yaml)
47 | assert_equal 0, @backend.writes.count
48 |
49 | @backend.load_translations unless @backend.is_a?(I18n::Backend::Simple)
50 | assert_equal('baz', I18n.t('foo.bar'))
51 | assert_equal 2, @backend.writes.count
52 |
53 | FileUtils.touch(file, :mtime => Time.now + 1)
54 | @backend.load_translations
55 | assert_equal('baz', I18n.t('foo.bar'))
56 | assert_equal 2, @backend.writes.count
57 |
58 | File.write(file, { :en => { :foo => { :bar => 'baa' } } }.to_yaml)
59 | FileUtils.touch(file, :mtime => Time.now + 1)
60 | @backend.load_translations
61 | assert_equal('baa', I18n.t('foo.bar'))
62 | assert_equal 4, @backend.writes.count
63 | end
64 | end
65 | end
66 |
67 | class SimpleCacheFileTest < I18n::TestCase
68 | include CacheFileTest
69 |
70 | class Backend < I18n::Backend::Simple
71 | include CountWrites
72 | include I18n::Backend::CacheFile
73 | end
74 |
75 | def setup_backend!
76 | @backend = I18n.backend = Backend.new
77 | end
78 | end
79 |
80 | class KeyValueCacheFileTest < I18n::TestCase
81 | include CacheFileTest
82 |
83 | class Backend < I18n::Backend::KeyValue
84 | include CountWrites
85 | include I18n::Backend::CacheFile
86 | def initialize
87 | super({})
88 | end
89 | end
90 |
91 | def setup_backend!
92 | @backend = I18n.backend = Backend.new
93 | end
94 | end if I18n::TestCase.key_value?
95 |
--------------------------------------------------------------------------------
/test/backend/cache_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 | require 'openssl'
3 |
4 | begin
5 | require 'active_support'
6 | rescue LoadError
7 | $stderr.puts "Skipping cache tests using ActiveSupport"
8 | else
9 |
10 | class I18nBackendCacheTest < I18n::TestCase
11 | class Backend < I18n::Backend::Simple
12 | include I18n::Backend::Cache
13 | end
14 |
15 | def setup
16 | I18n.backend = Backend.new
17 | super
18 | I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
19 | I18n.cache_store.clear
20 | I18n.cache_key_digest = nil
21 | end
22 |
23 | def teardown
24 | super
25 | I18n.cache_store.clear
26 | I18n.cache_store = nil
27 | end
28 |
29 | test "it uses the cache" do
30 | assert I18n.cache_store.is_a?(ActiveSupport::Cache::MemoryStore)
31 | end
32 |
33 | test "translate hits the backend and caches the response" do
34 | I18n.backend.expects(:lookup).returns('Foo')
35 | assert_equal 'Foo', I18n.t(:foo)
36 |
37 | I18n.backend.expects(:lookup).never
38 | assert_equal 'Foo', I18n.t(:foo)
39 |
40 | I18n.backend.expects(:lookup).returns('Bar')
41 | assert_equal 'Bar', I18n.t(:bar)
42 | end
43 |
44 | test "translate returns a cached false response" do
45 | I18n.backend.expects(:lookup).never
46 | I18n.cache_store.expects(:read).returns(false)
47 | assert_equal false, I18n.t(:foo)
48 | end
49 |
50 | test "still raises MissingTranslationData but also caches it" do
51 | assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
52 | assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
53 | assert_equal 1, I18n.cache_store.instance_variable_get(:@data).size
54 |
55 | # I18n.backend.expects(:lookup).returns(nil)
56 | # assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
57 | # I18n.backend.expects(:lookup).never
58 | # assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
59 | end
60 |
61 | test "MissingTranslationData does not cache custom options" do
62 | I18n.t(:missing, :scope => :foo, :extra => true)
63 | assert_equal 1, I18n.cache_store.instance_variable_get(:@data).size
64 |
65 | value = I18n.cache_store.read(I18n.cache_store.instance_variable_get(:@data).keys.first)
66 |
67 | assert_equal({ scope: :foo }, value.options)
68 | end
69 |
70 | test "uses 'i18n' as a cache key namespace by default" do
71 | assert_equal 0, I18n.backend.send(:cache_key, :en, :foo, {}).index('i18n')
72 | end
73 |
74 | test "adds a custom cache key namespace" do
75 | with_cache_namespace('bar') do
76 | assert_equal 0, I18n.backend.send(:cache_key, :en, :foo, {}).index('i18n/bar/')
77 | end
78 | end
79 |
80 | test "adds locale and hash of key and hash of options" do
81 | options = { :bar => 1 }
82 | assert_equal "i18n//en/#{:foo.to_s.hash}/#{options.to_s.hash}", I18n.backend.send(:cache_key, :en, :foo, options)
83 | end
84 |
85 | test "cache_key uses configured digest method" do
86 | digest = OpenSSL::Digest::SHA256.new
87 | options = { :bar => 1 }
88 | options_hash = options.inspect
89 | with_cache_key_digest(digest) do
90 | assert_equal "i18n//en/#{digest.hexdigest(:foo.to_s)}/#{digest.hexdigest(options_hash)}", I18n.backend.send(:cache_key, :en, :foo, options)
91 | end
92 | end
93 |
94 | test "keys should not be equal" do
95 | interpolation_values1 = { :foo => 1, :bar => 2 }
96 | interpolation_values2 = { :foo => 2, :bar => 1 }
97 |
98 | key1 = I18n.backend.send(:cache_key, :en, :some_key, interpolation_values1)
99 | key2 = I18n.backend.send(:cache_key, :en, :some_key, interpolation_values2)
100 |
101 | assert key1 != key2
102 | end
103 |
104 | protected
105 |
106 | def with_cache_namespace(namespace)
107 | I18n.cache_namespace = namespace
108 | yield
109 | I18n.cache_namespace = nil
110 | end
111 |
112 | def with_cache_key_digest(digest)
113 | I18n.cache_key_digest = digest
114 | yield
115 | I18n.cache_key_digest = nil
116 | end
117 | end
118 |
119 | end # AS cache check
120 |
--------------------------------------------------------------------------------
/test/backend/cascade_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendCascadeTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Cascade
6 | end
7 |
8 | def setup
9 | super
10 | I18n.backend = Backend.new
11 | store_translations(:en, :foo => 'foo', :bar => { :baz => 'baz' })
12 | @cascade_options = { :step => 1, :offset => 1, :skip_root => false }
13 | end
14 |
15 | def lookup(key, options = {})
16 | I18n.t(key, **options.merge(:cascade => @cascade_options))
17 | end
18 |
19 | test "still returns an existing translation as usual" do
20 | assert_equal 'foo', lookup(:foo)
21 | assert_equal 'baz', lookup(:'bar.baz')
22 | end
23 |
24 | test "falls back by cutting keys off the end of the scope" do
25 | assert_equal 'foo', lookup(:foo, :scope => :'missing')
26 | assert_equal 'foo', lookup(:foo, :scope => :'missing.missing')
27 | assert_equal 'baz', lookup(:baz, :scope => :'bar.missing')
28 | assert_equal 'baz', lookup(:baz, :scope => :'bar.missing.missing')
29 | end
30 |
31 | test "raises I18n::MissingTranslationData exception when no translation was found" do
32 | assert_raises(I18n::MissingTranslationData) { lookup(:'foo.missing', :raise => true) }
33 | assert_raises(I18n::MissingTranslationData) { lookup(:'bar.baz.missing', :raise => true) }
34 | assert_raises(I18n::MissingTranslationData) { lookup(:'missing.bar.baz', :raise => true) }
35 | end
36 |
37 | test "cascades before evaluating the default" do
38 | assert_equal 'foo', lookup(:foo, :scope => :missing, :default => 'default')
39 | end
40 |
41 | test "cascades defaults, too" do
42 | assert_equal 'foo', lookup(nil, :default => [:'missing.missing', :'missing.foo'])
43 | end
44 |
45 | test "works with :offset => 2 and a single key" do
46 | @cascade_options[:offset] = 2
47 | lookup(:foo)
48 | end
49 |
50 | test "assemble required fallbacks for ActiveRecord validation messages" do
51 | store_translations(:en,
52 | :errors => {
53 | :odd => 'errors.odd',
54 | :reply => { :title => { :blank => 'errors.reply.title.blank' }, :taken => 'errors.reply.taken' },
55 | :topic => { :title => { :format => 'errors.topic.title.format' }, :length => 'errors.topic.length' }
56 | }
57 | )
58 | assert_equal 'errors.reply.title.blank', lookup(:'errors.reply.title.blank', :default => :'errors.topic.title.blank')
59 | assert_equal 'errors.reply.taken', lookup(:'errors.reply.title.taken', :default => :'errors.topic.title.taken')
60 | assert_equal 'errors.topic.title.format', lookup(:'errors.reply.title.format', :default => :'errors.topic.title.format')
61 | assert_equal 'errors.topic.length', lookup(:'errors.reply.title.length', :default => :'errors.topic.title.length')
62 | assert_equal 'errors.odd', lookup(:'errors.reply.title.odd', :default => :'errors.topic.title.odd')
63 | end
64 |
65 | test "assemble action view translation helper lookup cascade" do
66 | @cascade_options[:offset] = 2
67 |
68 | store_translations(:en,
69 | :menu => { :show => 'menu.show' },
70 | :namespace => {
71 | :menu => { :new => 'namespace.menu.new' },
72 | :controller => {
73 | :menu => { :edit => 'namespace.controller.menu.edit' },
74 | :action => {
75 | :menu => { :destroy => 'namespace.controller.action.menu.destroy' }
76 | }
77 | }
78 | }
79 | )
80 |
81 | assert_equal 'menu.show', lookup(:'namespace.controller.action.menu.show')
82 | assert_equal 'namespace.menu.new', lookup(:'namespace.controller.action.menu.new')
83 | assert_equal 'namespace.controller.menu.edit', lookup(:'namespace.controller.action.menu.edit')
84 | assert_equal 'namespace.controller.action.menu.destroy', lookup(:'namespace.controller.action.menu.destroy')
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/test/backend/exceptions_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendExceptionsTest < I18n::TestCase
4 | def setup
5 | super
6 | I18n.backend = I18n::Backend::Simple.new
7 | end
8 |
9 | test "throw message: MissingTranslation message from #translate includes the given scope and full key" do
10 | exception = catch(:exception) do
11 | I18n.t(:'baz.missing', :scope => :'foo.bar', :throw => true)
12 | end
13 | assert_equal "Translation missing: en.foo.bar.baz.missing", exception.message
14 | end
15 |
16 | test "exceptions: MissingTranslationData message from #translate includes the given scope and full key" do
17 | begin
18 | I18n.t(:'baz.missing', :scope => :'foo.bar', :raise => true)
19 | rescue I18n::MissingTranslationData => exception
20 | end
21 | assert_equal "Translation missing: en.foo.bar.baz.missing", exception.message
22 | end
23 |
24 | test "exceptions: MissingTranslationData message from #localize includes the given scope and full key" do
25 | begin
26 | I18n.l(Time.now, :format => :foo)
27 | rescue I18n::MissingTranslationData => exception
28 | end
29 | assert_equal "Translation missing: en.time.formats.foo", exception.message
30 | end
31 |
32 | test "exceptions: MissingInterpolationArgument message includes missing key, provided keys and full string" do
33 | exception = I18n::MissingInterpolationArgument.new('key', {:this => 'was given'}, 'string')
34 | assert_equal %|missing interpolation argument "key" in "string" (#{{:this=>"was given"}} given)|, exception.message
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/test/backend/interpolation_compiler_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class InterpolationCompilerTest < I18n::TestCase
4 | Compiler = I18n::Backend::InterpolationCompiler::Compiler
5 |
6 | def compile_and_interpolate(str, values = {})
7 | Compiler.compile_if_an_interpolation(str).i18n_interpolate(values)
8 | end
9 |
10 | def assert_escapes_interpolation_key(expected, malicious_str)
11 | assert_equal(expected, Compiler.send(:escape_key_sym, malicious_str))
12 | end
13 |
14 | def test_escape_key_properly_escapes
15 | assert_escapes_interpolation_key ':"\""', '"'
16 | assert_escapes_interpolation_key ':"\\\\"', '\\'
17 | assert_escapes_interpolation_key ':"\\\\\""', '\\"'
18 | assert_escapes_interpolation_key ':"\#{}"', '#{}'
19 | assert_escapes_interpolation_key ':"\\\\\#{}"', '\#{}'
20 | end
21 |
22 | def assert_escapes_plain_string(expected, plain_str)
23 | assert_equal expected, Compiler.send(:escape_plain_str, plain_str)
24 | end
25 |
26 | def test_escape_plain_string_properly_escapes
27 | assert_escapes_plain_string '\\"', '"'
28 | assert_escapes_plain_string '\'', '\''
29 | assert_escapes_plain_string '\\#', '#'
30 | assert_escapes_plain_string '\\#{}', '#{}'
31 | assert_escapes_plain_string '\\\\\\"','\\"'
32 | end
33 |
34 | def test_non_interpolated_strings_or_arrays_dont_get_compiled
35 | ['abc', '\\{a}}', '{a}}', []].each do |obj|
36 | Compiler.compile_if_an_interpolation(obj)
37 | assert_equal false, obj.respond_to?(:i18n_interpolate)
38 | end
39 | end
40 |
41 | def test_interpolated_string_gets_compiled
42 | assert_equal '-A-', compile_and_interpolate('-%{a}-', :a => 'A')
43 | end
44 |
45 | def assert_handles_key(str, key)
46 | assert_equal 'A', compile_and_interpolate(str, key => 'A')
47 | end
48 |
49 | def test_compiles_fancy_keys
50 | assert_handles_key('%{\}', :'\\' )
51 | assert_handles_key('%{#}', :'#' )
52 | assert_handles_key('%{#{}', :'#{' )
53 | assert_handles_key('%{#$SAFE}', :'#$SAFE')
54 | assert_handles_key('%{\000}', :'\000' )
55 | assert_handles_key('%{\'}', :'\'' )
56 | assert_handles_key('%{\'\'}', :'\'\'' )
57 | assert_handles_key('%{a.b}', :'a.b' )
58 | assert_handles_key('%{ }', :' ' )
59 | assert_handles_key('%{:}', :':' )
60 | assert_handles_key("%{:''}", :":''" )
61 | assert_handles_key('%{:"}', :':"' )
62 | end
63 |
64 | def test_str_containing_only_escaped_interpolation_is_handled_correctly
65 | assert_equal 'abc %{x}', compile_and_interpolate('abc %%{x}')
66 | end
67 |
68 | def test_handles_weird_strings
69 | assert_equal '#{} a', compile_and_interpolate('#{} %{a}', :a => 'a')
70 | assert_equal '"#{abc}"', compile_and_interpolate('"#{ab%{a}c}"', :a => '' )
71 | assert_equal 'a}', compile_and_interpolate('%{{a}}', :'{a' => 'a')
72 | assert_equal '"', compile_and_interpolate('"%{a}', :a => '' )
73 | assert_equal 'a%{a}', compile_and_interpolate('%{a}%%{a}', :a => 'a')
74 | assert_equal '%%{a}', compile_and_interpolate('%%%{a}')
75 | assert_equal '\";eval("a")', compile_and_interpolate('\";eval("%{a}")', :a => 'a')
76 | assert_equal '\";eval("a")', compile_and_interpolate('\";eval("a")%{a}', :a => '' )
77 | assert_equal "\na", compile_and_interpolate("\n%{a}", :a => 'a')
78 | end
79 |
80 | def test_raises_exception_when_argument_is_missing
81 | assert_raises(I18n::MissingInterpolationArgument) do
82 | compile_and_interpolate('%{first} %{last}', :first => 'first')
83 | end
84 | end
85 |
86 | def test_custom_missing_interpolation_argument_handler
87 | old_handler = I18n.config.missing_interpolation_argument_handler
88 | I18n.config.missing_interpolation_argument_handler = lambda do |key, values, string|
89 | "missing key is #{key}, values are #{values.inspect}, given string is '#{string}'"
90 | end
91 | assert_equal %|first missing key is last, values are #{{:first=>"first"}.to_s}, given string is '%{first} %{last}'|,
92 | compile_and_interpolate('%{first} %{last}', :first => 'first')
93 | ensure
94 | I18n.config.missing_interpolation_argument_handler = old_handler
95 | end
96 | end
97 |
98 | class I18nBackendInterpolationCompilerTest < I18n::TestCase
99 | class Backend < I18n::Backend::Simple
100 | include I18n::Backend::InterpolationCompiler
101 | end
102 |
103 | include I18n::Tests::Interpolation
104 |
105 | def setup
106 | I18n.backend = Backend.new
107 | super
108 | end
109 |
110 | # pre-compile default strings to make sure we are testing I18n::Backend::InterpolationCompiler
111 | def interpolate(*args)
112 | options = args.last.kind_of?(Hash) ? args.last : {}
113 | if default_str = options[:default]
114 | I18n::Backend::InterpolationCompiler::Compiler.compile_if_an_interpolation(default_str)
115 | end
116 | super
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/test/backend/key_value_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendKeyValueTest < I18n::TestCase
4 | def setup_backend!(subtree=true)
5 | I18n.backend = I18n::Backend::KeyValue.new({}, subtree)
6 | store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' })
7 | end
8 |
9 | def assert_flattens(expected, nested, escape=true, subtree=true)
10 | assert_equal expected, I18n.backend.flatten_translations("en", nested, escape, subtree)
11 | end
12 |
13 | test "hash flattening works" do
14 | setup_backend!
15 | assert_flattens(
16 | {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}, :"b.f" => {:x=>"x"}, :"b.c"=>"c", :"b.f.x"=>"x", :"b.d"=>"d"},
17 | {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}}
18 | )
19 | assert_flattens({:a=>{:b =>['a', 'b']}, :"a.b"=>['a', 'b']}, {:a=>{:b =>['a', 'b']}})
20 | assert_flattens({:"a\001b" => "c"}, {:"a.b" => "c"})
21 | assert_flattens({:"a.b"=>['a', 'b']}, {:a=>{:b =>['a', 'b']}}, true, false)
22 | assert_flattens({:"a.b" => "c"}, {:"a.b" => "c"}, false)
23 | end
24 |
25 | test "store_translations supports numeric keys" do
26 | setup_backend!
27 | store_translations(:en, 1 => 'foo')
28 | assert_equal 'foo', I18n.t('1')
29 | assert_equal 'foo', I18n.t(1)
30 | assert_equal 'foo', I18n.t(:'1')
31 | end
32 |
33 | test "store_translations handle subtrees by default" do
34 | setup_backend!
35 | assert_equal({ :bar => 'bar', :baz => 'baz' }, I18n.t("foo"))
36 | end
37 |
38 | test "store_translations merge subtrees accordingly" do
39 | setup_backend!
40 | store_translations(:en, :foo => { :baz => "BAZ"})
41 | assert_equal('BAZ', I18n.t("foo.baz"))
42 | assert_equal({ :bar => 'bar', :baz => 'BAZ' }, I18n.t("foo"))
43 | end
44 |
45 | test "store_translations does not handle subtrees if desired" do
46 | setup_backend!(false)
47 | assert_raises I18n::MissingTranslationData do
48 | I18n.t("foo", :raise => true)
49 | end
50 | end
51 |
52 | test 'initialized? checks that a store is available' do
53 | setup_backend!
54 | I18n.backend.reload!
55 | assert_equal I18n.backend.initialized?, true
56 | end
57 |
58 | test 'translations gets the translations from the store' do
59 | setup_backend!
60 | I18n.backend.send(:translations)
61 | expected = { :en => {:foo => { :bar => 'bar', :baz => 'baz' }} }
62 | assert_equal expected, translations
63 | end
64 |
65 | test "subtrees enabled: given incomplete pluralization data it raises I18n::InvalidPluralizationData" do
66 | setup_backend!
67 | store_translations(:en, :bar => { :one => "One" })
68 | assert_raises(I18n::InvalidPluralizationData) { I18n.t(:bar, :count => 2) }
69 | end
70 |
71 | test "subtrees disabled: given incomplete pluralization data it returns an error message" do
72 | setup_backend!(false)
73 | store_translations(:en, :bar => { :one => "One" })
74 | assert_equal "Translation missing: en.bar", I18n.t(:bar, :count => 2)
75 | end
76 |
77 | test "translate handles subtrees for pluralization" do
78 | setup_backend!(false)
79 | store_translations(:en, :bar => { :one => "One" })
80 | assert_equal("One", I18n.t("bar", :count => 1))
81 | end
82 |
83 | test "subtrees enabled: returns localized string given missing pluralization data" do
84 | setup_backend!(true)
85 | assert_equal 'bar', I18n.t("foo.bar", count: 1)
86 | end
87 |
88 | test "subtrees disabled: returns localized string given missing pluralization data" do
89 | setup_backend!(false)
90 | assert_equal 'bar', I18n.t("foo.bar", count: 1)
91 | end
92 |
93 | test "subtrees enabled: Returns fallback default given missing pluralization data" do
94 | setup_backend!(true)
95 | I18n.backend.extend I18n::Backend::Fallbacks
96 | assert_equal 'default', I18n.t(:missing_bar, count: 1, default: 'default')
97 | assert_equal 'default', I18n.t(:missing_bar, count: 0, default: 'default')
98 | end
99 |
100 | test "subtrees disabled: Returns fallback default given missing pluralization data" do
101 | setup_backend!(false)
102 | I18n.backend.extend I18n::Backend::Fallbacks
103 | assert_equal 'default', I18n.t(:missing_bar, count: 1, default: 'default')
104 | assert_equal 'default', I18n.t(:missing_bar, count: 0, default: 'default')
105 | end
106 | end if I18n::TestCase.key_value?
107 |
--------------------------------------------------------------------------------
/test/backend/memoize_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 | require 'backend/simple_test'
3 |
4 | class I18nBackendMemoizeTest < I18nBackendSimpleTest
5 | module MemoizeSpy
6 | attr_accessor :spy_calls
7 |
8 | def available_locales
9 | self.spy_calls = (self.spy_calls || 0) + 1
10 | super
11 | end
12 | end
13 |
14 | class MemoizeBackend < I18n::Backend::Simple
15 | include MemoizeSpy
16 | include I18n::Backend::Memoize
17 | end
18 |
19 | def setup
20 | super
21 | I18n.backend = MemoizeBackend.new
22 | end
23 |
24 | def test_memoizes_available_locales
25 | I18n.backend.spy_calls = 0
26 | assert_equal I18n.available_locales, I18n.available_locales
27 | assert_equal 1, I18n.backend.spy_calls
28 | end
29 |
30 | def test_resets_available_locales_on_reload!
31 | I18n.available_locales
32 | I18n.backend.spy_calls = 0
33 | I18n.reload!
34 | assert_equal I18n.available_locales, I18n.available_locales
35 | assert_equal 1, I18n.backend.spy_calls
36 | end
37 |
38 | def test_resets_available_locales_on_store_translations
39 | I18n.available_locales
40 | I18n.backend.spy_calls = 0
41 | I18n.backend.store_translations(:copa, :ca => :bana)
42 | assert_equal I18n.available_locales, I18n.available_locales
43 | assert I18n.available_locales.include?(:copa)
44 | assert_equal 1, I18n.backend.spy_calls
45 | end
46 |
47 | def test_eager_load
48 | I18n.eager_load!
49 | I18n.backend.spy_calls = 0
50 | assert_equal I18n.available_locales, I18n.available_locales
51 | assert_equal 0, I18n.backend.spy_calls
52 | end
53 |
54 | module TestLookup
55 | def lookup(locale, key, scope = [], options = {})
56 | keys = I18n.normalize_keys(locale, key, scope, options[:separator])
57 | keys.inspect
58 | end
59 | end
60 |
61 | def test_lookup_concurrent_consistency
62 | backend_impl = Class.new(I18n::Backend::Simple) do
63 | include TestLookup
64 | include I18n::Backend::Memoize
65 | end
66 | backend = backend_impl.new
67 |
68 | memoized_lookup = backend.send(:memoized_lookup)
69 |
70 | assert_equal "[:foo, :scoped, :sample]", backend.translate('foo', scope = [:scoped, :sample])
71 |
72 | 30.times.inject([]) do |memo, i|
73 | memo << Thread.new do
74 | backend.translate('bar', scope); backend.translate(:baz, scope)
75 | end
76 | end.each(&:join)
77 |
78 | memoized_lookup = backend.send(:memoized_lookup)
79 | puts memoized_lookup.inspect if $VERBOSE
80 | assert_equal 3, memoized_lookup.size, "NON-THREAD-SAFE lookup memoization backend: #{memoized_lookup.class}"
81 | # if a plain Hash is used might eventually end up in a weird (inconsistent) state
82 | end
83 |
84 | end
85 |
--------------------------------------------------------------------------------
/test/backend/metadata_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendMetadataTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Metadata
6 | end
7 |
8 | def setup
9 | super
10 | I18n.backend = Backend.new
11 | store_translations(:en, :foo => 'Hi %{name}')
12 | end
13 |
14 | test "translation strings carry metadata" do
15 | translation = I18n.t(:foo, :name => 'David')
16 | assert translation.respond_to?(:translation_metadata)
17 | assert translation.translation_metadata.is_a?(Hash)
18 | end
19 |
20 | test "translate adds the locale to metadata on Strings" do
21 | assert_equal :en, I18n.t(:foo, :name => 'David', :locale => :en).translation_metadata[:locale]
22 | end
23 |
24 | test "translate adds the key to metadata on Strings" do
25 | assert_equal :foo, I18n.t(:foo, :name => 'David').translation_metadata[:key]
26 | end
27 |
28 | test "translate adds the default to metadata on Strings" do
29 | assert_equal 'bar', I18n.t(:foo, :default => 'bar', :name => '').translation_metadata[:default]
30 | end
31 |
32 | test "translation adds the interpolation values to metadata on Strings" do
33 | assert_equal({:name => 'David'}, I18n.t(:foo, :name => 'David').translation_metadata[:values])
34 | end
35 |
36 | test "interpolation adds the original string to metadata on Strings" do
37 | assert_equal('Hi %{name}', I18n.t(:foo, :name => 'David').translation_metadata[:original])
38 | end
39 |
40 | test "pluralization adds the count to metadata on Strings" do
41 | assert_equal(1, I18n.t(:missing, :count => 1, :default => { :one => 'foo' }).translation_metadata[:count])
42 | end
43 |
44 | test "metadata works with frozen values" do
45 | assert_equal(1, I18n.t(:missing, :count => 1, :default => 'foo'.freeze).translation_metadata[:count])
46 | end
47 | end
48 |
49 |
--------------------------------------------------------------------------------
/test/backend/pluralization_fallback_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendPluralizationFallbackTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Pluralization
6 | include I18n::Backend::Fallbacks
7 | end
8 |
9 | def setup
10 | super
11 | I18n.default_locale = :'en'
12 | I18n.backend = Backend.new
13 |
14 | store_translations('en', cat: { zero: 'cat', one: 'cat', other: 'cats' })
15 | store_translations('en-US', cat: { zero: 'no cat', one: nil, other: 'lots of cats' })
16 |
17 | store_translations('ru', cat: { one: 'кот', few: 'кошек', many: 'кошка', other: 'кошек' })
18 | # probably not a real locale but just to demonstrate
19 | store_translations('ru-US', cat: { one: nil, few: nil, many: nil, other: nil })
20 | store_translations('ru', i18n: { plural: { rule: russian_rule }})
21 | end
22 |
23 | test "fallbacks: nils are ignored and fallback is applied" do
24 | assert_equal "no cat", I18n.t("cat", count: 0, locale: "en-US")
25 | assert_equal "cat", I18n.t("cat", count: 0, locale: "en")
26 |
27 | assert_equal "cat", I18n.t("cat", count: 1, locale: "en-US")
28 | assert_equal "cat", I18n.t("cat", count: 1, locale: "en")
29 |
30 | assert_equal "lots of cats", I18n.t("cat", count: 2, locale: "en-US")
31 | assert_equal "cats", I18n.t("cat", count: 2, locale: "en")
32 | end
33 |
34 | test "fallbacks: nils are ignored and fallback is applied, with custom rule" do
35 | # more specs: https://github.com/svenfuchs/rails-i18n/blob/master/spec/unit/pluralization/east_slavic.rb
36 | assert_equal "кошка", I18n.t("cat", count: 0, locale: "ru")
37 | assert_equal "кошка", I18n.t("cat", count: 0, locale: "ru-US")
38 |
39 | assert_equal "кот", I18n.t("cat", count: 1, locale: "ru")
40 | assert_equal "кот", I18n.t("cat", count: 1, locale: "ru-US")
41 |
42 | assert_equal "кошек", I18n.t("cat", count: 2, locale: "ru")
43 | assert_equal "кошек", I18n.t("cat", count: 2, locale: "ru-US")
44 |
45 | assert_equal "кошек", I18n.t("cat", count: 1.5, locale: "ru")
46 | assert_equal "кошек", I18n.t("cat", count: 1.5, locale: "ru-US")
47 | end
48 |
49 | private
50 |
51 | # copied from https://github.com/svenfuchs/rails-i18n/blob/master/lib/rails_i18n/common_pluralizations/east_slavic.rb
52 | def russian_rule
53 | lambda do |n|
54 | n ||= 0
55 | mod10 = n % 10
56 | mod100 = n % 100
57 |
58 | if mod10 == 1 && mod100 != 11
59 | :one
60 | elsif (2..4).include?(mod10) && !(12..14).include?(mod100)
61 | :few
62 | elsif mod10 == 0 || (5..9).include?(mod10) || (11..14).include?(mod100)
63 | :many
64 | else
65 | :other
66 | end
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/test/backend/pluralization_scope_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendPluralizationScopeTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Pluralization
6 | include I18n::Backend::Fallbacks
7 | end
8 |
9 | def setup
10 | super
11 | I18n.default_locale = :'en'
12 | I18n.backend = Backend.new
13 |
14 | translations = {
15 | i18n: {
16 | plural: {
17 | keys: [:one, :other],
18 | rule: lambda { |n| n == 1 ? :one : :other },
19 | }
20 | },
21 | activerecord: {
22 | models: {
23 | my_model: {
24 | one: 'one model',
25 | other: 'more models',
26 | some_other_key: {
27 | key: 'value'
28 | }
29 | }
30 | }
31 | }
32 | }
33 |
34 | store_translations('en', translations)
35 | end
36 |
37 | test "pluralization picks :other for 2" do
38 | args = {
39 | scope: [:activerecord, :models],
40 | count: 2,
41 | default: ["My model"]
42 | }
43 | assert_equal 'more models', I18n.translate(:my_model, **args)
44 | end
45 |
46 | test "pluralization picks :one for 1" do
47 | args = {
48 | scope: [:activerecord, :models],
49 | count: 1,
50 | default: ["My model"]
51 | }
52 | assert_equal 'one model', I18n.translate(:my_model, **args)
53 | end
54 |
55 | end
56 |
--------------------------------------------------------------------------------
/test/backend/pluralization_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nBackendPluralizationTest < I18n::TestCase
4 | class Backend < I18n::Backend::Simple
5 | include I18n::Backend::Pluralization
6 | include I18n::Backend::Fallbacks
7 | end
8 |
9 | def setup
10 | super
11 | I18n.backend = Backend.new
12 | @rule = lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : n == 0 || (2..10).include?(n % 100) ? :few : (11..19).include?(n % 100) ? :many : :other }
13 | store_translations(:xx, :i18n => { :plural => { :rule => @rule } })
14 | @entry = { :"0" => 'none', :"1" => 'single', :one => 'one', :few => 'few', :many => 'many', :other => 'other' }
15 | @entry_with_zero = @entry.merge( { :zero => 'zero' } )
16 | end
17 |
18 | test "pluralization picks a pluralizer from :'i18n.pluralize'" do
19 | assert_equal @rule, I18n.backend.send(:pluralizer, :xx)
20 | end
21 |
22 | test "pluralization picks the explicit 1 rule for count == 1, the explicit rule takes priority over the matching :one rule" do
23 | assert_equal 'single', I18n.t(:count => 1, :default => @entry, :locale => :xx)
24 | assert_equal 'single', I18n.t(:count => 1.0, :default => @entry, :locale => :xx)
25 | end
26 |
27 | test "pluralization picks :one for 1, since in this case that is the matching rule for 1 (when there is no explicit 1 rule)" do
28 | @entry.delete(:"1")
29 | assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :xx)
30 | end
31 |
32 | test "pluralization picks :few for 2" do
33 | assert_equal 'few', I18n.t(:count => 2, :default => @entry, :locale => :xx)
34 | end
35 |
36 | test "pluralization picks :many for 11" do
37 | assert_equal 'many', I18n.t(:count => 11, :default => @entry, :locale => :xx)
38 | end
39 |
40 | test "pluralization picks zero for 0 if the key is contained in the data" do
41 | assert_equal 'zero', I18n.t(:count => 0, :default => @entry_with_zero, :locale => :xx)
42 | end
43 |
44 | test "pluralization picks explicit 0 rule for count == 0, since the explicit rule takes priority over the matching :few rule" do
45 | assert_equal 'none', I18n.t(:count => 0, :default => @entry, :locale => :xx)
46 | assert_equal 'none', I18n.t(:count => 0.0, :default => @entry, :locale => :xx)
47 | assert_equal 'none', I18n.t(:count => -0, :default => @entry, :locale => :xx)
48 | end
49 |
50 | test "pluralization picks :few for 0 (when there is no explicit 0 rule)" do
51 | @entry.delete(:"0")
52 | assert_equal 'few', I18n.t(:count => 0, :default => @entry, :locale => :xx)
53 | end
54 |
55 | test "pluralization does Lateral Inheritance to :other to cover missing data" do
56 | @entry.delete(:many)
57 | assert_equal 'other', I18n.t(:count => 11, :default => @entry, :locale => :xx)
58 | end
59 |
60 | test "pluralization picks one for 1 if the entry has attributes hash on unknown locale" do
61 | @entry[:attributes] = { :field => 'field', :second => 'second' }
62 | assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :pirate)
63 | end
64 |
65 | test "Nested keys within pluralization context" do
66 | store_translations(:xx,
67 | :stars => {
68 | one: "%{count} star",
69 | other: "%{count} stars",
70 | special: {
71 | one: "%{count} special star",
72 | other: "%{count} special stars",
73 | }
74 | }
75 | )
76 | assert_equal "1 star", I18n.t('stars', count: 1, :locale => :xx)
77 | assert_equal "20 stars", I18n.t('stars', count: 20, :locale => :xx)
78 | assert_equal "1 special star", I18n.t('stars.special', count: 1, :locale => :xx)
79 | assert_equal "20 special stars", I18n.t('stars.special', count: 20, :locale => :xx)
80 | end
81 |
82 | test "Fallbacks can pick up rules from fallback locales, too" do
83 | assert_equal @rule, I18n.backend.send(:pluralizer, :'xx-XX')
84 | end
85 |
86 | test "linked lookup works with pluralization backend" do
87 | I18n.backend.store_translations(:xx, {
88 | :automobiles => :autos,
89 | :autos => :cars,
90 | :cars => { :porsche => { :one => "I have %{count} Porsche 🚗", :other => "I have %{count} Porsches 🚗" } }
91 | })
92 | assert_equal "I have 1 Porsche 🚗", I18n.t(:'automobiles.porsche', count: 1, :locale => :xx)
93 | assert_equal "I have 20 Porsches 🚗", I18n.t(:'automobiles.porsche', count: 20, :locale => :xx)
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/test/backend/transliterator_test.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | require 'test_helper'
3 |
4 | class I18nBackendTransliterator < I18n::TestCase
5 | def setup
6 | super
7 | I18n.backend = I18n::Backend::Simple.new
8 | @proc = lambda { |n| n.upcase }
9 | @hash = { "ü" => "ue", "ö" => "oe", "a" => "a" }
10 | @transliterator = I18n::Backend::Transliterator.get
11 | end
12 |
13 | test "transliteration rule can be a proc" do
14 | store_translations(:xx, :i18n => {:transliterate => {:rule => @proc}})
15 | assert_equal "HELLO", I18n.backend.transliterate(:xx, "hello")
16 | end
17 |
18 | test "transliteration rule can be a hash" do
19 | store_translations(:xx, :i18n => {:transliterate => {:rule => @hash}})
20 | assert_equal "ue", I18n.backend.transliterate(:xx, "ü")
21 | end
22 |
23 | test "transliteration rule must be a proc or hash" do
24 | store_translations(:xx, :i18n => {:transliterate => {:rule => ""}})
25 | assert_raises I18n::ArgumentError do
26 | I18n.backend.transliterate(:xx, "ü")
27 | end
28 | end
29 |
30 | test "transliterator defaults to latin => ascii when no rule is given" do
31 | assert_equal "AEroskobing", I18n.backend.transliterate(:xx, "Ærøskøbing")
32 | end
33 |
34 | test "default transliterator should not modify ascii characters" do
35 | (0..127).each do |byte|
36 | char = [byte].pack("U")
37 | assert_equal char, @transliterator.transliterate(char)
38 | end
39 | end
40 |
41 | test "default transliterator correctly transliterates latin characters" do
42 | # create string with range of Unicode's western characters with
43 | # diacritics, excluding the division and multiplication signs which for
44 | # some reason or other are floating in the middle of all the letters.
45 | string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include? c}.append(0x1E9E).pack("U*")
46 | string.split(//) do |char|
47 | assert_match %r{^[a-zA-Z']*$}, @transliterator.transliterate(string)
48 | end
49 | end
50 |
51 | test "should replace non-ASCII chars not in map with a replacement char" do
52 | assert_equal "abc?", @transliterator.transliterate("abcſ")
53 | end
54 |
55 | test "can replace non-ASCII chars not in map with a custom replacement string" do
56 | assert_equal "abc#", @transliterator.transliterate("abcſ", "#")
57 | end
58 |
59 | test "default transliterator raises errors for invalid UTF-8" do
60 | assert_raises ArgumentError do
61 | @transliterator.transliterate("a\x92b")
62 | end
63 | end
64 |
65 | test "I18n.transliterate should transliterate using a default transliterator" do
66 | assert_equal "aeo", I18n.transliterate("áèö")
67 | end
68 |
69 | test "I18n.transliterate should transliterate using a locale" do
70 | store_translations(:xx, :i18n => {:transliterate => {:rule => @hash}})
71 | assert_equal "ue", I18n.transliterate("ü", :locale => :xx)
72 | end
73 |
74 | test "default transliterator fails with custom rules with uncomposed input" do
75 | char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS
76 | transliterator = I18n::Backend::Transliterator.get(@hash)
77 | refute_equal "ue", transliterator.transliterate(char)
78 | end
79 |
80 | test "DEFAULT_APPROXIMATIONS is frozen to prevent concurrency issues" do
81 | assert I18n::Backend::Transliterator::HashTransliterator::DEFAULT_APPROXIMATIONS.frozen?
82 | end
83 |
84 | end
85 |
--------------------------------------------------------------------------------
/test/gettext/backend_test.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | require 'test_helper'
4 |
5 | class I18nGettextBackendTest < I18n::TestCase
6 | include I18n::Gettext::Helpers
7 |
8 | class Backend < I18n::Backend::Simple
9 | include I18n::Backend::Gettext
10 | end
11 |
12 | def setup
13 | super
14 | I18n.backend = Backend.new
15 | I18n.locale = :en
16 | I18n.load_path = ["#{locales_dir}/de.po"]
17 | I18n.default_separator = '|'
18 | end
19 |
20 | def test_backend_loads_po_file
21 | I18n.backend.send(:init_translations)
22 | assert I18n.backend.send(:translations)[:de][:"Axis"]
23 | end
24 |
25 | def test_looks_up_a_translation
26 | I18n.locale = :de
27 | assert_equal 'Auto', gettext('car')
28 | end
29 |
30 | def test_uses_default_translation
31 | assert_equal 'car', gettext('car')
32 | end
33 |
34 | def test_looks_up_a_namespaced_translation
35 | I18n.locale = :de
36 | assert_equal 'Räderzahl', sgettext('Car|Wheels count')
37 | assert_equal 'Räderzahl', pgettext('Car', 'Wheels count')
38 | assert_equal 'Räderzahl!', pgettext('New car', 'Wheels count')
39 | end
40 |
41 | def test_uses_namespaced_default_translation
42 | assert_equal 'Wheels count', sgettext('Car|Wheels count')
43 | assert_equal 'Wheels count', pgettext('Car', 'Wheels count')
44 | assert_equal 'Wheels count', pgettext('New car', 'Wheels count')
45 | end
46 |
47 | def test_pluralizes_entry
48 | I18n.locale = :de
49 | assert_equal 'Achse', ngettext('Axis', 'Axis', 1)
50 | assert_equal 'Achsen', ngettext('Axis', 'Axis', 2)
51 | end
52 |
53 | def test_pluralizes_default_entry
54 | assert_equal 'Axis', ngettext('Axis', 'Axis', 1)
55 | assert_equal 'Axis', ngettext('Axis', 'Axis', 2)
56 | end
57 |
58 | def test_pluralizes_namespaced_entry
59 | I18n.locale = :de
60 | assert_equal 'Rad', nsgettext('Car|wheel', 'wheels', 1)
61 | assert_equal 'Räder', nsgettext('Car|wheel', 'wheels', 2)
62 | assert_equal 'Rad', npgettext('Car', 'wheel', 'wheels', 1)
63 | assert_equal 'Räder', npgettext('Car', 'wheel', 'wheels', 2)
64 | assert_equal 'Rad!', npgettext('New car', 'wheel', 'wheels', 1)
65 | assert_equal 'Räder!', npgettext('New car', 'wheel', 'wheels', 2)
66 | end
67 |
68 | def test_pluralizes_namespaced_default_entry
69 | assert_equal 'wheel', nsgettext('Car|wheel', 'wheels', 1)
70 | assert_equal 'wheels', nsgettext('Car|wheel', 'wheels', 2)
71 | assert_equal 'wheel', npgettext('Car', 'wheel', 'wheels', 1)
72 | assert_equal 'wheels', npgettext('Car', 'wheel', 'wheels', 2)
73 | assert_equal 'wheel', npgettext('New car', 'wheel', 'wheels', 1)
74 | assert_equal 'wheels', npgettext('New car', 'wheel', 'wheels', 2)
75 | end
76 |
77 | def test_pluralizes_namespaced_entry_with_alternative_syntax
78 | I18n.locale = :de
79 | assert_equal 'Rad', nsgettext(['Car|wheel', 'wheels'], 1)
80 | assert_equal 'Räder', nsgettext(['Car|wheel', 'wheels'], 2)
81 | assert_equal 'Rad', npgettext('Car', ['wheel', 'wheels'], 1)
82 | assert_equal 'Räder', npgettext('Car', ['wheel', 'wheels'], 2)
83 | assert_equal 'Rad!', npgettext('New car', ['wheel', 'wheels'], 1)
84 | assert_equal 'Räder!', npgettext('New car', ['wheel', 'wheels'], 2)
85 | end
86 |
87 | def test_ngettextpluralizes_entry_with_dots
88 | I18n.locale = :de
89 | assert_equal 'Auf 1 Achse.', n_("On %{count} wheel.", "On %{count} wheels.", 1)
90 | assert_equal 'Auf 2 Achsen.', n_("On %{count} wheel.", "On %{count} wheels.", 2)
91 | end
92 | end
93 |
--------------------------------------------------------------------------------
/test/i18n/exceptions_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nExceptionsTest < I18n::TestCase
4 | def test_invalid_locale_stores_locale
5 | force_invalid_locale
6 | rescue I18n::ArgumentError => exception
7 | assert_nil exception.locale
8 | end
9 |
10 | test "passing an invalid locale raises an InvalidLocale exception" do
11 | force_invalid_locale do |exception|
12 | assert_equal 'nil is not a valid locale', exception.message
13 | end
14 | end
15 |
16 | test "MissingTranslation can be initialized without options" do
17 | exception = I18n::MissingTranslation.new(:en, 'foo')
18 | assert_equal({}, exception.options)
19 | end
20 |
21 | test "MissingTranslationData exception stores locale, key and options" do
22 | force_missing_translation_data do |exception|
23 | assert_equal 'de', exception.locale
24 | assert_equal :foo, exception.key
25 | assert_equal({:scope => :bar}, exception.options)
26 | end
27 | end
28 |
29 | test "MissingTranslationData message contains the locale and scoped key" do
30 | force_missing_translation_data do |exception|
31 | assert_equal 'translation missing: de.bar.foo', exception.message
32 | end
33 | end
34 |
35 | test "InvalidPluralizationData stores entry, count and key" do
36 | force_invalid_pluralization_data do |exception|
37 | assert_equal({:other => "bar"}, exception.entry)
38 | assert_equal 1, exception.count
39 | assert_equal :one, exception.key
40 | end
41 | end
42 |
43 | test "InvalidPluralizationData message contains count, data and missing key" do
44 | force_invalid_pluralization_data do |exception|
45 | assert_match '1', exception.message
46 | assert_match %|#{{:other=>"bar"}}|, exception.message
47 | assert_match 'one', exception.message
48 | end
49 | end
50 |
51 | test "MissingInterpolationArgument stores key and string" do
52 | assert_raises(I18n::MissingInterpolationArgument) { force_missing_interpolation_argument }
53 | force_missing_interpolation_argument do |exception|
54 | assert_equal :bar, exception.key
55 | assert_equal "%{bar}", exception.string
56 | end
57 | end
58 |
59 | test "MissingInterpolationArgument message contains the missing and given arguments" do
60 | force_missing_interpolation_argument do |exception|
61 | assert_equal %|missing interpolation argument :bar in "%{bar}" (#{{:baz=>"baz"}.to_s} given)|, exception.message
62 | end
63 | end
64 |
65 | test "ReservedInterpolationKey stores key and string" do
66 | force_reserved_interpolation_key do |exception|
67 | assert_equal :scope, exception.key
68 | assert_equal "%{scope}", exception.string
69 | end
70 | end
71 |
72 | test "ReservedInterpolationKey message contains the reserved key" do
73 | force_reserved_interpolation_key do |exception|
74 | assert_equal 'reserved key :scope used in "%{scope}"', exception.message
75 | end
76 | end
77 |
78 | test "MissingTranslationData#new can be initialized with just two arguments" do
79 | assert I18n::MissingTranslationData.new('en', 'key')
80 | end
81 |
82 | private
83 |
84 | def force_invalid_locale
85 | I18n.translate(:foo, :locale => nil)
86 | rescue I18n::ArgumentError => e
87 | block_given? ? yield(e) : raise(e)
88 | end
89 |
90 | def force_missing_translation_data(options = {})
91 | store_translations('de', :bar => nil)
92 | I18n.translate(:foo, **options.merge(:scope => :bar, :locale => :de))
93 | rescue I18n::ArgumentError => e
94 | block_given? ? yield(e) : raise(e)
95 | end
96 |
97 | def force_invalid_pluralization_data
98 | store_translations('de', :foo => { :other => 'bar' })
99 | I18n.translate(:foo, :count => 1, :locale => :de)
100 | rescue I18n::ArgumentError => e
101 | block_given? ? yield(e) : raise(e)
102 | end
103 |
104 | def force_missing_interpolation_argument
105 | store_translations('de', :foo => "%{bar}")
106 | I18n.translate(:foo, :baz => 'baz', :locale => :de)
107 | rescue I18n::ArgumentError => e
108 | block_given? ? yield(e) : raise(e)
109 | end
110 |
111 | def force_reserved_interpolation_key
112 | store_translations('de', :foo => "%{scope}")
113 | I18n.translate(:foo, :baz => 'baz', :locale => :de)
114 | rescue I18n::ArgumentError => e
115 | block_given? ? yield(e) : raise(e)
116 | end
117 | end
118 |
--------------------------------------------------------------------------------
/test/i18n/gettext_plural_keys_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nGettextPluralKeysTest < I18n::TestCase
4 | def setup
5 | super
6 | I18n::Gettext.plural_keys[:zz] = [:value1, :value2]
7 | end
8 |
9 | test "Returns the plural keys of the given locale if present" do
10 | assert_equal I18n::Gettext.plural_keys(:zz), [:value1, :value2]
11 | end
12 |
13 | test "Returns the plural keys of :en if given locale not present" do
14 | assert_equal I18n::Gettext.plural_keys(:yy), [:one, :other]
15 | end
16 |
17 | test "Returns the whole hash with no arguments" do
18 | assert_equal I18n::Gettext.plural_keys, { :en => [:one, :other], :zz => [:value1, :value2] }
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/test/i18n/interpolate_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | # thanks to Masao's String extensions, some tests taken from Masao's tests
4 | # http://github.com/mutoh/gettext/blob/edbbe1fa8238fa12c7f26f2418403015f0270e47/test/test_string.rb
5 |
6 | class I18nInterpolateTest < I18n::TestCase
7 | test "String interpolates a hash argument w/ named placeholders" do
8 | assert_equal "Masao Mutoh", I18n.interpolate("%{first} %{last}", :first => 'Masao', :last => 'Mutoh' )
9 | end
10 |
11 | test "String interpolates a hash argument w/ named placeholders (reverse order)" do
12 | assert_equal "Mutoh, Masao", I18n.interpolate("%{last}, %{first}", :first => 'Masao', :last => 'Mutoh' )
13 | end
14 |
15 | test "String interpolates named placeholders with sprintf syntax" do
16 | assert_equal "10, 43.4", I18n.interpolate("%d, %.1f", :integer => 10, :float => 43.4)
17 | end
18 |
19 | test "String interpolates named placeholders with sprintf syntax, does not recurse" do
20 | assert_equal "%s", I18n.interpolate("%{msg}", :msg => '%s', :not_translated => 'should not happen' )
21 | end
22 |
23 | test "String interpolation does not replace anything when no placeholders are given" do
24 | assert_equal "aaa", I18n.interpolate("aaa", :num => 1)
25 | end
26 |
27 | test "String interpolation sprintf behaviour equals Ruby 1.9 behaviour" do
28 | assert_equal "1", I18n.interpolate("%d", :num => 1)
29 | assert_equal "0b1", I18n.interpolate("%#b", :num => 1)
30 | assert_equal "foo", I18n.interpolate("%s", :msg => "foo")
31 | assert_equal "1.000000", I18n.interpolate("%f", :num => 1.0)
32 | assert_equal " 1", I18n.interpolate("%3.0f", :num => 1.0)
33 | assert_equal "100.00", I18n.interpolate("%2.2f", :num => 100.0)
34 | assert_equal "0x64", I18n.interpolate("%#x", :num => 100.0)
35 | assert_raises(ArgumentError) { I18n.interpolate("%,d", :num => 100) }
36 | assert_raises(ArgumentError) { I18n.interpolate("%/d", :num => 100) }
37 | end
38 |
39 | test "String interpolation raises an I18n::MissingInterpolationArgument when the string has extra placeholders" do
40 | assert_raises(I18n::MissingInterpolationArgument, "key not found") do
41 | I18n.interpolate("%{first} %{last}", :first => 'Masao')
42 | end
43 | end
44 |
45 | test "String interpolation does not raise when extra values were passed" do
46 | assert_nothing_raised do
47 | assert_equal "Masao Mutoh", I18n.interpolate("%{first} %{last}", :first => 'Masao', :last => 'Mutoh', :salutation => 'Mr.' )
48 | end
49 | end
50 |
51 | test "% acts as escape character in String interpolation" do
52 | assert_equal "%{first}", I18n.interpolate("%%{first}", :first => 'Masao')
53 | assert_equal "% 1", I18n.interpolate("%% %d", :num => 1.0)
54 | assert_equal "%{num} %d", I18n.interpolate("%%{num} %%d", :num => 1)
55 | end
56 |
57 | def test_sprintf_mix_unformatted_and_formatted_named_placeholders
58 | assert_equal "foo 1.000000", I18n.interpolate("%{name} %f", :name => "foo", :num => 1.0)
59 | end
60 |
61 | class RailsSafeBuffer < String
62 |
63 | def gsub(*args, &block)
64 | to_str.gsub(*args, &block)
65 | end
66 |
67 | end
68 |
69 | test "with String subclass that redefined gsub method" do
70 | assert_equal "Hello mars world", I18n.interpolate(RailsSafeBuffer.new("Hello %{planet} world"), :planet => 'mars')
71 | end
72 |
73 | test "with String subclass that redefined gsub method returns same object if no interpolations" do
74 | string = RailsSafeBuffer.new("Hello world")
75 | assert_same string, I18n.interpolate(string, :planet => 'mars')
76 | end
77 | end
78 |
79 | class I18nMissingInterpolationCustomHandlerTest < I18n::TestCase
80 | def setup
81 | super
82 | @old_handler = I18n.config.missing_interpolation_argument_handler
83 | I18n.config.missing_interpolation_argument_handler = lambda do |key, values, string|
84 | "missing key is #{key}, values are #{values.inspect}, given string is '#{string}'"
85 | end
86 | end
87 |
88 | def teardown
89 | I18n.config.missing_interpolation_argument_handler = @old_handler
90 | super
91 | end
92 |
93 | test "String interpolation can use custom missing interpolation handler" do
94 | assert_equal %|Masao missing key is last, values are #{{:first=>"Masao"}.to_s}, given string is '%{first} %{last}'|,
95 | I18n.interpolate("%{first} %{last}", :first => 'Masao')
96 | end
97 | end
98 |
99 | class I18nCustomInterpolationPatternTest < I18n::TestCase
100 | def setup
101 | super
102 | @old_interpolation_patterns = I18n.config.interpolation_patterns
103 | I18n.config.interpolation_patterns << /\{\{(\w+)\}\}/
104 | end
105 |
106 | def teardown
107 | I18n.config.interpolation_patterns = @old_interpolation_patterns
108 | super
109 | end
110 |
111 | test "String interpolation can use custom interpolation pattern" do
112 | assert_equal "Masao Mutoh", I18n.interpolate("{{first}} {{last}}", :first => "Masao", :last => "Mutoh")
113 | end
114 | end
115 |
--------------------------------------------------------------------------------
/test/i18n/load_path_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nLoadPathTest < I18n::TestCase
4 | def setup
5 | super
6 | I18n.locale = :en
7 | I18n.backend = I18n::Backend::Simple.new
8 | store_translations(:en, :foo => {:bar => 'bar', :baz => 'baz'})
9 | end
10 |
11 | test "nested load paths do not break locale loading" do
12 | I18n.load_path = [[locales_dir + '/en.yml']]
13 | assert_equal "baz", I18n.t(:'foo.bar')
14 | end
15 |
16 | test "loading an empty yml file raises an InvalidLocaleData exception" do
17 | assert_raises I18n::InvalidLocaleData do
18 | I18n.load_path = [[locales_dir + '/invalid/empty.yml']]
19 | I18n.t(:'foo.bar', :default => "baz")
20 | end
21 | end
22 |
23 | test "loading an invalid yml file raises an InvalidLocaleData exception" do
24 | assert_raises I18n::InvalidLocaleData do
25 | I18n.load_path = [[locales_dir + '/invalid/syntax.yml']]
26 | I18n.t(:'foo.bar', :default => "baz")
27 | end
28 | end
29 |
30 | test "adding arrays of filenames to the load path does not break locale loading" do
31 | I18n.load_path << Dir[locales_dir + '/*.{rb,yml}']
32 | assert_equal "baz", I18n.t(:'foo.bar')
33 | end
34 |
35 | test "adding Pathnames to the load path does not break YML file locale loading" do
36 | I18n.load_path << Pathname.new(locales_dir + '/en.yml')
37 | assert_equal "baz", I18n.t(:'foo.bar')
38 | end
39 |
40 | test "adding Pathnames to the load path does not break Ruby file locale loading" do
41 | I18n.load_path << Pathname.new(locales_dir + '/en.rb')
42 | assert_equal "bas", I18n.t(:'fuh.bah')
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/test/i18n/middleware_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nMiddlewareTest < I18n::TestCase
4 | def setup
5 | super
6 | I18n.default_locale = :fr
7 | @app = DummyRackApp.new
8 | @middleware = I18n::Middleware.new(@app)
9 | end
10 |
11 | test "middleware initializes new config object after request" do
12 | old_i18n_config_object_id = Thread.current[:i18n_config].object_id
13 | @middleware.call({})
14 |
15 | updated_i18n_config_object_id = Thread.current[:i18n_config].object_id
16 | refute_equal updated_i18n_config_object_id, old_i18n_config_object_id
17 | end
18 |
19 | test "successfully resets i18n locale to default locale by defining new config" do
20 | @middleware.call({})
21 |
22 | assert_equal :fr, I18n.locale
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/locale/tag/rfc4646_test.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | require 'test_helper'
3 |
4 | class I18nLocaleTagRfc4646ParserTest < I18n::TestCase
5 | include I18n::Locale
6 |
7 | test "Rfc4646::Parser given a valid tag 'de' returns an array of subtags" do
8 | assert_equal ['de', nil, nil, nil, nil, nil, nil], Tag::Rfc4646::Parser.match('de')
9 | end
10 |
11 | test "Rfc4646::Parser given a valid tag 'de-DE' returns an array of subtags" do
12 | assert_equal ['de', nil, 'DE', nil, nil, nil, nil], Tag::Rfc4646::Parser.match('de-DE')
13 | end
14 |
15 | test "Rfc4646::Parser given a valid lowercase tag 'de-latn-de-variant-x-phonebk' returns an array of subtags" do
16 | assert_equal ['de', 'latn', 'de', 'variant', nil, 'x-phonebk', nil], Tag::Rfc4646::Parser.match('de-latn-de-variant-x-phonebk')
17 | end
18 |
19 | test "Rfc4646::Parser given a valid uppercase tag 'DE-LATN-DE-VARIANT-X-PHONEBK' returns an array of subtags" do
20 | assert_equal ['DE', 'LATN', 'DE', 'VARIANT', nil, 'X-PHONEBK', nil], Tag::Rfc4646::Parser.match('DE-LATN-DE-VARIANT-X-PHONEBK')
21 | end
22 |
23 | test "Rfc4646::Parser given an invalid tag 'a-DE' it returns false" do
24 | assert_equal false, Tag::Rfc4646::Parser.match('a-DE')
25 | end
26 |
27 | test "Rfc4646::Parser given an invalid tag 'de-419-DE' it returns false" do
28 | assert_equal false, Tag::Rfc4646::Parser.match('de-419-DE')
29 | end
30 | end
31 |
32 | # Tag for the locale 'de-Latn-DE-Variant-a-ext-x-phonebk-i-klingon'
33 |
34 | class I18nLocaleTagSubtagsTest < I18n::TestCase
35 | include I18n::Locale
36 |
37 | def setup
38 | super
39 | subtags = %w(de Latn DE variant a-ext x-phonebk i-klingon)
40 | @tag = Tag::Rfc4646.new(*subtags)
41 | end
42 |
43 | test "returns 'de' as the language subtag in lowercase" do
44 | assert_equal 'de', @tag.language
45 | end
46 |
47 | test "returns 'Latn' as the script subtag in titlecase" do
48 | assert_equal 'Latn', @tag.script
49 | end
50 |
51 | test "returns 'DE' as the region subtag in uppercase" do
52 | assert_equal 'DE', @tag.region
53 | end
54 |
55 | test "returns 'variant' as the variant subtag in lowercase" do
56 | assert_equal 'variant', @tag.variant
57 | end
58 |
59 | test "returns 'a-ext' as the extension subtag" do
60 | assert_equal 'a-ext', @tag.extension
61 | end
62 |
63 | test "returns 'x-phonebk' as the privateuse subtag" do
64 | assert_equal 'x-phonebk', @tag.privateuse
65 | end
66 |
67 | test "returns 'i-klingon' as the grandfathered subtag" do
68 | assert_equal 'i-klingon', @tag.grandfathered
69 | end
70 |
71 | test "returns a formatted tag string from #to_s" do
72 | assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon', @tag.to_s
73 | end
74 |
75 | test "returns an array containing the formatted subtags from #to_a" do
76 | assert_equal %w(de Latn DE variant a-ext x-phonebk i-klingon), @tag.to_a
77 | end
78 | end
79 |
80 | # Tag inheritance
81 |
82 | class I18nLocaleTagSubtagsTest < I18n::TestCase
83 | test "#parent returns 'de-Latn-DE-variant-a-ext-x-phonebk' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
84 | tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk i-klingon))
85 | assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk', tag.parent.to_s
86 | end
87 |
88 | test "#parent returns 'de-Latn-DE-variant-a-ext' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk'" do
89 | tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk))
90 | assert_equal 'de-Latn-DE-variant-a-ext', tag.parent.to_s
91 | end
92 |
93 | test "#parent returns 'de-Latn-DE-variant' as the parent of 'de-Latn-DE-variant-a-ext'" do
94 | tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext))
95 | assert_equal 'de-Latn-DE-variant', tag.parent.to_s
96 | end
97 |
98 | test "#parent returns 'de-Latn-DE' as the parent of 'de-Latn-DE-variant'" do
99 | tag = Tag::Rfc4646.new(*%w(de Latn DE variant))
100 | assert_equal 'de-Latn-DE', tag.parent.to_s
101 | end
102 |
103 | test "#parent returns 'de-Latn' as the parent of 'de-Latn-DE'" do
104 | tag = Tag::Rfc4646.new(*%w(de Latn DE))
105 | assert_equal 'de-Latn', tag.parent.to_s
106 | end
107 |
108 | test "#parent returns 'de' as the parent of 'de-Latn'" do
109 | tag = Tag::Rfc4646.new(*%w(de Latn))
110 | assert_equal 'de', tag.parent.to_s
111 | end
112 |
113 | # TODO RFC4647 says: "If no language tag matches the request, the "default" value is returned."
114 | # where should we set the default language?
115 | # test "#parent returns '' as the parent of 'de'" do
116 | # tag = Tag::Rfc4646.new *%w(de)
117 | # assert_equal '', tag.parent.to_s
118 | # end
119 |
120 | test "#parent returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
121 | parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon
122 | de-Latn-DE-variant-a-ext-x-phonebk
123 | de-Latn-DE-variant-a-ext
124 | de-Latn-DE-variant
125 | de-Latn-DE
126 | de-Latn
127 | de)
128 | tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk i-klingon))
129 | assert_equal parents, tag.self_and_parents.map(&:to_s)
130 | end
131 |
132 | test "returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
133 | parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon
134 | de-Latn-DE-variant-a-ext-x-phonebk
135 | de-Latn-DE-variant-a-ext
136 | de-Latn-DE-variant
137 | de-Latn-DE
138 | de-Latn
139 | de)
140 | tag = Tag::Rfc4646.new(*%w(de Latn DE variant a-ext x-phonebk i-klingon))
141 | assert_equal parents, tag.self_and_parents.map(&:to_s)
142 | end
143 | end
144 |
--------------------------------------------------------------------------------
/test/locale/tag/simple_test.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | require 'test_helper'
3 |
4 | class I18nLocaleTagSimpleTest < I18n::TestCase
5 | include I18n::Locale
6 |
7 | test "returns 'de' as the language subtag in lowercase" do
8 | assert_equal %w(de Latn DE), Tag::Simple.new('de-Latn-DE').subtags
9 | end
10 |
11 | test "returns a formatted tag string from #to_s" do
12 | assert_equal 'de-Latn-DE', Tag::Simple.new('de-Latn-DE').to_s
13 | end
14 |
15 | test "returns an array containing the formatted subtags from #to_a" do
16 | assert_equal %w(de Latn DE), Tag::Simple.new('de-Latn-DE').to_a
17 | end
18 |
19 | # Tag inheritance
20 |
21 | test "#parent returns 'de-Latn' as the parent of 'de-Latn-DE'" do
22 | assert_equal 'de-Latn', Tag::Simple.new('de-Latn-DE').parent.to_s
23 | end
24 |
25 | test "#parent returns 'de' as the parent of 'de-Latn'" do
26 | assert_equal 'de', Tag::Simple.new('de-Latn').parent.to_s
27 | end
28 |
29 | test "#self_and_parents returns an array of 3 tags for 'de-Latn-DE'" do
30 | assert_equal %w(de-Latn-DE de-Latn de), Tag::Simple.new('de-Latn-DE').self_and_parents.map { |tag| tag.to_s}
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/test/run_all.rb:
--------------------------------------------------------------------------------
1 | def bundle_check
2 | `bundle check` == "Resolving dependencies...\nThe Gemfile's dependencies are satisfied\n"
3 | end
4 |
5 | def execute(command)
6 | puts command
7 | system command
8 | end
9 |
10 | gemfiles = %w(Gemfile) + Dir['gemfiles/Gemfile*'].reject { |f| f.end_with?('.lock') }
11 |
12 | results = gemfiles.map do |gemfile|
13 | puts "\nBUNDLE_GEMFILE=#{gemfile}"
14 | ENV['BUNDLE_GEMFILE'] = File.expand_path("../../#{gemfile}", __FILE__)
15 |
16 | execute 'bundle install' unless bundle_check
17 | execute 'bundle exec rake test'
18 | end
19 |
20 | exit results.all?
21 |
--------------------------------------------------------------------------------
/test/run_one.rb:
--------------------------------------------------------------------------------
1 | def bundle_check
2 | `bundle check` == "Resolving dependencies...\nThe Gemfile's dependencies are satisfied\n"
3 | end
4 |
5 | def execute(command)
6 | puts command
7 | system command
8 | end
9 |
10 | execute 'bundle install' unless bundle_check
11 | execute "bundle exec ruby -w -I'lib:test' #{ARGV[0]}"
12 |
--------------------------------------------------------------------------------
/test/test_data/locales/de.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: version 0.0.1\n"
10 | "POT-Creation-Date: 2009-02-26 19:50+0100\n"
11 | "PO-Revision-Date: 2009-02-18 14:53+0100\n"
12 | "Last-Translator: FULL NAME \n"
13 | "Language-Team: LANGUAGE \n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18 |
19 | # #: app/helpers/translation_helper.rb:3
20 | # msgid "%{relative_time} ago"
21 | # msgstr "vor %{relative_time}"
22 |
23 | #: app/views/cars/show.html.erb:5
24 | msgid "Axis"
25 | msgid_plural "Axis"
26 | msgstr[0] "Achse"
27 | msgstr[1] "Achsen"
28 |
29 | #: app/controllers/cars_controller.rb:47
30 | msgid "Car was successfully created."
31 | msgstr "Auto wurde erfolgreich gespeichert"
32 |
33 | #: app/controllers/cars_controller.rb:64
34 | msgid "Car was successfully updated."
35 | msgstr "Auto wurde erfolgreich aktualisiert"
36 |
37 | #: app/views/cars/show.html.erb:1 locale/model_attributes.rb:3
38 | msgid "Car|Model"
39 | msgstr "Modell"
40 |
41 | #: app/views/cars/show.html.erb:3 locale/model_attributes.rb:4
42 | msgid "Car|Wheels count"
43 | msgstr "Räderzahl"
44 |
45 | msgctxt "New car"
46 | msgid "Wheels count"
47 | msgstr "Räderzahl!"
48 |
49 | #: app/views/cars/show.html.erb:7
50 | msgid "Created"
51 | msgstr "Erstellt"
52 |
53 | #: app/views/cars/show.html.erb:9
54 | msgid "Month"
55 | msgstr "Monat"
56 |
57 | #: locale/model_attributes.rb:2
58 | msgid "car"
59 | msgstr "Auto"
60 |
61 | #: locale/testlog_phrases.rb:2
62 | msgid "this is a dynamic translation which was found thorugh gettext_test_log!"
63 | msgstr ""
64 | "Dies ist eine dynamische Übersetzung, die durch gettext_test_log "
65 | "gefunden wurde!"
66 |
67 | #: app/views/cars/nowhere_really
68 | msgid "Car|wheel"
69 | msgid_plural "Car|wheels"
70 | msgstr[0] "Rad"
71 | msgstr[1] "Räder"
72 |
73 | msgctxt "New car"
74 | msgid "wheel"
75 | msgid_plural "wheels"
76 | msgstr[0] "Rad!"
77 | msgstr[1] "Räder!"
78 |
79 | msgid "On %{count} wheel."
80 | msgid_plural "On %{count} wheels."
81 | msgstr[0] "Auf %{count} Achse."
82 | msgstr[1] "Auf %{count} Achsen."
83 |
--------------------------------------------------------------------------------
/test/test_data/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": {
3 | "foo": {
4 | "bar": "baz"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/test_data/locales/en.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | { :en => { :fuh => { :bah => "bas" } } }
--------------------------------------------------------------------------------
/test/test_data/locales/en.yaml:
--------------------------------------------------------------------------------
1 | en:
2 | foo:
3 | bar: baz
--------------------------------------------------------------------------------
/test/test_data/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | foo:
3 | bar: baz
4 |
--------------------------------------------------------------------------------
/test/test_data/locales/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
2 | animal:
3 | dog: chien
4 |
--------------------------------------------------------------------------------
/test/test_data/locales/invalid/empty.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-i18n/i18n/4dddd855039b0c5a0b5b3b2df69783374058a7c9/test/test_data/locales/invalid/empty.yml
--------------------------------------------------------------------------------
/test/test_data/locales/invalid/syntax.yml:
--------------------------------------------------------------------------------
1 | en:
2 | foo: foo
3 | bar:
4 | baz:
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | require 'minitest/autorun'
2 | require 'bundler/setup'
3 | require 'i18n'
4 | require 'mocha/minitest'
5 | require 'test_declarative'
6 |
7 | class I18n::TestCase < Minitest::Test
8 | def assert_nothing_raised(*args)
9 | yield
10 | end
11 |
12 | def self.key_value?
13 | defined?(ActiveSupport)
14 | end
15 |
16 | def setup
17 | super
18 | I18n.load_path = nil
19 | I18n.enforce_available_locales = false
20 | end
21 |
22 | def teardown
23 | I18n.locale = nil
24 | I18n.default_locale = nil
25 | I18n.load_path = nil
26 | I18n.available_locales = nil
27 | I18n.backend = nil
28 | I18n.default_separator = nil
29 | I18n.enforce_available_locales = true
30 | I18n.fallbacks = nil if I18n.respond_to?(:fallbacks=)
31 | super
32 | end
33 |
34 | protected
35 |
36 | def translations
37 | I18n.backend.instance_variable_get(:@translations)
38 | end
39 |
40 | def store_translations(locale, data, options = I18n::EMPTY_HASH)
41 | I18n.backend.store_translations(locale, data, options)
42 | end
43 |
44 | def locales_dir
45 | File.dirname(__FILE__) + '/test_data/locales'
46 | end
47 |
48 | def stub_const(klass, constant, new_value)
49 | old_value = klass.const_get(constant)
50 | klass.send(:remove_const, constant)
51 | klass.const_set(constant, new_value)
52 | yield
53 | ensure
54 | klass.send(:remove_const, constant)
55 | klass.const_set(constant, old_value)
56 | end
57 | end
58 |
59 | class DummyRackApp
60 | def call(env)
61 | I18n.locale = :es
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/test/utils_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class I18nUtilsTest < I18n::TestCase
4 |
5 | test ".deep_symbolize_keys" do
6 | hash = { 'foo' => { 'bar' => { 'baz' => 'bar' } } }
7 | expected = { :foo => { :bar => { :baz => 'bar' } } }
8 | assert_equal expected, I18n::Utils.deep_symbolize_keys(hash)
9 | end
10 |
11 | test "#deep_symbolize_keys with numeric keys" do
12 | hash = { 1 => { 2 => { 3 => 'bar' } } }
13 | expected = { 1 => { 2 => { 3 => 'bar' } } }
14 | assert_equal expected, I18n::Utils.deep_symbolize_keys(hash)
15 | end
16 |
17 | test "#except" do
18 | hash = { :foo => 'bar', :baz => 'bar' }
19 | expected = { :foo => 'bar' }
20 | assert_equal expected, I18n::Utils.except(hash, :baz)
21 | end
22 |
23 | test "#deep_merge!" do
24 | hash = { :foo => { :bar => { :baz => 'bar' } }, :baz => 'bar' }
25 | I18n::Utils.deep_merge!(hash, :foo => { :bar => { :baz => 'foo' } })
26 |
27 | expected = { :foo => { :bar => { :baz => 'foo' } }, :baz => 'bar' }
28 | assert_equal expected, hash
29 | end
30 | end
31 |
--------------------------------------------------------------------------------