sayso-i18n 0.5.0.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/CHANGELOG.textile +152 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +103 -0
  4. data/ci/Gemfile.no-rails +5 -0
  5. data/ci/Gemfile.no-rails.lock +14 -0
  6. data/ci/Gemfile.rails-2.3.x +9 -0
  7. data/ci/Gemfile.rails-2.3.x.lock +23 -0
  8. data/ci/Gemfile.rails-3.x +9 -0
  9. data/ci/Gemfile.rails-3.x.lock +23 -0
  10. data/lib/i18n.rb +324 -0
  11. data/lib/i18n/backend.rb +19 -0
  12. data/lib/i18n/backend/base.rb +174 -0
  13. data/lib/i18n/backend/cache.rb +102 -0
  14. data/lib/i18n/backend/cascade.rb +53 -0
  15. data/lib/i18n/backend/chain.rb +80 -0
  16. data/lib/i18n/backend/fallbacks.rb +70 -0
  17. data/lib/i18n/backend/flatten.rb +113 -0
  18. data/lib/i18n/backend/flatten_yml.rb +70 -0
  19. data/lib/i18n/backend/gettext.rb +71 -0
  20. data/lib/i18n/backend/interpolation_compiler.rb +121 -0
  21. data/lib/i18n/backend/key_value.rb +100 -0
  22. data/lib/i18n/backend/memoize.rb +46 -0
  23. data/lib/i18n/backend/metadata.rb +65 -0
  24. data/lib/i18n/backend/pluralization.rb +55 -0
  25. data/lib/i18n/backend/simple.rb +87 -0
  26. data/lib/i18n/backend/transliterator.rb +98 -0
  27. data/lib/i18n/config.rb +86 -0
  28. data/lib/i18n/core_ext/hash.rb +29 -0
  29. data/lib/i18n/core_ext/kernel/surpress_warnings.rb +9 -0
  30. data/lib/i18n/core_ext/string/interpolate.rb +105 -0
  31. data/lib/i18n/exceptions.rb +88 -0
  32. data/lib/i18n/gettext.rb +25 -0
  33. data/lib/i18n/gettext/helpers.rb +64 -0
  34. data/lib/i18n/gettext/po_parser.rb +329 -0
  35. data/lib/i18n/interpolate/ruby.rb +31 -0
  36. data/lib/i18n/locale.rb +6 -0
  37. data/lib/i18n/locale/fallbacks.rb +96 -0
  38. data/lib/i18n/locale/tag.rb +28 -0
  39. data/lib/i18n/locale/tag/parents.rb +22 -0
  40. data/lib/i18n/locale/tag/rfc4646.rb +74 -0
  41. data/lib/i18n/locale/tag/simple.rb +39 -0
  42. data/lib/i18n/tests.rb +12 -0
  43. data/lib/i18n/tests/basics.rb +54 -0
  44. data/lib/i18n/tests/defaults.rb +40 -0
  45. data/lib/i18n/tests/interpolation.rb +133 -0
  46. data/lib/i18n/tests/link.rb +56 -0
  47. data/lib/i18n/tests/localization.rb +19 -0
  48. data/lib/i18n/tests/localization/date.rb +84 -0
  49. data/lib/i18n/tests/localization/date_time.rb +77 -0
  50. data/lib/i18n/tests/localization/procs.rb +116 -0
  51. data/lib/i18n/tests/localization/time.rb +76 -0
  52. data/lib/i18n/tests/lookup.rb +74 -0
  53. data/lib/i18n/tests/pluralization.rb +35 -0
  54. data/lib/i18n/tests/procs.rb +55 -0
  55. data/lib/i18n/version.rb +3 -0
  56. data/test/all.rb +8 -0
  57. data/test/api/all_features_test.rb +58 -0
  58. data/test/api/cascade_test.rb +28 -0
  59. data/test/api/chain_test.rb +24 -0
  60. data/test/api/fallbacks_test.rb +30 -0
  61. data/test/api/flatten_yml_test.rb +32 -0
  62. data/test/api/key_value_test.rb +28 -0
  63. data/test/api/memoize_test.rb +60 -0
  64. data/test/api/pluralization_test.rb +30 -0
  65. data/test/api/simple_test.rb +28 -0
  66. data/test/backend/cache_test.rb +83 -0
  67. data/test/backend/cascade_test.rb +85 -0
  68. data/test/backend/chain_test.rb +72 -0
  69. data/test/backend/exceptions_test.rb +23 -0
  70. data/test/backend/fallbacks_test.rb +120 -0
  71. data/test/backend/flatten_yml_test.rb +49 -0
  72. data/test/backend/interpolation_compiler_test.rb +102 -0
  73. data/test/backend/key_value_test.rb +46 -0
  74. data/test/backend/memoize_test.rb +47 -0
  75. data/test/backend/metadata_test.rb +67 -0
  76. data/test/backend/pluralization_test.rb +44 -0
  77. data/test/backend/simple_test.rb +83 -0
  78. data/test/backend/transliterator_test.rb +81 -0
  79. data/test/core_ext/hash_test.rb +30 -0
  80. data/test/core_ext/string/interpolate_test.rb +99 -0
  81. data/test/gettext/api_test.rb +206 -0
  82. data/test/gettext/backend_test.rb +93 -0
  83. data/test/i18n/exceptions_test.rb +116 -0
  84. data/test/i18n/interpolate_test.rb +61 -0
  85. data/test/i18n/load_path_test.rb +26 -0
  86. data/test/i18n_test.rb +236 -0
  87. data/test/locale/fallbacks_test.rb +124 -0
  88. data/test/locale/tag/rfc4646_test.rb +142 -0
  89. data/test/locale/tag/simple_test.rb +32 -0
  90. data/test/run_all.rb +21 -0
  91. data/test/test_data/locales/de.po +72 -0
  92. data/test/test_data/locales/en.rb +3 -0
  93. data/test/test_data/locales/en.yml +3 -0
  94. data/test/test_data/locales/invalid/empty.yml +1 -0
  95. data/test/test_data/locales/plurals.rb +113 -0
  96. data/test/test_helper.rb +56 -0
  97. metadata +194 -0
