multi_formal_i18n_tenancy 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc CHANGED
@@ -6,25 +6,22 @@ This gem extend the standard i18n backend (I18n::Backend::Simple) to introduce {
6
6
 
7
7
  <b>1 ) *_formal.yml</b>
8
8
 
9
- Given you want to offer your users the option to be addressed formally or informally through a session locale switch:
9
+ Given you want to offer your users the option to be addressed formally or informally through a {session locale switch}[http://https://github.com/Applicat/multi_formal_i18n_tenancy#Backend Locale Switch]:
10
10
 
11
- I18n.locale = :de # or :de_formal
11
+ Then this is a {DRY}[http://en.wikipedia.org/wiki/Don%27t_repeat_yourself] solution for the workaround about having duplication of de locales in the de_formal namespace even though informal & formal translation are the same.
12
12
 
13
- So this is a {DRY}[http://en.wikipedia.org/wiki/Don%27t_repeat_yourself] solution for the workaround about having duplication of de locales in the de_formal namespace even though informal & formal translation are the same.
14
-
15
- This locale file owns all translations from its base *.yml and lets you override them through the same translation keys (except of the deviant locale namespaces de and de_formal at the beginning).
13
+ This locale file will own all translations from its base *.yml and lets you override them through the same translation keys (except of the deviant locale namespaces de and de_formal at the beginning).
16
14
 
17
15
  <b>2 ) #{locales_path}/tenants/your_tenant_name/**/your_tenant_name.yml</b>
18
16
 
19
- Given you want to have tenant specific locales through a session locale switch:
20
-
21
- # ('Your Tenant Name'.parameterize.gsub('-', '_') + '_de').to_sym == :your_tenant_name_de
22
- I18n.locale :your_tenant_name_de # or :your_tenant_name_de_formal
17
+ Given you want to have tenant specific locales through a {session locale switch}[http://https://github.com/Applicat/multi_formal_i18n_tenancy#Backend Locale Switch]:
23
18
 
24
19
  <b>Precondition:</b> Assure that you recursively add locale files to i18n's locale path e.g. through your Rails 3 application.rb:
25
20
 
26
21
  config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
27
22
 
23
+ You may like to enable multi tenancy for your rails application by deploying the repository branch on multiple servers each with a different tenant-specific application constants file to load by a config system like {rails_config}[https://github.com/railsjedi/rails_config] at initialization.
24
+
28
25
  This locale file owns all translations from its base *.yml and optional *_formal.yml under #{locales_path}/tenants/your-tenant-name/ (recurively) and lets you override them through the same translation keys (except of the deviant locale namespaces de and de_formal at the beginning).
29
26
 
30
27
  == Installation
@@ -39,6 +36,55 @@ OPTIONAL: you have to manually set the i18n backend for your non-rails Ruby appl
39
36
 
40
37
  I18n.backend = MultiFormalI18nTenancy::Backend.new
41
38
 
39
+ == Backend Locale Switch
40
+
41
+ Available since version 0.0.3.
42
+
43
+ <b>Initializer</b>
44
+
45
+ To initialize the global default locale based on current configuration.
46
+
47
+ Put this file for instance under config/initializer/multi_tenancy.rb
48
+
49
+ I18n.locale = I18n.backend.available_locale(
50
+ formal: Settings.address_formally, tenant: Settings.tenant.name
51
+ )
52
+
53
+ Settings is a constant for your settings brought by most popular rack app config system: https://github.com/railsjedi/rails_config
54
+
55
+ I18n.locale = I18n.backend.available_locale(formal: Settings.formal, tenant: Settings.tenant.name)
56
+
57
+ <b>Controller</b>
58
+
59
+ Dependency: https://github.com/iain/http_accept_language
60
+
61
+ class ApplicationController
62
+ AVAILABLE_LOCALES = %w{de en}
63
+
64
+ before_filter :set_locale
65
+
66
+ private
67
+
68
+ def set_locale
69
+ if user_signed_in?
70
+ # use of devise helper methods: user_signed_in?, current_user
71
+ I18n.locale = current_user.language # you have to add a language string column to your schema
72
+ else
73
+ locale = request.preferred_language_from AVAILABLE_LOCALES
74
+ locale ||= request.compatible_language_from AVAILABLE_LOCALES
75
+ locale ||= I18n.default_locale
76
+
77
+ unless Settings.tenant.name == 'global'
78
+ locale = ("#{Settings.tenant.name}_#{locale}").to_sym
79
+ end
80
+ end
81
+
82
+ I18n.locale = I18n.backend.available_locale(
83
+ base_locale: locale, formal: Settings.formal_address, tenant: Settings.tenant.name
84
+ )
85
+ end
86
+ end
87
+
42
88
  == Compatibility
43
89
 
44
90
  Tested on MacOS with: Rails 3.1 & Ruby 1.9.2, Rails 3.2.6 & Ruby 1.9.3.
@@ -6,7 +6,7 @@ class MultiFormalI18nTenancy::Backend < I18n::Backend::Simple
6
6
  TENANT_LOCALE_PATTERN = /tenants$/
7
7
 
8
8
  attr_accessor :filenames
9
-
9
+
10
10
  # Accepts a list of paths to translation files. Loads translations from
11
11
  # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
12
12
  # for details.
@@ -64,7 +64,9 @@ class MultiFormalI18nTenancy::Backend < I18n::Backend::Simple
64
64
  base_locale.gsub!(FORMAL_LOCALE_PATTERN, '')
65
65
  end
66
66
 
67
- translations[locale] = (translations[base_locale.to_sym] || {}).clone
67
+ unless tenant && locale.to_s.match(FORMAL_LOCALE_PATTERN)
68
+ translations[locale] = (translations[base_locale.to_sym] || {}).clone
69
+ end
68
70
  else
69
71
  translations[locale] ||= {}
70
72
  end
@@ -74,6 +76,46 @@ class MultiFormalI18nTenancy::Backend < I18n::Backend::Simple
74
76
  translations[locale].deep_merge!(data)
75
77
  end
76
78
 
79
+ def available_locale(options = {})
80
+ options.assert_valid_keys(:base_locale, :formal, :tenant) if options.respond_to? :assert_valid_keys
81
+
82
+ base_locale = options[:base_locale] || I18n.default_locale
83
+ formal = options[:formal] || false
84
+ tenant = (options[:tenant] || '').parameterize.gsub('-', '_')
85
+
86
+ deepest_available_locale = nil
87
+
88
+ # take last / deepest available locale of possible combinations
89
+ variant_index = -1
90
+
91
+ [
92
+ [
93
+ (formal && tenant),
94
+ [tenant, base_locale, 'formal']
95
+ ],
96
+ [
97
+ formal && tenant && available_locales.include?([tenant, base_locale].join('_').to_sym) &&
98
+ available_locales.include?([base_locale, 'formal'].join('_').to_sym),
99
+ [tenant, base_locale, 'formal']
100
+ ],
101
+ [tenant, [tenant, base_locale]],
102
+ [formal, [base_locale, 'formal']],
103
+ [true, [base_locale]],
104
+ ].each do |variant|
105
+ variant_index += 1
106
+
107
+ next unless variant[0]
108
+
109
+ if available_locales.include?(variant[1].join('_').to_sym) || (variant_index == 1 && [tenant, base_locale, 'formal'] == variant[1])
110
+ deepest_available_locale = variant[1].join('_').to_sym
111
+ end
112
+
113
+ break if deepest_available_locale
114
+ end
115
+
116
+ deepest_available_locale || I18n.default_locale
117
+ end
118
+
77
119
  private
78
120
 
79
121
  def tenant_from_locale?(locale)
@@ -1,3 +1,3 @@
1
1
  module MultiFormalI18nTenancy
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'i18n'
1
+ require 'active_support/core_ext/string/inflections'
2
2
 
3
3
  module MultiFormalI18nTenancy
4
4
  end
@@ -0,0 +1,4 @@
1
+ de:
2
+ devise:
3
+ example: 'Dein Beispiel'
4
+ informal_example: 'Dein anderes Beispiel'
@@ -0,0 +1,4 @@
1
+ de_formal:
2
+ devise:
3
+ example: Ihr Beispiel
4
+ examples: Ihre Beispiele
@@ -0,0 +1,2 @@
1
+ en:
2
+ example: 'Dummy'
@@ -0,0 +1,2 @@
1
+ another_tenant_name_fr_formal:
2
+ formal_available: 'jamais vous'
@@ -0,0 +1,3 @@
1
+ your_tenant_name_de_formal:
2
+ devise:
3
+ example: Ihr formelles Beispiel
@@ -0,0 +1,2 @@
1
+ your_tenant_name_en:
2
+ example: 'Dummy change'
@@ -7,16 +7,17 @@ require 'multi_formal_i18n_tenancy'
7
7
 
8
8
  describe MultiFormalI18nTenancy::Backend do
9
9
  before :all do
10
- I18n.backend = MultiFormalI18nTenancy::Backend.new
11
- I18n.load_path = [
12
- File.expand_path('../../../fixtures/de.yml', __FILE__),
13
- File.expand_path('../../../fixtures/de_formal.yml', __FILE__),
14
- File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de.yml', __FILE__),
15
- File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml', __FILE__)
16
- ]
10
+ I18n.default_locale = :de
11
+ I18n.locale = I18n.default_locale
17
12
  end
18
13
 
19
- describe '.translate' do
14
+ describe '#load_translations' do
15
+ include_context :all_locale_file_constellations
16
+
17
+ before :all do
18
+ I18n.backend = MultiFormalI18nTenancy::Backend.new
19
+ end
20
+
20
21
  context 'default' do
21
22
  context 'formal translation available' do
22
23
  it 'returns the formal translation' do
@@ -52,6 +53,140 @@ describe MultiFormalI18nTenancy::Backend do
52
53
  I18n.t('formal_unavailable_again').should == 'Du auch wieder'
53
54
  end
54
55
  end
56
+
57
+ context 'formal translation available for locale without base locale' do
58
+ it 'inits the locale without inheritation of unavailable base locale' do
59
+ I18n.locale = :another_tenant_name_fr_formal
60
+ I18n.t('formal_available').should == 'jamais vous'
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'locale files with more than 1 dot' do
66
+ it 'principally works' do
67
+ I18n.locale = :de
68
+ I18n.t('devise.example').should == 'Dein Beispiel'
69
+ I18n.locale = :de_formal
70
+ I18n.t('devise.example').should == 'Ihr Beispiel'
71
+ I18n.locale = :your_tenant_name_de_formal
72
+ I18n.t('devise.informal_example').should == 'Dein anderes Beispiel' # devise.de.yml
73
+ I18n.t('devise.examples').should == 'Ihre Beispiele' # devise.de_formal.yml
74
+ I18n.t('devise.example').should == 'Ihr formelles Beispiel' # tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '.available_locale' do
80
+ context 'any locale' do
81
+ before :all do
82
+ I18n.load_path = [
83
+ File.expand_path('../../../fixtures/de.yml', __FILE__),
84
+ File.expand_path('../../../fixtures/en.yml', __FILE__)
85
+ ]
86
+ end
87
+
88
+ # .available_locale any locale principally works
89
+ it 'principally works' do
90
+ I18n.backend = MultiFormalI18nTenancy::Backend.new
91
+
92
+ [
93
+ [{}, :de],
94
+ [{base_locale: :en}, :en],
95
+ [{base_locale: :unavailable}, :de],
96
+ [{tenant: 'Your Tenant Name'}, :de],
97
+ [{formal: true, tenant: 'Your Tenant Name'}, :de],
98
+ [{formal: true, tenant: 'Unavailable Tenant Name'}, :de]
99
+ ].each do |variant|
100
+ actual = I18n.backend.available_locale(variant.first)
101
+ actual.should(
102
+ be(variant.last),
103
+ "input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
104
+ )
105
+ end
106
+ end
107
+ end
108
+
109
+ context 'any formal locale' do
110
+ before :all do
111
+ I18n.load_path = [
112
+ File.expand_path('../../../fixtures/de.yml', __FILE__),
113
+ File.expand_path('../../../fixtures/de_formal.yml', __FILE__)
114
+ ]
115
+ end
116
+
117
+ it 'principally works' do
118
+ I18n.backend = MultiFormalI18nTenancy::Backend.new
119
+
120
+ [
121
+ [{formal: true}, :de_formal],
122
+ [{formal: true, tenant: 'Your Tenant Name'}, :de_formal]
123
+ ].each do |variant|
124
+ actual = I18n.backend.available_locale(variant.first)
125
+ actual.should(
126
+ be(variant.last),
127
+ "input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
128
+ )
129
+ end
130
+ end
131
+ end
132
+
133
+ context 'any formal locale plus tenant specific locale' do
134
+ before :all do
135
+ I18n.load_path = [
136
+ File.expand_path('../../../fixtures/de.yml', __FILE__),
137
+ File.expand_path('../../../fixtures/de_formal.yml', __FILE__),
138
+ File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de.yml', __FILE__),
139
+ File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_en.yml', __FILE__)
140
+ ]
141
+ end
142
+
143
+ it 'principally works' do
144
+ I18n.backend = MultiFormalI18nTenancy::Backend.new
145
+
146
+ [
147
+ [{formal: true}, :de_formal],
148
+ # should be :your_tenant_name_de_formal even though there is no :your_tenant_name_de_formal file
149
+ # but the base file :de_formal to inherit from
150
+ [{formal: true, tenant: 'Your Tenant Name'}, :your_tenant_name_de_formal],
151
+ [{base_locale: :en, formal: true, tenant: 'Your Tenant Name'}, :your_tenant_name_en]
152
+ ].each do |variant|
153
+ actual = I18n.backend.available_locale(variant.first)
154
+ actual.should(
155
+ be(variant.last),
156
+ "input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
157
+ )
158
+ end
159
+ end
160
+ end
161
+
162
+ context 'any formal tenant-specific locale' do
163
+ include_context :all_locale_file_constellations
164
+
165
+ it 'principally works' do
166
+ I18n.backend = MultiFormalI18nTenancy::Backend.new
167
+ end
168
+ end
169
+
170
+ context 'tenant has special locale without an equivalent base locale' do
171
+ before :all do
172
+ I18n.load_path = [
173
+ File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml', __FILE__)
174
+ ]
175
+ end
176
+
177
+ it 'principally works' do
178
+ I18n.backend = MultiFormalI18nTenancy::Backend.new
179
+
180
+ [
181
+ [{base_locale: :de, formal: true, tenant: 'Your Tenant Name'}, :your_tenant_name_de_formal]
182
+ ].each do |variant|
183
+ actual = I18n.backend.available_locale(variant.first)
184
+ actual.should(
185
+ be(variant.last),
186
+ "input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
187
+ )
188
+ end
189
+ end
55
190
  end
56
191
  end
57
192
  end
@@ -0,0 +1,14 @@
1
+ shared_context :all_locale_file_constellations do
2
+ before :all do
3
+ I18n.load_path = [
4
+ File.expand_path('../../../fixtures/de.yml', __FILE__),
5
+ File.expand_path('../../../fixtures/de_formal.yml', __FILE__),
6
+ File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de.yml', __FILE__),
7
+ File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml', __FILE__),
8
+ File.expand_path('../../../fixtures/tenants/another_tenant_name/another_tenant_name_fr_formal.yml', __FILE__),
9
+ File.expand_path('../../../fixtures/devise.de.yml', __FILE__),
10
+ File.expand_path('../../../fixtures/devise.de_formal.yml', __FILE__),
11
+ File.expand_path('../../../fixtures/tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml', __FILE__),
12
+ ]
13
+ end
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multi_formal_i18n_tenancy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-12 00:00:00.000000000 Z
12
+ date: 2012-07-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: i18n
16
- requirement: &70230785180340 !ruby/object:Gem::Requirement
15
+ name: activesupport
16
+ requirement: &70220249428440 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70230785180340
24
+ version_requirements: *70220249428440
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70230785178460 !ruby/object:Gem::Requirement
27
+ requirement: &70220249426980 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: 2.11.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70230785178460
35
+ version_requirements: *70220249426980
36
36
  description: Your formal locales will inherit translations from their base locale
37
37
  and locales stored in an enterprise folder can override base + formal translations
38
38
  email:
@@ -50,11 +50,18 @@ files:
50
50
  - README.rdoc
51
51
  - spec/fixtures/de.yml
52
52
  - spec/fixtures/de_formal.yml
53
+ - spec/fixtures/devise.de.yml
54
+ - spec/fixtures/devise.de_formal.yml
55
+ - spec/fixtures/en.yml
56
+ - spec/fixtures/tenants/another_tenant_name/another_tenant_name_fr_formal.yml
57
+ - spec/fixtures/tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml
53
58
  - spec/fixtures/tenants/your_tenant_name/your_tenant_name_de.yml
54
59
  - spec/fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml
60
+ - spec/fixtures/tenants/your_tenant_name/your_tenant_name_en.yml
55
61
  - spec/lib/multi_formal_i18n_tenancy/backend_spec.rb
56
62
  - spec/spec_helper.rb
57
63
  - spec/support/deferred_garbage_collection.rb
64
+ - spec/support/shared_contexts/all_locale_file_constellations.rb
58
65
  homepage: http://applicat.github.com/multi_formal_i18n_tenancy
59
66
  licenses: []
60
67
  post_install_message:
@@ -69,7 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
76
  version: '0'
70
77
  segments:
71
78
  - 0
72
- hash: 2892429864419253687
79
+ hash: 2083026653359506385
73
80
  required_rubygems_version: !ruby/object:Gem::Requirement
74
81
  none: false
75
82
  requirements:
@@ -78,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
85
  version: '0'
79
86
  segments:
80
87
  - 0
81
- hash: 2892429864419253687
88
+ hash: 2083026653359506385
82
89
  requirements: []
83
90
  rubyforge_project:
84
91
  rubygems_version: 1.8.17
@@ -89,8 +96,15 @@ summary: Your formal locales will inherit translations from their base locale an
89
96
  test_files:
90
97
  - spec/fixtures/de.yml
91
98
  - spec/fixtures/de_formal.yml
99
+ - spec/fixtures/devise.de.yml
100
+ - spec/fixtures/devise.de_formal.yml
101
+ - spec/fixtures/en.yml
102
+ - spec/fixtures/tenants/another_tenant_name/another_tenant_name_fr_formal.yml
103
+ - spec/fixtures/tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml
92
104
  - spec/fixtures/tenants/your_tenant_name/your_tenant_name_de.yml
93
105
  - spec/fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml
106
+ - spec/fixtures/tenants/your_tenant_name/your_tenant_name_en.yml
94
107
  - spec/lib/multi_formal_i18n_tenancy/backend_spec.rb
95
108
  - spec/spec_helper.rb
96
109
  - spec/support/deferred_garbage_collection.rb
110
+ - spec/support/shared_contexts/all_locale_file_constellations.rb