multi_formal_i18n_tenancy 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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