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