i18n_multitenant 0.0.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +99 -0
- data/Rakefile +27 -0
- data/lib/i18n_multitenant.rb +3 -0
- data/lib/i18n_multitenant/backend.rb +134 -0
- data/lib/i18n_multitenant/railtie.rb +6 -0
- data/lib/i18n_multitenant/version.rb +3 -0
- data/spec/fixtures/de.yml +4 -0
- data/spec/fixtures/de_formal.yml +3 -0
- data/spec/fixtures/devise.de.yml +4 -0
- data/spec/fixtures/devise.de_formal.yml +4 -0
- data/spec/fixtures/en.yml +2 -0
- data/spec/fixtures/tenants/another_tenant_name/another_tenant_name_fr_formal.yml +2 -0
- data/spec/fixtures/tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml +3 -0
- data/spec/fixtures/tenants/your_tenant_name/your_tenant_name_de.yml +3 -0
- data/spec/fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml +2 -0
- data/spec/fixtures/tenants/your_tenant_name/your_tenant_name_en.yml +2 -0
- data/spec/lib/i18n_multitenant/backend_spec.rb +204 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/shared_contexts/all_locale_file_constellations.rb +14 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eb35d5985cf6d4869028f5370b636016011059be
|
4
|
+
data.tar.gz: b47174e2024be883a1a0b25a63c6678acb7af154
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 80ccc546b9f990086bbabcf9213d0ec2d0a47afed41f7092b4a8572e44a889658a7cc31f2a6d1cdcca9e6b27e6119f1cc63053b2547b3b8e6e5dcacf684054e3
|
7
|
+
data.tar.gz: 849ea28b7d3fb78006cddbff7e4f6f53aeb1da8f5d551dc1f2c925d7f03c4f7598fc930011132c1473897b79170a62a68a7506221641313531668f09ca23d9b8
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2017 Máximo Mussini
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# I18nMultitenant
|
2
|
+
|
3
|
+
This gem extend the standard i18n backend (I18n::Backend::Simple) to introduce {Conventions over configuration}[http://en.wikipedia.org/wiki/Convention_over_configuration] about 2 new types of locale files.
|
4
|
+
|
5
|
+
WARNING: Have to rethink parts of the implementation which seem to reimplement I18n::Backend::Fallbacks without this nice to have convention over configuration "algorithm".
|
6
|
+
|
7
|
+
So you can also write this with a little help of i18n's basic fallback module (at bootstrap process):
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
11
|
+
|
12
|
+
I18n.fallbacks[:es] = [:es]
|
13
|
+
I18n.fallbacks[:tenant_name_es] = [:tenant_name_es, :es]
|
14
|
+
```
|
15
|
+
|
16
|
+
This gem will just save you these lines through a convenient convention over configuration "algorithm".
|
17
|
+
|
18
|
+
## Conventions
|
19
|
+
|
20
|
+
### *_formal.yml
|
21
|
+
|
22
|
+
Given you want to offer your users the option to be addressed formally or informally through a {session locale switch}[https://github.com/Applicat/i18n_multitenant#backend-locale-switch]:
|
23
|
+
|
24
|
+
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.
|
25
|
+
|
26
|
+
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).
|
27
|
+
|
28
|
+
### #{locales_path}/tenants/your_tenant_name/**/your_tenant_name.yml
|
29
|
+
|
30
|
+
Given you want to have tenant specific locales through a {session locale switch}[https://github.com/Applicat/i18n_multitenant#backend-locale-switch]:
|
31
|
+
|
32
|
+
<b>Precondition:</b> Assure that you recursively add locale files to i18n's locale path e.g. through your Rails 3 application.rb:
|
33
|
+
|
34
|
+
config.i18n.load_path +# Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
|
35
|
+
|
36
|
+
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.
|
37
|
+
|
38
|
+
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).
|
39
|
+
|
40
|
+
## Installation
|
41
|
+
|
42
|
+
Add this to your Gemfile and run the +bundle+ command.
|
43
|
+
|
44
|
+
gem "i18n_multitenant"
|
45
|
+
|
46
|
+
The gem (# engine) will automatically set the backend for your Rails application.
|
47
|
+
|
48
|
+
OPTIONAL: you have to manually set the i18n backend for your non-rails Ruby application:
|
49
|
+
|
50
|
+
I18n.backend # I18nMultitenant::Backend.new
|
51
|
+
|
52
|
+
## Backend Locale Switch
|
53
|
+
|
54
|
+
Available since version 0.0.3.
|
55
|
+
|
56
|
+
<b>Initializer</b>
|
57
|
+
|
58
|
+
To initialize the global default locale based on current configuration.
|
59
|
+
|
60
|
+
Put this file for instance under config/initializer/multi_tenancy.rb
|
61
|
+
|
62
|
+
I18n.locale # I18n.backend.available_locale(
|
63
|
+
formal: Settings.address_formally, tenant: Settings.tenant.name
|
64
|
+
)
|
65
|
+
|
66
|
+
Settings is a constant for your settings brought by most popular rack app config system: https://github.com/railsjedi/rails_config
|
67
|
+
|
68
|
+
I18n.locale # I18n.backend.available_locale(formal: Settings.formal, tenant: Settings.tenant.name)
|
69
|
+
|
70
|
+
<b>Controller</b>
|
71
|
+
|
72
|
+
Dependency: https://github.com/iain/http_accept_language
|
73
|
+
|
74
|
+
class ApplicationController
|
75
|
+
AVAILABLE_LOCALES # %w{de en}
|
76
|
+
|
77
|
+
before_filter :set_locale
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def set_locale
|
82
|
+
if user_signed_in?
|
83
|
+
# use of devise helper methods: user_signed_in?, current_user
|
84
|
+
I18n.locale # current_user.language # you have to add a language string column to your schema
|
85
|
+
else
|
86
|
+
locale # request.preferred_language_from AVAILABLE_LOCALES
|
87
|
+
locale ||# request.compatible_language_from AVAILABLE_LOCALES
|
88
|
+
locale ||# I18n.default_locale
|
89
|
+
|
90
|
+
unless Settings.tenant.name ## 'global'
|
91
|
+
locale # ("#{Settings.tenant.name.downcase.strip.tr(' ', '_')}_#{locale}").to_sym
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
I18n.locale # I18n.backend.available_locale(
|
96
|
+
base_locale: locale, formal: Settings.formal_address, tenant: Settings.tenant.name
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
5
|
+
require 'i18n_multitenant/version'
|
6
|
+
|
7
|
+
name = 'i18n_multitenant'
|
8
|
+
version = I18nMultitenant::VERSION
|
9
|
+
|
10
|
+
task :gem => :build
|
11
|
+
task :build do
|
12
|
+
system "gem build #{name}.gemspec"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :install => :build do
|
16
|
+
system "sudo gem install #{name}-#{version}.gem"
|
17
|
+
end
|
18
|
+
|
19
|
+
task :release => :build do
|
20
|
+
system "git tag -a v#{version} -m 'Tagging #{version}'"
|
21
|
+
system 'git push --tags'
|
22
|
+
system "gem push #{name}-#{version}.gem"
|
23
|
+
system "rm #{name}-#{version}.gem"
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new(:spec)
|
27
|
+
task :default => :spec
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
class I18nMultitenant::Backend < I18n::Backend::Simple
|
4
|
+
FORMAL_FILENAME_PATTERN = /_formal\.[a-zA-Z]+$/
|
5
|
+
FORMAL_LOCALE_PATTERN = /_formal$/
|
6
|
+
|
7
|
+
TENANT_FILENAME_PATTERN = /tenants/
|
8
|
+
TENANT_LOCALE_PATTERN = /tenants$/
|
9
|
+
|
10
|
+
attr_accessor :filenames
|
11
|
+
|
12
|
+
# Accepts a list of paths to translation files. Loads translations from
|
13
|
+
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
|
14
|
+
# for details.
|
15
|
+
def load_translations(*filenames)
|
16
|
+
filenames = I18n.load_path if filenames.empty?
|
17
|
+
|
18
|
+
@filenames = filenames.flatten
|
19
|
+
|
20
|
+
@filenames.each { |filename| load_file(filename) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def available_locale(options = {})
|
24
|
+
options.assert_valid_keys(:base_locale, :formal, :tenant) if options.respond_to? :assert_valid_keys
|
25
|
+
|
26
|
+
base_locale = options[:base_locale] || I18n.default_locale
|
27
|
+
formal = options[:formal] || false
|
28
|
+
tenant = (options[:tenant] || '').downcase.strip.tr(' ', '_')
|
29
|
+
|
30
|
+
deepest_available_locale = nil
|
31
|
+
|
32
|
+
# take last / deepest available locale of possible combinations
|
33
|
+
variant_index = -1
|
34
|
+
|
35
|
+
[
|
36
|
+
[
|
37
|
+
(formal && tenant),
|
38
|
+
[tenant, base_locale, 'formal']
|
39
|
+
],
|
40
|
+
[
|
41
|
+
formal && tenant && available_locales.include?([tenant, base_locale].join('_').to_sym) &&
|
42
|
+
available_locales.include?([base_locale, 'formal'].join('_').to_sym),
|
43
|
+
[tenant, base_locale, 'formal']
|
44
|
+
],
|
45
|
+
[tenant, [tenant, base_locale]],
|
46
|
+
[formal, [base_locale, 'formal']],
|
47
|
+
[true, [base_locale]],
|
48
|
+
].each do |variant|
|
49
|
+
variant_index += 1
|
50
|
+
|
51
|
+
next unless variant[0]
|
52
|
+
|
53
|
+
if available_locales.include?(variant[1].join('_').to_sym) || (variant_index == 1 && [tenant, base_locale, 'formal'] == variant[1])
|
54
|
+
deepest_available_locale = variant[1].join('_').to_sym
|
55
|
+
end
|
56
|
+
|
57
|
+
break if deepest_available_locale
|
58
|
+
end
|
59
|
+
|
60
|
+
deepest_available_locale || I18n.default_locale
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# Looks up a translation from the translations hash. Returns nil if
|
66
|
+
# eiher key is nil, or locale, scope or key do not exist as a key in the
|
67
|
+
# nested translations hash. Splits keys or scopes containing dots
|
68
|
+
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
|
69
|
+
# <tt>%w(currency format)</tt>.
|
70
|
+
def lookup(locale, key, scope = [], options = {})
|
71
|
+
init_translations unless initialized?
|
72
|
+
|
73
|
+
# only formal address: [:de, :de_formal]
|
74
|
+
# only multi tenancy: [:de, :tenant_name_de]
|
75
|
+
# formal address & multi tenancy: [:de, :tenant_name_de, :de_formal, :tenant_name_de_formal]
|
76
|
+
|
77
|
+
locales = []
|
78
|
+
|
79
|
+
base_locale = locale.to_s.gsub(FORMAL_LOCALE_PATTERN, '')
|
80
|
+
|
81
|
+
tenant = tenant_from_locale?(locale)
|
82
|
+
base_locale.gsub!(/^#{tenant}_/, '') if tenant
|
83
|
+
|
84
|
+
locales << base_locale.to_sym
|
85
|
+
|
86
|
+
if locale.to_s.match(FORMAL_LOCALE_PATTERN) && tenant && locale.to_s.match(/^#{tenant}_/)
|
87
|
+
locales << locale.to_s.gsub(FORMAL_LOCALE_PATTERN, '').to_sym
|
88
|
+
locales << locale.to_s.gsub(/^#{tenant}_/, '').to_sym
|
89
|
+
end
|
90
|
+
|
91
|
+
locales << locale unless locales.include?(locale)
|
92
|
+
|
93
|
+
entry, last_entry = nil, nil
|
94
|
+
|
95
|
+
locales.each do |locale|
|
96
|
+
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
|
97
|
+
|
98
|
+
entry = keys.inject(translations) do |result, _key|
|
99
|
+
_key = _key.to_sym
|
100
|
+
|
101
|
+
unless result.is_a?(Hash) && result.has_key?(_key)
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
result = result[_key]
|
105
|
+
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
|
106
|
+
result
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
if entry.nil?
|
111
|
+
entry = last_entry
|
112
|
+
else
|
113
|
+
last_entry = entry
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
entry
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def tenant_from_locale?(locale)
|
123
|
+
tenant = locale.to_s.gsub(FORMAL_LOCALE_PATTERN, '').split('_')
|
124
|
+
|
125
|
+
if tenant.length > 2
|
126
|
+
tenant.pop # pop languages like de
|
127
|
+
tenant = tenant.join('_')
|
128
|
+
else
|
129
|
+
tenant = nil
|
130
|
+
end
|
131
|
+
|
132
|
+
tenant && @filenames.select{|f| f.match("/tenants/#{tenant}/")}.any? ? tenant : nil
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe I18nMultitenant::Backend do
|
4
|
+
before :all do
|
5
|
+
I18n.config.enforce_available_locales = false
|
6
|
+
I18n.default_locale = :de
|
7
|
+
I18n.locale = I18n.default_locale
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#lookup' do
|
11
|
+
include_context :all_locale_file_constellations
|
12
|
+
|
13
|
+
before :all do
|
14
|
+
I18n.backend = I18nMultitenant::Backend.new
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'default' do
|
18
|
+
context 'formal translation available' do
|
19
|
+
context 'returns the translation' do
|
20
|
+
Given { I18n.locale = :de }
|
21
|
+
Then { I18n.t('formal_available') == 'Du' }
|
22
|
+
Then { I18n.t('de_formal_formal_available') == 'Du' }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'returns the formal translation' do
|
26
|
+
Given { I18n.locale = :de_formal }
|
27
|
+
Then { I18n.t('formal_available') == 'Sie' }
|
28
|
+
Then { I18n.t('de_formal_formal_available') == 'Sie' }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'formal translation unavailable' do
|
33
|
+
context 'inerits the informal locales from :de' do
|
34
|
+
Given { I18n.locale = :de_formal }
|
35
|
+
Then { I18n.t('formal_unavailable') == 'Du' }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'tenant-specific' do
|
41
|
+
context 'formal translation available' do
|
42
|
+
context 'returns the translation' do
|
43
|
+
Given { I18n.locale = :your_tenant_name_de }
|
44
|
+
Then { I18n.t('formal_available') == 'Du auch' }
|
45
|
+
Then { I18n.t('de_formal_formal_available') == 'Du' }
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'returns the formal translation' do
|
49
|
+
Given { I18n.locale = :your_tenant_name_de_formal }
|
50
|
+
Then { I18n.t('formal_available') == 'Sie auch' }
|
51
|
+
Then { I18n.t('de_formal_formal_available') == 'Sie' }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'formal translation unavailable' do
|
56
|
+
context 'inerits the informal locales from :de and :your_tenant_name_de' do
|
57
|
+
Given { I18n.locale = :your_tenant_name_de_formal }
|
58
|
+
Then { I18n.t('formal_unavailable') == 'Du' }
|
59
|
+
Then { I18n.t('formal_unavailable_again') == 'Du auch wieder' }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'formal translation available for locale without base locale' do
|
64
|
+
context 'inits the locale without inheritation of unavailable base locale' do
|
65
|
+
Given { I18n.locale = :another_tenant_name_fr_formal }
|
66
|
+
Then { I18n.t('formal_available') == 'jamais vous' }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'locale files with more than 1 dot' do
|
72
|
+
context 'principally works' do
|
73
|
+
Given { I18n.locale = :de }
|
74
|
+
Then { I18n.t('devise.example') == 'Dein Beispiel' }
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'principally works' do
|
78
|
+
Given { I18n.locale = :de_formal }
|
79
|
+
Then { I18n.t('devise.example') == 'Ihr Beispiel' }
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'principally works tenant' do
|
83
|
+
Given { I18n.locale = :your_tenant_name_de_formal }
|
84
|
+
Then { I18n.t('devise.informal_example') == 'Dein anderes Beispiel' } # devise.de.yml
|
85
|
+
Then { I18n.t('devise.examples') == 'Ihre Beispiele' } # devise.de_formal.yml
|
86
|
+
Then { I18n.t('devise.example') == 'Ihr formelles Beispiel' } # tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '.available_locale' do
|
92
|
+
context 'any locale' do
|
93
|
+
before :all do
|
94
|
+
I18n.load_path = [
|
95
|
+
File.expand_path('../../../fixtures/de.yml', __FILE__),
|
96
|
+
File.expand_path('../../../fixtures/en.yml', __FILE__)
|
97
|
+
]
|
98
|
+
end
|
99
|
+
|
100
|
+
# .available_locale any locale principally works
|
101
|
+
it 'principally works' do
|
102
|
+
I18n.backend = I18nMultitenant::Backend.new
|
103
|
+
|
104
|
+
[
|
105
|
+
[{}, :de],
|
106
|
+
[{base_locale: :en}, :en],
|
107
|
+
[{base_locale: :unavailable}, :de],
|
108
|
+
[{tenant: 'Your Tenant Name'}, :de],
|
109
|
+
[{formal: true, tenant: 'Your Tenant Name'}, :de],
|
110
|
+
[{formal: true, tenant: 'Unavailable Tenant Name'}, :de]
|
111
|
+
].each do |variant|
|
112
|
+
actual = I18n.backend.available_locale(variant.first)
|
113
|
+
expect(actual).to(
|
114
|
+
be(variant.last),
|
115
|
+
"input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'any formal locale' do
|
122
|
+
before :all do
|
123
|
+
I18n.load_path = [
|
124
|
+
File.expand_path('../../../fixtures/de.yml', __FILE__),
|
125
|
+
File.expand_path('../../../fixtures/de_formal.yml', __FILE__)
|
126
|
+
]
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'principally works' do
|
130
|
+
I18n.backend = I18nMultitenant::Backend.new
|
131
|
+
|
132
|
+
[
|
133
|
+
[{formal: true}, :de_formal],
|
134
|
+
[{formal: true, tenant: 'Your Tenant Name'}, :de_formal]
|
135
|
+
].each do |variant|
|
136
|
+
actual = I18n.backend.available_locale(variant.first)
|
137
|
+
expect(actual).to(
|
138
|
+
be(variant.last),
|
139
|
+
"input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'any formal locale plus tenant specific locale' do
|
146
|
+
before :all do
|
147
|
+
I18n.load_path = [
|
148
|
+
File.expand_path('../../../fixtures/de.yml', __FILE__),
|
149
|
+
File.expand_path('../../../fixtures/de_formal.yml', __FILE__),
|
150
|
+
File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de.yml', __FILE__),
|
151
|
+
File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_en.yml', __FILE__)
|
152
|
+
]
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'principally works' do
|
156
|
+
I18n.backend = I18nMultitenant::Backend.new
|
157
|
+
|
158
|
+
[
|
159
|
+
[{formal: true}, :de_formal],
|
160
|
+
# should be :your_tenant_name_de_formal even though there is no :your_tenant_name_de_formal file
|
161
|
+
# but the base file :de_formal to inherit from
|
162
|
+
[{formal: true, tenant: 'Your Tenant Name'}, :your_tenant_name_de_formal],
|
163
|
+
[{base_locale: :en, formal: true, tenant: 'Your Tenant Name'}, :your_tenant_name_en]
|
164
|
+
].each do |variant|
|
165
|
+
actual = I18n.backend.available_locale(variant.first)
|
166
|
+
expect(actual).to(
|
167
|
+
be(variant.last),
|
168
|
+
"input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
|
169
|
+
)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'any formal tenant-specific locale' do
|
175
|
+
include_context :all_locale_file_constellations
|
176
|
+
|
177
|
+
it 'principally works' do
|
178
|
+
I18n.backend = I18nMultitenant::Backend.new
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'tenant has special locale without an equivalent base locale' do
|
183
|
+
before :all do
|
184
|
+
I18n.load_path = [
|
185
|
+
File.expand_path('../../../fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml', __FILE__)
|
186
|
+
]
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'principally works' do
|
190
|
+
I18n.backend = I18nMultitenant::Backend.new
|
191
|
+
|
192
|
+
[
|
193
|
+
[{base_locale: :de, formal: true, tenant: 'Your Tenant Name'}, :your_tenant_name_de_formal]
|
194
|
+
].each do |variant|
|
195
|
+
actual = I18n.backend.available_locale(variant.first)
|
196
|
+
expect(actual).to(
|
197
|
+
be(variant.last),
|
198
|
+
"input #{variant.first.inspect} should result in the output: #{variant.last.inspect} but got #{actual.inspect}"
|
199
|
+
)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
if ENV['CODECLIMATE_REPO_TOKEN']
|
3
|
+
require 'codeclimate-test-reporter'
|
4
|
+
CodeClimate::TestReporter.start
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rspec/given'
|
8
|
+
require 'pry'
|
9
|
+
require 'i18n_multitenant'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.raise_errors_for_deprecations!
|
13
|
+
config.expose_dsl_globally = false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Load Support files
|
17
|
+
Dir['./spec/support/**/*.rb'].each do |f|
|
18
|
+
require f
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.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
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: i18n_multitenant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Máximo Mussini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: i18n
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: I18nMultitenant allows you to specify translations that are tenant-specific,
|
28
|
+
falling back to the base locale.
|
29
|
+
email:
|
30
|
+
- maximomussini@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- CHANGELOG.md
|
36
|
+
- LICENSE.txt
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- lib/i18n_multitenant.rb
|
40
|
+
- lib/i18n_multitenant/backend.rb
|
41
|
+
- lib/i18n_multitenant/railtie.rb
|
42
|
+
- lib/i18n_multitenant/version.rb
|
43
|
+
- spec/fixtures/de.yml
|
44
|
+
- spec/fixtures/de_formal.yml
|
45
|
+
- spec/fixtures/devise.de.yml
|
46
|
+
- spec/fixtures/devise.de_formal.yml
|
47
|
+
- spec/fixtures/en.yml
|
48
|
+
- spec/fixtures/tenants/another_tenant_name/another_tenant_name_fr_formal.yml
|
49
|
+
- spec/fixtures/tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml
|
50
|
+
- spec/fixtures/tenants/your_tenant_name/your_tenant_name_de.yml
|
51
|
+
- spec/fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml
|
52
|
+
- spec/fixtures/tenants/your_tenant_name/your_tenant_name_en.yml
|
53
|
+
- spec/lib/i18n_multitenant/backend_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
- spec/support/shared_contexts/all_locale_file_constellations.rb
|
56
|
+
homepage: https://github.com/ElMassimo/i18n_multitenant
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options:
|
62
|
+
- "--charset=UTF-8"
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.0
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.5.1
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Translation for multi-tenant applications.
|
81
|
+
test_files:
|
82
|
+
- spec/fixtures/de.yml
|
83
|
+
- spec/fixtures/de_formal.yml
|
84
|
+
- spec/fixtures/devise.de.yml
|
85
|
+
- spec/fixtures/devise.de_formal.yml
|
86
|
+
- spec/fixtures/en.yml
|
87
|
+
- spec/fixtures/tenants/another_tenant_name/another_tenant_name_fr_formal.yml
|
88
|
+
- spec/fixtures/tenants/your_tenant_name/devise.your_tenant_name_de_formal.yml
|
89
|
+
- spec/fixtures/tenants/your_tenant_name/your_tenant_name_de.yml
|
90
|
+
- spec/fixtures/tenants/your_tenant_name/your_tenant_name_de_formal.yml
|
91
|
+
- spec/fixtures/tenants/your_tenant_name/your_tenant_name_en.yml
|
92
|
+
- spec/lib/i18n_multitenant/backend_spec.rb
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- spec/support/shared_contexts/all_locale_file_constellations.rb
|