theoooo-i18n 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Basics
5
+ def test_available_locales
6
+ backend_store_translations 'de', :foo => 'bar'
7
+ backend_store_translations 'en', :foo => 'foo'
8
+ assert_equal ['de', 'en'], I18n.backend.available_locales.map{|locale| locale.to_s }.sort
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,63 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Interpolation
5
+ def interpolate(options)
6
+ I18n.backend.translate('en', nil, options)
7
+ end
8
+
9
+ def test_interpolation_given_no_interpolation_values_it_does_not_alter_the_string
10
+ assert_equal 'Hi {{name}}!', interpolate(:default => 'Hi {{name}}!')
11
+ end
12
+
13
+ def test_interpolation_given_interpolation_values_it_interpolates_the_values_to_the_string
14
+ assert_equal 'Hi David!', interpolate(:default => 'Hi {{name}}!', :name => 'David')
15
+ end
16
+
17
+ def test_interpolation_given_interpolation_values_with_nil_values_it_interpolates_the_values_to_the_string
18
+ assert_equal 'Hi !', interpolate(:default => 'Hi {{name}}!', :name => nil)
19
+ end
20
+
21
+ def test_interpolate_with_ruby_1_9_syntax
22
+ assert_equal 'Hi David!', interpolate(:default => 'Hi %{name}!', :name => 'David')
23
+ end
24
+
25
+ def test_interpolate_given_a_value_hash_interpolates_into_unicode_string
26
+ assert_equal 'Häi David!', interpolate(:default => 'Häi {{name}}!', :name => 'David')
27
+ end
28
+
29
+ def test_interpolate_given_a_unicode_value_hash_interpolates_to_the_string
30
+ assert_equal 'Hi ゆきひろ!', interpolate(:default => 'Hi {{name}}!', :name => 'ゆきひろ')
31
+ end
32
+
33
+ def test_interpolate_given_a_unicode_value_hash_interpolates_into_unicode_string
34
+ assert_equal 'こんにちは、ゆきひろさん!', interpolate(:default => 'こんにちは、{{name}}さん!', :name => 'ゆきひろ')
35
+ end
36
+
37
+ if Kernel.const_defined?(:Encoding)
38
+ def test_interpolate_given_a_non_unicode_multibyte_value_hash_interpolates_into_a_string_with_the_same_encoding
39
+ assert_equal euc_jp('Hi ゆきひろ!'), interpolate(:default => 'Hi {{name}}!', :name => euc_jp('ゆきひろ'))
40
+ end
41
+
42
+ def test_interpolate_given_a_unicode_value_hash_into_a_non_unicode_multibyte_string_raises_encoding_compatibility_error
43
+ assert_raises(Encoding::CompatibilityError) do
44
+ interpolate(:default => euc_jp('こんにちは、{{name}}さん!'), :name => 'ゆきひろ')
45
+ end
46
+ end
47
+
48
+ def test_interpolate_given_a_non_unicode_multibyte_value_hash_into_an_unicode_string_raises_encoding_compatibility_error
49
+ assert_raises(Encoding::CompatibilityError) do
50
+ interpolate(:default => 'こんにちは、{{name}}さん!', :name => euc_jp('ゆきひろ'))
51
+ end
52
+ end
53
+ end
54
+
55
+ def test_interpolate_given_a_string_containing_a_reserved_key_raises_reserved_interpolation_key
56
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '{{default}}', :foo => :bar) }
57
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '{{scope}}', :foo => :bar) }
58
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '{{separator}}', :foo => :bar) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Lambda
5
+ def test_translate_simple_proc
6
+ setup_proc_translations
7
+ assert_equal 'bar=bar, baz=baz, foo=foo', I18n.backend.translate('en', :a_lambda, :foo => 'foo', :bar => 'bar', :baz => 'baz')
8
+ end
9
+
10
+ def test_translate_proc_in_defaults
11
+ setup_proc_translations
12
+ assert_equal 'bar=bar, baz=baz, foo=foo', I18n.backend.translate('en', :does_not_exist, :default => :a_lambda, :foo => 'foo', :bar => 'bar', :baz => 'baz')
13
+ assert_equal 'bar=bar, baz=baz, foo=foo', I18n.backend.translate('en', :does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3, :a_lambda], :foo => 'foo', :bar => 'bar', :baz => 'baz')
14
+ end
15
+
16
+ def test_translate_proc_with_pluralize
17
+ setup_proc_translations
18
+ params = { :zero => 'zero', :one => 'one', :other => 'other' }
19
+ assert_equal 'zero', I18n.backend.translate('en', :lambda_for_pluralize, params.merge(:count => 0))
20
+ assert_equal 'one', I18n.backend.translate('en', :lambda_for_pluralize, params.merge(:count => 1))
21
+ assert_equal 'other', I18n.backend.translate('en', :lambda_for_pluralize, params.merge(:count => 2))
22
+ end
23
+
24
+ def test_translate_proc_with_interpolate
25
+ setup_proc_translations
26
+ assert_equal 'bar baz foo', I18n.backend.translate('en', :lambda_for_interpolate, :foo => 'foo', :bar => 'bar', :baz => 'baz')
27
+ end
28
+
29
+ def test_translate_with_proc_as_default
30
+ expected = 'result from lambda'
31
+ assert_equal expected, I18n.backend.translate(:en, :'does not exist', :default => lambda { |key, values| expected })
32
+ end
33
+
34
+ private
35
+
36
+ def setup_proc_translations
37
+ I18n.backend.store_translations 'en', {
38
+ :a_lambda => lambda { |key, values|
39
+ values.keys.sort_by(&:to_s).collect { |key| "#{key}=#{values[key]}"}.join(', ')
40
+ },
41
+ :lambda_for_pluralize => lambda { |key, values| values },
42
+ :lambda_for_interpolate => lambda { |key, values|
43
+ "{{#{values.keys.sort_by(&:to_s).join('}} {{')}}}"
44
+ }
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
data/test/api/link.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Link
5
+ def test_translate_calls_translate_if_resolves_to_a_symbol
6
+ setup_linked_translations
7
+ assert_equal 'foo', I18n.backend.translate('en', :link_to_foo)
8
+ end
9
+
10
+ def test_translate_calls_translate_if_resolves_to_a_symbol2
11
+ setup_linked_translations
12
+ assert_equal('baz', I18n.backend.translate('en', :link_to_baz))
13
+ end
14
+
15
+ def test_translate_calls_translate_if_resolves_to_a_symbol3
16
+ setup_linked_translations
17
+ assert I18n.backend.translate('en', :link_to_bar).key?(:baz)
18
+ end
19
+
20
+ def test_translate_calls_translate_if_resolves_to_a_symbol_with_scope_1
21
+ setup_linked_translations
22
+ assert_equal('baz', I18n.backend.translate('en', :link_to_baz, :scope => :bar))
23
+ end
24
+
25
+ def test_translate_calls_translate_if_resolves_to_a_symbol_with_scope_1
26
+ setup_linked_translations
27
+ assert_equal('buz', I18n.backend.translate('en', :'bar.link_to_buz'))
28
+ end
29
+
30
+ private
31
+
32
+ def setup_linked_translations
33
+ I18n.backend.store_translations 'en', {
34
+ :foo => 'foo',
35
+ :bar => { :baz => 'baz', :link_to_baz => :baz, :link_to_buz => :'boz.buz' },
36
+ :boz => { :buz => 'buz' },
37
+ :link_to_foo => :foo,
38
+ :link_to_bar => :bar,
39
+ :link_to_baz => :'bar.baz'
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Localization
5
+ module Date
6
+ # TODO should be Mrz, shouldn't it?
7
+ def test_localize_given_the_short_format_it_uses_it
8
+ assert_equal '01. Mar', I18n.backend.localize('de', date, :short)
9
+ end
10
+
11
+ def test_localize_given_the_long_format_it_uses_it
12
+ assert_equal '01. März 2008', I18n.backend.localize('de', date, :long)
13
+ end
14
+
15
+ def test_localize_given_the_default_format_it_uses_it
16
+ assert_equal '01.03.2008', I18n.backend.localize('de', date, :default)
17
+ end
18
+
19
+ def test_localize_given_a_day_name_format_it_returns_a_day_name
20
+ assert_equal 'Samstag', I18n.backend.localize('de', date, '%A')
21
+ end
22
+
23
+ def test_localize_given_an_abbr_day_name_format_it_returns_an_abbrevated_day_name
24
+ assert_equal 'Sa', I18n.backend.localize('de', date, '%a')
25
+ end
26
+
27
+ def test_localize_given_a_month_name_format_it_returns_a_month_name
28
+ assert_equal 'März', I18n.backend.localize('de', date, '%B')
29
+ end
30
+
31
+ # TODO should be Mrz, shouldn't it?
32
+ def test_localize_given_an_abbr_month_name_format_it_returns_an_abbrevated_month_name
33
+ assert_equal 'Mar', I18n.backend.localize('de', date, '%b')
34
+ end
35
+
36
+ def test_localize_given_a_format_specified_as_a_proc
37
+ assert_equal '1ter März 2008', I18n.backend.localize('de', date, :long_ordinalized)
38
+ end
39
+
40
+ def test_localize_given_a_format_specified_as_a_proc_with_additional_options
41
+ assert_equal '1ter März 2008 (MEZ)', I18n.backend.localize('de', date, :long_ordinalized, :timezone => 'MEZ')
42
+ end
43
+
44
+ def test_localize_given_no_format_it_does_not_fail
45
+ assert_nothing_raised{ I18n.backend.localize 'de', date }
46
+ end
47
+
48
+ def test_localize_given_an_unknown_format_it_does_not_fail
49
+ assert_nothing_raised{ I18n.backend.localize 'de', date, '%x' }
50
+ end
51
+
52
+ def test_localize_nil_raises_argument_error
53
+ assert_raises(I18n::ArgumentError) { I18n.backend.localize 'de', nil }
54
+ end
55
+
56
+ def test_localize_object_raises_argument_error
57
+ assert_raises(I18n::ArgumentError) { I18n.backend.localize 'de', Object.new }
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,61 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Localization
5
+ module DateTime
6
+ # TODO should be Mrz, shouldn't it?
7
+ def test_localize_given_the_short_format_it_uses_it
8
+ assert_equal '01. Mar 06:00', I18n.backend.localize('de', morning_datetime, :short)
9
+ end
10
+
11
+ def test_localize_given_the_long_format_it_uses_it
12
+ assert_equal '01. März 2008 06:00', I18n.backend.localize('de', morning_datetime, :long)
13
+ end
14
+
15
+ # TODO should be Mrz, shouldn't it?
16
+ def test_localize_given_the_default_format_it_uses_it
17
+ assert_equal 'Sa, 01. Mar 2008 06:00:00 +0000', I18n.backend.localize('de', morning_datetime, :default)
18
+ end
19
+
20
+ def test_localize_given_a_day_name_format_it_returns_the_correct_day_name
21
+ assert_equal 'Samstag', I18n.backend.localize('de', morning_datetime, '%A')
22
+ end
23
+
24
+ def test_localize_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name
25
+ assert_equal 'Sa', I18n.backend.localize('de', morning_datetime, '%a')
26
+ end
27
+
28
+ def test_localize_given_a_month_name_format_it_returns_the_correct_month_name
29
+ assert_equal 'März', I18n.backend.localize('de', morning_datetime, '%B')
30
+ end
31
+
32
+ # TODO should be Mrz, shouldn't it?
33
+ def test_localize_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name
34
+ assert_equal 'Mar', I18n.backend.localize('de', morning_datetime, '%b')
35
+ end
36
+
37
+ def test_localize_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator
38
+ assert_equal 'am', I18n.backend.localize('de', morning_datetime, '%p')
39
+ assert_equal 'pm', I18n.backend.localize('de', evening_datetime, '%p')
40
+ end
41
+
42
+ def test_localize_given_a_format_specified_as_a_proc
43
+ assert_equal '1ter März 2008, 06:00 Uhr', I18n.backend.localize('de', morning_datetime, :long_ordinalized)
44
+ end
45
+
46
+ def test_localize_given_a_format_specified_as_a_proc_with_additional_options
47
+ assert_equal '1ter März 2008, 06:00 Uhr (MEZ)', I18n.backend.localize('de', morning_datetime, :long_ordinalized, :timezone => 'MEZ')
48
+ end
49
+
50
+ def test_localize_given_no_format_it_does_not_fail
51
+ assert_nothing_raised{ I18n.backend.localize 'de', morning_datetime }
52
+ end
53
+
54
+ def test_localize_given_an_unknown_format_it_does_not_fail
55
+ assert_nothing_raised{ I18n.backend.localize 'de', morning_datetime, '%x' }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,24 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Localization
5
+ module Lambda
6
+ def test_localize_uses_lambda_day_names
7
+ assert_match /Суббота/, I18n.backend.localize('ru', time, "%A, %d %B")
8
+ assert_match /суббота/, I18n.backend.localize('ru', time, "%d %B (%A)")
9
+ end
10
+
11
+ def test_localize_uses_lambda_month_names
12
+ assert_match /марта/, I18n.backend.localize('ru', time, "%d %B %Y")
13
+ assert_match /Март/, I18n.backend.localize('ru', time, "%B %Y")
14
+ end
15
+
16
+ def test_localize_uses_lambda_abbr_day_names
17
+ assert_match /марта/, I18n.backend.localize('ru', time, "%d %b %Y")
18
+ assert_match /март/, I18n.backend.localize('ru', time, "%b %Y")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,61 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Localization
5
+ module Time
6
+ # TODO should be Mrz, shouldn't it?
7
+ def test_localize_given_the_short_format_it_uses_it
8
+ assert_equal '01. Mar 06:00', I18n.backend.localize('de', morning_time, :short)
9
+ end
10
+
11
+ def test_localize_given_the_long_format_it_uses_it
12
+ assert_equal '01. März 2008 06:00', I18n.backend.localize('de', morning_time, :long)
13
+ end
14
+
15
+ # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this?
16
+ # def test_localize_given_the_default_format_it_uses_it
17
+ # assert_equal 'Sa, 01. Mar 2008 06:00:00 +0000', I18n.backend.localize('de', morning_time, :default)
18
+ # end
19
+
20
+ def test_localize_given_a_day_name_format_it_returns_the_correct_day_name
21
+ assert_equal 'Samstag', I18n.backend.localize('de', morning_time, '%A')
22
+ end
23
+
24
+ def test_localize_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name
25
+ assert_equal 'Sa', I18n.backend.localize('de', morning_time, '%a')
26
+ end
27
+
28
+ def test_localize_given_a_month_name_format_it_returns_the_correct_month_name
29
+ assert_equal 'März', I18n.backend.localize('de', morning_time, '%B')
30
+ end
31
+
32
+ # TODO should be Mrz, shouldn't it?
33
+ def test_localize_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name
34
+ assert_equal 'Mar', I18n.backend.localize('de', morning_time, '%b')
35
+ end
36
+
37
+ def test_localize_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator
38
+ assert_equal 'am', I18n.backend.localize('de', morning_time, '%p')
39
+ assert_equal 'pm', I18n.backend.localize('de', evening_time, '%p')
40
+ end
41
+
42
+ def test_localize_given_a_format_specified_as_a_proc
43
+ assert_equal '1ter März 2008, 06:00 Uhr', I18n.backend.localize('de', morning_time, :long_ordinalized)
44
+ end
45
+
46
+ def test_localize_given_a_format_specified_as_a_proc_with_additional_options
47
+ assert_equal '1ter März 2008, 06:00 Uhr (MEZ)', I18n.backend.localize('de', morning_time, :long_ordinalized, :timezone => 'MEZ')
48
+ end
49
+
50
+ def test_localize_given_no_format_it_does_not_fail
51
+ assert_nothing_raised{ I18n.backend.localize 'de', morning_time }
52
+ end
53
+
54
+ def test_localize_given_an_unknown_format_it_does_not_fail
55
+ assert_nothing_raised{ I18n.backend.localize 'de', morning_time, '%x' }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Pluralization
5
+ def test_pluralize_given_0_returns_zero_string_if_zero_key_given
6
+ assert_equal 'zero', I18n.t(:default => { :zero => 'zero' }, :count => 0)
7
+ end
8
+
9
+ def test_pluralize_given_0_returns_plural_string_if_no_zero_key_given
10
+ assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 0)
11
+ end
12
+
13
+ def test_pluralize_given_1_returns_singular_string
14
+ assert_equal 'bar', I18n.t(:default => { :one => 'bar' }, :count => 1)
15
+ end
16
+
17
+ def test_pluralize_given_2_returns_plural_string
18
+ assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 2)
19
+ end
20
+
21
+ def test_pluralize_given_3_returns_plural_string
22
+ assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 3)
23
+ end
24
+
25
+ def test_pluralize_given_nil_returns_the_given_entry
26
+ assert_equal({ :zero => 'zero' }, I18n.t(:default => { :zero => 'zero' }, :count => nil))
27
+ end
28
+
29
+ def test_interpolate_given_incomplete_pluralization_data_raises_invalid_pluralization_data
30
+ assert_raises(I18n::InvalidPluralizationData){ I18n.t(:default => { :one => 'bar' }, :count => 2) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ module Tests
2
+ module Backend
3
+ module Api
4
+ module Translation
5
+ def translate(key, options = {})
6
+ I18n.backend.translate('en', key, options)
7
+ end
8
+
9
+ def test_given_no_keys_it_returns_the_default
10
+ assert_equal 'default', translate(nil, :default => 'default')
11
+ end
12
+
13
+ def test_translate_given_a_symbol_as_a_default_translates_the_symbol
14
+ assert_equal 'bar', translate(nil, :default => :'foo.bar')
15
+ end
16
+
17
+ def test_translate_default_with_scope_stays_in_scope_when_looking_up_the_symbol
18
+ assert_equal 'bar', translate(:missing, :default => :bar, :scope => :foo)
19
+ end
20
+
21
+ def test_translate_given_an_array_as_default_uses_the_first_match
22
+ assert_equal 'bar', translate(:does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :bar])
23
+ end
24
+
25
+ def test_translate_given_an_array_of_inexistent_keys_it_raises_missing_translation_data
26
+ assert_raises I18n::MissingTranslationData do
27
+ translate(:does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :does_not_exist_3])
28
+ end
29
+ end
30
+
31
+ def test_translate_an_array_of_keys_translates_all_of_them
32
+ assert_equal %w(bar baz), translate([:bar, :baz], :scope => [:foo])
33
+ end
34
+
35
+ def test_translate_with_a_missing_key_and_no_default_raises_missing_translation_data
36
+ assert_raises(I18n::MissingTranslationData) do
37
+ translate(:missing)
38
+ end
39
+ end
40
+
41
+ def test_translate_given_nil_as_a_locale_raises_an_argument_error
42
+ assert_raises(I18n::InvalidLocale) do
43
+ I18n.backend.translate(nil, :bar)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
3
+ require 'i18n/backend/active_record'
4
+
5
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
6
+ ActiveRecord::Schema.define(:version => 1) do
7
+ create_table :translations do |t|
8
+ t.string :locale
9
+ t.string :key
10
+ t.string :value
11
+ t.boolean :proc
12
+ end
13
+ end
14
+
15
+ class I18nActiveRecordBackendApiBasicsTest < Test::Unit::TestCase
16
+ include Tests::Backend::ActiveRecord::Setup::Base
17
+ include Tests::Backend::Api::Basics
18
+ end
19
+
20
+ class I18nActiveRecordBackendApiTranslateTest < Test::Unit::TestCase
21
+ include Tests::Backend::ActiveRecord::Setup::Base
22
+ include Tests::Backend::Api::Translation
23
+ end
24
+
25
+ class I18nActiveRecordBackendApiInterpolateTest < Test::Unit::TestCase
26
+ include Tests::Backend::ActiveRecord::Setup::Base
27
+ include Tests::Backend::Api::Interpolation
28
+ end
29
+
30
+ class I18nActiveRecordBackendApiLambdaTest < Test::Unit::TestCase
31
+ include Tests::Backend::ActiveRecord::Setup::Base
32
+ include Tests::Backend::Api::Lambda
33
+ end
34
+
35
+ class I18nActiveRecordBackendApiTranslateLinkedTest < Test::Unit::TestCase
36
+ include Tests::Backend::ActiveRecord::Setup::Base
37
+ include Tests::Backend::Api::Link
38
+ end
39
+
40
+ class I18nActiveRecordBackendApiPluralizationTest < Test::Unit::TestCase
41
+ include Tests::Backend::ActiveRecord::Setup::Base
42
+ include Tests::Backend::Api::Pluralization
43
+ end
44
+
45
+ class I18nActiveRecordBackendApiLocalizeDateTest < Test::Unit::TestCase
46
+ include Tests::Backend::ActiveRecord::Setup::Localization
47
+ include Tests::Backend::Api::Localization::Date
48
+ end
49
+
50
+ class I18nActiveRecordBackendApiLocalizeDateTimeTest < Test::Unit::TestCase
51
+ include Tests::Backend::ActiveRecord::Setup::Localization
52
+ include Tests::Backend::Api::Localization::DateTime
53
+ end
54
+
55
+ class I18nActiveRecordBackendApiLocalizeTimeTest < Test::Unit::TestCase
56
+ include Tests::Backend::ActiveRecord::Setup::Localization
57
+ include Tests::Backend::Api::Localization::Time
58
+ end
59
+
60
+ class I18nActiveRecordBackendApiLocalizeLambdaTest < Test::Unit::TestCase
61
+ include Tests::Backend::ActiveRecord::Setup::Localization
62
+ include Tests::Backend::Api::Localization::Lambda
63
+ end
64
+