@@ -0,0 +1,85 @@
1
+ require 'test_helper'
2
+
3
+ class I18nBackendCascadeTest < Test::Unit::TestCase
4
+ class Backend < I18n::Backend::Simple
5
+ include I18n::Backend::Cascade
6
+ end
7
+
8
+ def setup
9
+ I18n.backend = Backend.new
10
+ store_translations(:en, :foo => 'foo', :bar => { :baz => 'baz' })
11
+ @cascade_options = { :step => 1, :offset => 1, :skip_root => false }
12
+ end
13
+
14
+ def lookup(key, options = {})
15
+ I18n.t(key, options.merge(:cascade => @cascade_options))
16
+ end
17
+
18
+ test "still returns an existing translation as usual" do
19
+ assert_equal 'foo', lookup(:foo)
20
+ assert_equal 'baz', lookup(:'bar.baz')
21
+ end
22
+
23
+ test "falls back by cutting keys off the end of the scope" do
24
+ assert_equal 'foo', lookup(:foo, :scope => :'missing')
25
+ assert_equal 'foo', lookup(:foo, :scope => :'missing.missing')
26
+ assert_equal 'baz', lookup(:baz, :scope => :'bar.missing')
27
+ assert_equal 'baz', lookup(:baz, :scope => :'bar.missing.missing')
28
+ end
29
+
30
+ test "raises I18n::MissingTranslationData exception when no translation was found" do
31
+ assert_raise(I18n::MissingTranslationData) { lookup(:'foo.missing', :raise => true) }
32
+ assert_raise(I18n::MissingTranslationData) { lookup(:'bar.baz.missing', :raise => true) }
33
+ assert_raise(I18n::MissingTranslationData) { lookup(:'missing.bar.baz', :raise => true) }
34
+ end
35
+
36
+ test "cascades before evaluating the default" do
37
+ assert_equal 'foo', lookup(:foo, :scope => :missing, :default => 'default')
38
+ end
39
+
40
+ test "cascades defaults, too" do
41
+ assert_equal 'foo', lookup(nil, :default => [:'missing.missing', :'missing.foo'])
42
+ end
43
+
44
+ test "works with :offset => 2 and a single key" do
45
+ @cascade_options[:offset] = 2
46
+ lookup(:foo)
47
+ end
48
+
49
+ test "assemble required fallbacks for ActiveRecord validation messages" do
50
+ store_translations(:en,
51
+ :errors => {
52
+ :odd => 'errors.odd',
53
+ :reply => { :title => { :blank => 'errors.reply.title.blank' }, :taken => 'errors.reply.taken' },
54
+ :topic => { :title => { :format => 'errors.topic.title.format' }, :length => 'errors.topic.length' }
55
+ }
56
+ )
57
+ assert_equal 'errors.reply.title.blank', lookup(:'errors.reply.title.blank', :default => :'errors.topic.title.blank')
58
+ assert_equal 'errors.reply.taken', lookup(:'errors.reply.title.taken', :default => :'errors.topic.title.taken')
59
+ assert_equal 'errors.topic.title.format', lookup(:'errors.reply.title.format', :default => :'errors.topic.title.format')
60
+ assert_equal 'errors.topic.length', lookup(:'errors.reply.title.length', :default => :'errors.topic.title.length')
61
+ assert_equal 'errors.odd', lookup(:'errors.reply.title.odd', :default => :'errors.topic.title.odd')
62
+ end
63
+
64
+ test "assemble action view translation helper lookup cascade" do
65
+ @cascade_options[:offset] = 2
66
+
67
+ store_translations(:en,
68
+ :menu => { :show => 'menu.show' },
69
+ :namespace => {
70
+ :menu => { :new => 'namespace.menu.new' },
71
+ :controller => {
72
+ :menu => { :edit => 'namespace.controller.menu.edit' },
73
+ :action => {
74
+ :menu => { :destroy => 'namespace.controller.action.menu.destroy' }
75
+ }
76
+ }
77
+ }
78
+ )
79
+
80
+ assert_equal 'menu.show', lookup(:'namespace.controller.action.menu.show')
81
+ assert_equal 'namespace.menu.new', lookup(:'namespace.controller.action.menu.new')
82
+ assert_equal 'namespace.controller.menu.edit', lookup(:'namespace.controller.action.menu.edit')
83
+ assert_equal 'namespace.controller.action.menu.destroy', lookup(:'namespace.controller.action.menu.destroy')
84
+ end
85
+ end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+
3
+ class I18nBackendChainTest < Test::Unit::TestCase
4
+ def setup
5
+ @first = backend(:en => {
6
+ :foo => 'Foo', :formats => { :short => 'short' }, :plural_1 => { :one => '%{count}' }
7
+ })
8
+ @second = backend(:en => {
9
+ :bar => 'Bar', :formats => { :long => 'long' }, :plural_2 => { :one => 'one' }
10
+ })
11
+ @chain = I18n.backend = I18n::Backend::Chain.new(@first, @second)
12
+ end
13
+
14
+ test "looks up translations from the first chained backend" do
15
+ assert_equal 'Foo', @first.send(:translations)[:en][:foo]
16
+ assert_equal 'Foo', I18n.t(:foo)
17
+ end
18
+
19
+ test "looks up translations from the second chained backend" do
20
+ assert_equal 'Bar', @second.send(:translations)[:en][:bar]
21
+ assert_equal 'Bar', I18n.t(:bar)
22
+ end
23
+
24
+ test "defaults only apply to lookups on the last backend in the chain" do
25
+ assert_equal 'Foo', I18n.t(:foo, :default => 'Bah')
26
+ assert_equal 'Bar', I18n.t(:bar, :default => 'Bah')
27
+ assert_equal 'Bah', I18n.t(:bah, :default => 'Bah') # default kicks in only here
28
+ end
29
+
30
+ test "default" do
31
+ assert_equal 'Fuh', I18n.t(:default => 'Fuh')
32
+ assert_equal 'Zero', I18n.t(:default => { :zero => 'Zero' }, :count => 0)
33
+ assert_equal({ :zero => 'Zero' }, I18n.t(:default => { :zero => 'Zero' }))
34
+ assert_equal 'Foo', I18n.t(:default => :foo)
35
+ end
36
+
37
+ test 'default is returned if translation is missing' do
38
+ assert_equal({}, I18n.t(:'i18n.transliterate.rule', :locale => 'en', :default => {}))
39
+ end
40
+
41
+ test "namespace lookup collects results from all backends" do
42
+ assert_equal({ :short => 'short', :long => 'long' }, I18n.t(:formats))
43
+ end
44
+
45
+ test "namespace lookup with only the first backend returning a result" do
46
+ assert_equal({ :one => '%{count}' }, I18n.t(:plural_1))
47
+ end
48
+
49
+ test "pluralization still works" do
50
+ assert_equal '1', I18n.t(:plural_1, :count => 1)
51
+ assert_equal 'one', I18n.t(:plural_2, :count => 1)
52
+ end
53
+
54
+ test "bulk lookup collects results from all backends" do
55
+ assert_equal ['Foo', 'Bar'], I18n.t([:foo, :bar])
56
+ assert_equal ['Foo', 'Bar', 'Bah'], I18n.t([:foo, :bar, :bah], :default => 'Bah')
57
+ assert_equal [{ :short => 'short', :long => 'long' }, { :one => 'one' }, 'Bah'], I18n.t([:formats, :plural_2, :bah], :default => 'Bah')
58
+ end
59
+
60
+ test "store_translations options are not dropped while transfering to backend" do
61
+ @first.expects(:store_translations).with(:foo, {:bar => :baz}, {:option => 'persists'})
62
+ I18n.backend.store_translations :foo, {:bar => :baz}, {:option => 'persists'}
63
+ end
64
+
65
+ protected
66
+
67
+ def backend(translations)
68
+ backend = I18n::Backend::Simple.new
69
+ translations.each { |locale, data| backend.store_translations(locale, data) }
70
+ backend
71
+ end
72
+ end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class I18nBackendExceptionsTest < Test::Unit::TestCase
4
+ def setup
5
+ I18n.backend = I18n::Backend::Simple.new
6
+ end
7
+
8
+ test "exceptions: MissingTranslationData message from #translate includes the given scope and full key" do
9
+ begin
10
+ I18n.t(:'baz.missing', :scope => :'foo.bar', :raise => true)
11
+ rescue I18n::MissingTranslationData => exception
12
+ end
13
+ assert_equal "translation missing: en.foo.bar.baz.missing", exception.message
14
+ end
15
+
16
+ test "exceptions: MissingTranslationData message from #localize includes the given scope and full key" do
17
+ begin
18
+ I18n.l(Time.now, :format => :foo)
19
+ rescue I18n::MissingTranslationData => exception
20
+ end
21
+ assert_equal "translation missing: en.time.formats.foo", exception.message
22
+ end
23
+ end
@@ -0,0 +1,120 @@
1
+ require 'test_helper'
2
+
3
+ class I18nBackendFallbacksTranslateTest < Test::Unit::TestCase
4
+ class Backend < I18n::Backend::Simple
5
+ include I18n::Backend::Fallbacks
6
+ end
7
+
8
+ def setup
9
+ I18n.backend = Backend.new
10
+ store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en', :buz => 'Buz in :en')
11
+ store_translations(:de, :bar => 'Bar in :de', :baz => 'Baz in :de')
12
+ store_translations(:'de-DE', :baz => 'Baz in :de-DE')
13
+ end
14
+
15
+ test "still returns an existing translation as usual" do
16
+ assert_equal 'Foo in :en', I18n.t(:foo, :locale => :en)
17
+ assert_equal 'Bar in :de', I18n.t(:bar, :locale => :de)
18
+ assert_equal 'Baz in :de-DE', I18n.t(:baz, :locale => :'de-DE')
19
+ end
20
+
21
+ test "returns the :en translation for a missing :de translation" do
22
+ assert_equal 'Foo in :en', I18n.t(:foo, :locale => :de)
23
+ end
24
+
25
+ test "returns the :de translation for a missing :'de-DE' translation" do
26
+ assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE')
27
+ end
28
+
29
+ test "returns the :en translation for translation missing in both :de and :'de-De'" do
30
+ assert_equal 'Buz in :en', I18n.t(:buz, :locale => :'de-DE')
31
+ end
32
+
33
+ test "returns the :de translation for a missing :'de-DE' when :default is a String" do
34
+ assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE', :default => "Default Bar")
35
+ assert_equal "Default Bar", I18n.t(:missing_bar, :locale => :'de-DE', :default => "Default Bar")
36
+ end
37
+
38
+ test "returns the :de translation for a missing :'de-DE' when defaults is a Symbol (which exists in :en)" do
39
+ assert_equal "Bar in :de", I18n.t(:bar, :locale => :'de-DE', :default => [:buz])
40
+ end
41
+
42
+ test "returns the :'de-DE' default :baz translation for a missing :'de-DE' (which exists in :de)" do
43
+ assert_equal "Baz in :de-DE", I18n.t(:bar, :locale => :'de-DE', :default => [:baz])
44
+ end
45
+
46
+ test "returns the :de translation for a missing :'de-DE' when :default is a Proc" do
47
+ assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE', :default => Proc.new { "Default Bar" })
48
+ assert_equal "Default Bar", I18n.t(:missing_bar, :locale => :'de-DE', :default => Proc.new { "Default Bar" })
49
+ end
50
+
51
+ test "returns the :'de-DE' default :baz translation for a missing :'de-DE' when defaults contains Symbol" do
52
+ assert_equal 'Baz in :de-DE', I18n.t(:missing_foo, :locale => :'de-DE', :default => [:baz, "Default Bar"])
53
+ end
54
+
55
+ test "returns the defaults translation for a missing :'de-DE' when defaults contains a String or Proc before Symbol" do
56
+ assert_equal "Default Bar", I18n.t(:missing_foo, :locale => :'de-DE', :default => [:missing_bar, "Default Bar", :baz])
57
+ assert_equal "Default Bar", I18n.t(:missing_foo, :locale => :'de-DE', :default => [:missing_bar, Proc.new { "Default Bar" }, :baz])
58
+ end
59
+
60
+ test "returns the default translation for a missing :'de-DE' and existing :de when default is a Hash" do
61
+ assert_equal 'Default 6 Bars', I18n.t(:missing_foo, :locale => :'de-DE', :default => [:missing_bar, {:other => "Default %{count} Bars"}, "Default Bar"], :count => 6)
62
+ end
63
+
64
+ test "raises I18n::MissingTranslationData exception when no translation was found" do
65
+ assert_raise(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :en, :raise => true) }
66
+ assert_raise(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :de, :raise => true) }
67
+ end
68
+
69
+ test "should ensure that default is not splitted on new line char" do
70
+ assert_equal "Default \n Bar", I18n.t(:missing_bar, :default => "Default \n Bar")
71
+ end
72
+ end
73
+
74
+ class I18nBackendFallbacksLocalizeTest < Test::Unit::TestCase
75
+ class Backend < I18n::Backend::Simple
76
+ include I18n::Backend::Fallbacks
77
+ end
78
+
79
+ def setup
80
+ I18n.backend = Backend.new
81
+ store_translations(:en, :date => { :formats => { :en => 'en' }, :day_names => %w(Sunday) })
82
+ store_translations(:de, :date => { :formats => { :de => 'de' } })
83
+ end
84
+
85
+ test "still uses an existing format as usual" do
86
+ assert_equal 'en', I18n.l(Date.today, :format => :en, :locale => :en)
87
+ end
88
+
89
+ test "looks up and uses a fallback locale's format for a key missing in the given locale (1)" do
90
+ assert_equal 'en', I18n.l(Date.today, :format => :en, :locale => :de)
91
+ end
92
+
93
+ test "looks up and uses a fallback locale's format for a key missing in the given locale (2)" do
94
+ assert_equal 'de', I18n.l(Date.today, :format => :de, :locale => :'de-DE')
95
+ end
96
+
97
+ test "still uses an existing day name translation as usual" do
98
+ assert_equal 'Sunday', I18n.l(Date.new(2010, 1, 3), :format => '%A', :locale => :en)
99
+ end
100
+
101
+ test "uses a fallback locale's translation for a key missing in the given locale" do
102
+ assert_equal 'Sunday', I18n.l(Date.new(2010, 1, 3), :format => '%A', :locale => :de)
103
+ end
104
+ end
105
+
106
+ class I18nBackendFallbacksWithChainTest < Test::Unit::TestCase
107
+ class Backend < I18n::Backend::Simple
108
+ include I18n::Backend::Fallbacks
109
+ end
110
+
111
+ def setup
112
+ backend = Backend.new
113
+ backend.store_translations(:de, :foo => 'FOO')
114
+ I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, backend)
115
+ end
116
+
117
+ test "falls back from de-DE to de when there is no translation for de-DE available" do
118
+ assert_equal 'FOO', I18n.t(:foo, :locale => :'de-DE')
119
+ end
120
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../')); $:.uniq!
3
+ require 'test_helper'
4
+ require 'backend/simple_test'
5
+
6
+ class I18nBackendFlattenYmlTest < I18nBackendSimpleTest
7
+ class FlattenYmlBackend
8
+ include I18n::Backend::Base
9
+ include I18n::Backend::FlattenYml
10
+ end
11
+
12
+ def setup
13
+ super
14
+ I18n.backend = FlattenYmlBackend.new
15
+ end
16
+ end
17
+
18
+ class I18nBackendFlattenYmlSpecificTest < Test::Unit::TestCase
19
+ class FlattenYmlBackend
20
+ include I18n::Backend::Base
21
+ include I18n::Backend::FlattenYml
22
+ end
23
+
24
+ def setup
25
+ @backend = FlattenYmlBackend.new
26
+ end
27
+
28
+ def assert_unified(expected, flattened)
29
+ assert_equal expected, @backend.send(:unify_hash, flattened)
30
+ end
31
+
32
+ test "unify_hash works" do
33
+ assert_unified(
34
+ {:a=>'a', :b=>{:c=>'c', :f=>{:x=>'x', :y=>'y', :z=>'z'}}, :c=>{:d=>{:e=>'e'}}},
35
+ {:a=>'a', :b=>{:c=>'c', :f=>{:x=>'x'}}, :"b.f.y" => "y", :"b.f.z" => "z", :"c.d.e"=>"e"}
36
+ )
37
+ end
38
+
39
+ test "translate" do
40
+ @backend.store_translations :en, {:"a.b.c" => "foo", :x => {:y => {:z => "bar"}}}
41
+
42
+ assert_equal({:c=>"foo"}, @backend.translate(:en, :"a.b"))
43
+ assert_equal("foo", @backend.translate(:en, :"a.b.c"))
44
+
45
+ assert_equal({:z=>"bar"}, @backend.translate(:en, :"x.y"))
46
+ assert_equal("bar", @backend.translate(:en, :"x.y.z"))
47
+ end
48
+
49
+ end
@@ -0,0 +1,102 @@
1
+ require 'test_helper'
2
+
3
+ class InterpolationCompilerTest < Test::Unit::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
+ end
80
+
81
+ class I18nBackendInterpolationCompilerTest < Test::Unit::TestCase
82
+ class Backend < I18n::Backend::Simple
83
+ include I18n::Backend::InterpolationCompiler
84
+ end
85
+
86
+ include I18n::Tests::Interpolation
87
+
88
+ def setup
89
+ I18n.backend = Backend.new
90
+ super
91
+ end
92
+
93
+ # pre-compile default strings to make sure we are testing I18n::Backend::InterpolationCompiler
94
+ def interpolate(*args)
95
+ options = args.last.kind_of?(Hash) ? args.last : {}
96
+ if default_str = options[:default]
97
+ I18n::Backend::InterpolationCompiler::Compiler.compile_if_an_interpolation(default_str)
98
+ end
99
+ super
100
+ end
101
+
102
+ end