singleton-client 0.7.5 → 0.7.6
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 +4 -4
- data/README.md +27 -28
- data/lib/sgtn-client/api/source.rb +7 -65
- data/lib/sgtn-client/api/t.rb +16 -17
- data/lib/sgtn-client/api/translation.rb +77 -122
- data/lib/sgtn-client/cldr/localized_str.rb +1 -0
- data/lib/sgtn-client/common/data.rb +30 -0
- data/lib/sgtn-client/common/single_operation.rb +34 -0
- data/lib/sgtn-client/core/cache.rb +12 -81
- data/lib/sgtn-client/core/config.rb +38 -2
- data/lib/sgtn-client/exceptions.rb +6 -0
- data/lib/sgtn-client/loader/cache.rb +71 -0
- data/lib/sgtn-client/loader/chain_loader.rb +49 -0
- data/lib/sgtn-client/loader/consts.rb +15 -0
- data/lib/sgtn-client/loader/loader_factory.rb +33 -0
- data/lib/sgtn-client/loader/local_translation.rb +49 -0
- data/lib/sgtn-client/loader/server.rb +94 -0
- data/lib/sgtn-client/loader/single_loader.rb +48 -0
- data/lib/sgtn-client/loader/source.rb +56 -0
- data/lib/sgtn-client/loader/source_comparer.rb +58 -0
- data/lib/sgtn-client/sgtn-client.rb +2 -0
- data/lib/sgtn-client/util/cache-util.rb +30 -38
- data/lib/sgtn-client/util/locale-util.rb +62 -38
- data/lib/sgtn-client/util/string-util.rb +12 -0
- data/lib/sgtn-client/util/validate-util.rb +1 -6
- data/lib/singleton-client.rb +15 -0
- data/lib/singleton-ruby.rb +2 -0
- metadata +43 -147
- data/lib/sgtn-client/core/request.rb +0 -24
- data/lib/sgtn-client/util/file-util.rb +0 -37
@@ -3,9 +3,15 @@
|
|
3
3
|
|
4
4
|
require 'erb'
|
5
5
|
require 'yaml'
|
6
|
+
require 'observer'
|
6
7
|
|
7
8
|
module SgtnClient
|
8
9
|
#include Exceptions
|
10
|
+
|
11
|
+
module TranslationLoader
|
12
|
+
autoload :LoaderFactory, 'sgtn-client/loader/loader_factory'
|
13
|
+
end
|
14
|
+
|
9
15
|
module Configuration
|
10
16
|
|
11
17
|
def config
|
@@ -34,6 +40,7 @@ module SgtnClient
|
|
34
40
|
|
35
41
|
|
36
42
|
class Config
|
43
|
+
extend Observable
|
37
44
|
|
38
45
|
attr_accessor :username, :password, :signature, :app_id, :cert_path,
|
39
46
|
:token, :token_secret, :subject,
|
@@ -42,7 +49,7 @@ module SgtnClient
|
|
42
49
|
:mode, :endpoint, :merchant_endpoint, :platform_endpoint, :ipn_endpoint,
|
43
50
|
:rest_endpoint, :rest_token_endpoint, :client_id, :client_secret,
|
44
51
|
:openid_endpoint, :openid_redirect_uri, :openid_client_id, :openid_client_secret,
|
45
|
-
:verbose_logging, :product_name, :version, :vip_server,
|
52
|
+
:verbose_logging, :product_name, :version, :vip_server,
|
46
53
|
:translation_bundle, :source_bundle, :cache_expiry_period, :disable_cache, :default_language
|
47
54
|
|
48
55
|
|
@@ -152,6 +159,36 @@ module SgtnClient
|
|
152
159
|
Logging.logger
|
153
160
|
end
|
154
161
|
|
162
|
+
|
163
|
+
def loader
|
164
|
+
@loader ||= begin
|
165
|
+
config = SgtnClient::Config.configurations[SgtnClient::Config.default_environment]
|
166
|
+
SgtnClient::TranslationLoader::LoaderFactory.create(config)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def available_bundles
|
171
|
+
loader.available_bundles
|
172
|
+
rescue StandardError => e
|
173
|
+
SgtnClient.logger.error 'failed to get available bundles'
|
174
|
+
SgtnClient.logger.error e
|
175
|
+
Set.new
|
176
|
+
end
|
177
|
+
|
178
|
+
def available_locales
|
179
|
+
bundles = available_bundles
|
180
|
+
return Set.new if bundles.nil? || bundles.empty?
|
181
|
+
|
182
|
+
unless bundles.respond_to?(:locales)
|
183
|
+
def bundles.locales
|
184
|
+
@locales ||= reduce(Set.new) { |locales, id| locales << id.locale }
|
185
|
+
end
|
186
|
+
changed
|
187
|
+
notify_observers(:available_locales)
|
188
|
+
end
|
189
|
+
bundles.locales
|
190
|
+
end
|
191
|
+
|
155
192
|
private
|
156
193
|
# Read configurations from the given file name
|
157
194
|
# === Arguments
|
@@ -161,7 +198,6 @@ module SgtnClient
|
|
161
198
|
erb.filename = file_name
|
162
199
|
YAML.load(erb.result)
|
163
200
|
end
|
164
|
-
|
165
201
|
|
166
202
|
end
|
167
203
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2022 VMware, Inc.
|
4
|
+
# SPDX-License-Identifier: EPL-2.0
|
5
|
+
|
6
|
+
module SgtnClient
|
7
|
+
autoload :CacheUtil, 'sgtn-client/util/cache-util'
|
8
|
+
|
9
|
+
module TranslationLoader
|
10
|
+
autoload :CONSTS, 'sgtn-client/loader/consts'
|
11
|
+
|
12
|
+
module Cache # :nodoc:
|
13
|
+
# get from cache, return expired data immediately
|
14
|
+
def get_bundle(component, locale)
|
15
|
+
SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
|
16
|
+
|
17
|
+
key = SgtnClient::CacheUtil.get_cachekey(component, locale)
|
18
|
+
cache_item = SgtnClient::CacheUtil.get_cache(key)
|
19
|
+
if cache_item
|
20
|
+
if SgtnClient::CacheUtil.is_expired(cache_item)
|
21
|
+
Thread.new do # TODO: Use one thread # refresh in background
|
22
|
+
begin
|
23
|
+
load_bundle(component, locale)
|
24
|
+
rescue StandardError => e
|
25
|
+
SgtnClient.logger.error "an error occured while loading bundle: component=#{component}, locale=#{locale}"
|
26
|
+
SgtnClient.logger.error e
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return cache_item.dig(:items)
|
31
|
+
end
|
32
|
+
|
33
|
+
load_bundle(component, locale) # refresh synchronously if not in cache
|
34
|
+
end
|
35
|
+
|
36
|
+
# load and save to cache
|
37
|
+
def load_bundle(component, locale)
|
38
|
+
SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
|
39
|
+
|
40
|
+
key = SgtnClient::CacheUtil.get_cachekey(component, locale)
|
41
|
+
item = super
|
42
|
+
SgtnClient::CacheUtil.write_cache(key, item) if item
|
43
|
+
item
|
44
|
+
end
|
45
|
+
|
46
|
+
def available_bundles
|
47
|
+
SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}]"
|
48
|
+
|
49
|
+
cache_item = SgtnClient::CacheUtil.get_cache(CONSTS::AVAILABLE_BUNDLES_KEY)
|
50
|
+
if cache_item
|
51
|
+
if SgtnClient::CacheUtil.is_expired(cache_item)
|
52
|
+
Thread.new do # TODO: Use one thread
|
53
|
+
begin
|
54
|
+
item = super
|
55
|
+
SgtnClient::CacheUtil.write_cache(CONSTS::AVAILABLE_BUNDLES_KEY, item) if item
|
56
|
+
rescue StandardError => e
|
57
|
+
SgtnClient.logger.error 'an error occured while loading available bundles.'
|
58
|
+
SgtnClient.logger.error e
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
return cache_item.dig(:items)
|
63
|
+
end
|
64
|
+
|
65
|
+
item = super
|
66
|
+
SgtnClient::CacheUtil.write_cache(CONSTS::AVAILABLE_BUNDLES_KEY, item) if item
|
67
|
+
item
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
module TranslationLoader
|
6
|
+
class Chain
|
7
|
+
attr_accessor :loaders
|
8
|
+
|
9
|
+
def initialize(*loaders)
|
10
|
+
self.loaders = loaders
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_bundle(component, locale)
|
14
|
+
exception = nil
|
15
|
+
|
16
|
+
loaders.each do |loader|
|
17
|
+
begin
|
18
|
+
bundle = loader.load_bundle(component, locale)
|
19
|
+
return bundle if bundle
|
20
|
+
rescue StandardError => e
|
21
|
+
exception = e
|
22
|
+
SgtnClient.logger.error "[#{__FILE__}][#{__callee__}] {component: #{component},locale: #{locale}}, failed on #{loader.class}: #{e}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
raise exception || SgtnClient::SingletonError.new("can't load component: #{component}, locale: #{locale}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def available_bundles
|
30
|
+
exception = nil
|
31
|
+
total_data = Set.new
|
32
|
+
|
33
|
+
loaders.each do |loader|
|
34
|
+
begin
|
35
|
+
item = loader.available_bundles
|
36
|
+
total_data += item
|
37
|
+
rescue StandardError => e
|
38
|
+
exception = e
|
39
|
+
SgtnClient.logger.error "[#{__FILE__}][#{__callee__}] failed on #{loader.class}: #{e}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
raise exception if total_data.empty? && exception
|
44
|
+
|
45
|
+
total_data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2022 VMware, Inc.
|
4
|
+
# SPDX-License-Identifier: EPL-2.0
|
5
|
+
|
6
|
+
module SgtnClient
|
7
|
+
module TranslationLoader
|
8
|
+
module CONSTS
|
9
|
+
OLD_SOURCE_LOCALE = 'old_source'
|
10
|
+
REAL_SOURCE_LOCALE = 'latest'
|
11
|
+
|
12
|
+
AVAILABLE_BUNDLES_KEY = 'available_bundles'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
module TranslationLoader
|
6
|
+
autoload :Source, 'sgtn-client/loader/source'
|
7
|
+
autoload :SgtnServer, 'sgtn-client/loader/server'
|
8
|
+
autoload :LocalTranslation, 'sgtn-client/loader/local_translation'
|
9
|
+
autoload :Chain, 'sgtn-client/loader/chain_loader'
|
10
|
+
autoload :SourceComparer, 'sgtn-client/loader/source_comparer'
|
11
|
+
autoload :SingleLoader, 'sgtn-client/loader/single_loader'
|
12
|
+
autoload :Cache, 'sgtn-client/loader/cache'
|
13
|
+
|
14
|
+
module LoaderFactory
|
15
|
+
def self.create(config)
|
16
|
+
SgtnClient.logger.info "[#{method(__callee__).owner}.#{__callee__}] config=#{config}"
|
17
|
+
|
18
|
+
loaders = []
|
19
|
+
loaders << Source.new(config) if config['source_bundle']
|
20
|
+
loaders << SgtnServer.new(config) if config['vip_server']
|
21
|
+
loaders << LocalTranslation.new(config) if config['translation_bundle']
|
22
|
+
raise SgtnClient::SingletonError, 'no translation is available!' if loaders.empty?
|
23
|
+
|
24
|
+
chain_loader = Class.new(Chain)
|
25
|
+
chain_loader.include SourceComparer
|
26
|
+
chain_loader.include SingleLoader
|
27
|
+
chain_loader.include Cache
|
28
|
+
|
29
|
+
chain_loader.new(*loaders)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module SgtnClient
|
8
|
+
module Common
|
9
|
+
autoload :BundleID, 'sgtn-client/common/data'
|
10
|
+
end
|
11
|
+
|
12
|
+
module TranslationLoader
|
13
|
+
autoload :CONSTS, 'sgtn-client/loader/consts'
|
14
|
+
|
15
|
+
class LocalTranslation
|
16
|
+
BUNDLE_PREFIX = 'messages_'.freeze
|
17
|
+
BUNDLE_SUFFIX = '.json'.freeze
|
18
|
+
|
19
|
+
def initialize(config)
|
20
|
+
@base_path = Pathname.new(config['translation_bundle']) + config['product_name'] + config['version'].to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_bundle(component, locale)
|
24
|
+
SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component=#{component}, locale=#{locale}"
|
25
|
+
|
26
|
+
file_name = BUNDLE_PREFIX + locale + BUNDLE_SUFFIX
|
27
|
+
file_path = @base_path + component + file_name
|
28
|
+
|
29
|
+
bundle_data = JSON.parse(File.read(file_path))
|
30
|
+
messages = bundle_data['messages']
|
31
|
+
|
32
|
+
raise SgtnClient::SingletonError, "no messages in local bundle file: #{file_path}." unless messages
|
33
|
+
|
34
|
+
messages
|
35
|
+
end
|
36
|
+
|
37
|
+
def available_bundles
|
38
|
+
SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}]"
|
39
|
+
|
40
|
+
@available_bundles ||= begin
|
41
|
+
@base_path.glob('*/*.json').reduce(Set.new) do |bundles, f|
|
42
|
+
locale = f.basename.to_s.sub(/\A#{BUNDLE_PREFIX}/i, '').sub(/#{BUNDLE_SUFFIX}\z/i, '')
|
43
|
+
bundles.add Common::BundleID.new(f.parent.basename.to_s, locale)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2022 VMware, Inc.
|
4
|
+
# SPDX-License-Identifier: EPL-2.0
|
5
|
+
|
6
|
+
require 'faraday'
|
7
|
+
require 'faraday_middleware'
|
8
|
+
|
9
|
+
module SgtnClient
|
10
|
+
module Common
|
11
|
+
autoload :BundleID, 'sgtn-client/common/data'
|
12
|
+
end
|
13
|
+
|
14
|
+
module TranslationLoader
|
15
|
+
autoload :CONSTS, 'sgtn-client/loader/consts'
|
16
|
+
|
17
|
+
class SgtnServer
|
18
|
+
ERROR_ILLEGAL_DATA = 'server returned illegal data.'
|
19
|
+
ERROR_BUSINESS_ERROR = 'server returned business error.'
|
20
|
+
|
21
|
+
REQUEST_ARGUMENTS = { timeout: 10 }.freeze
|
22
|
+
|
23
|
+
def initialize(config)
|
24
|
+
@server_url = config['vip_server']
|
25
|
+
|
26
|
+
product_root = format('/i18n/api/v2/translation/products/%s/versions/%s', config['product_name'], config['version'])
|
27
|
+
|
28
|
+
@bundle_url = "#{product_root}/locales/%s/components/%s"
|
29
|
+
@locales_url = "#{product_root}/localelist"
|
30
|
+
@components_url = "#{product_root}/componentlist"
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_bundle(component, locale)
|
34
|
+
SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component=#{component}, locale=#{locale}"
|
35
|
+
|
36
|
+
messages = query_server(format(@bundle_url, locale, component), ['messages'])
|
37
|
+
messages
|
38
|
+
end
|
39
|
+
|
40
|
+
def available_bundles
|
41
|
+
SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}]"
|
42
|
+
|
43
|
+
components_thread = Thread.new { available_components }
|
44
|
+
available_locales.reduce(Set.new) do |bundles, locale|
|
45
|
+
components_thread.value.reduce(bundles) do |inner_bundles, component|
|
46
|
+
inner_bundles << Common::BundleID.new(component, locale)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def available_locales
|
54
|
+
query_server(@locales_url, ['locales'])
|
55
|
+
end
|
56
|
+
|
57
|
+
def available_components
|
58
|
+
query_server(@components_url, ['components'])
|
59
|
+
end
|
60
|
+
|
61
|
+
def query_server(url, path_to_data = [], queries = nil, headers = nil)
|
62
|
+
conn = Faraday.new(@server_url, request: REQUEST_ARGUMENTS) do |f|
|
63
|
+
f.response :json # decode response bodies as JSON
|
64
|
+
f.use :gzip
|
65
|
+
f.response :raise_error
|
66
|
+
f.response :logger, SgtnClient.logger, { log_level: :debug }
|
67
|
+
end
|
68
|
+
resp = conn.get(url, queries, headers)
|
69
|
+
|
70
|
+
process_business_error(resp.body)
|
71
|
+
extract_data(resp.body, path_to_data)
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_data(parsedbody, path_to_data)
|
75
|
+
data = parsedbody.dig('data', *path_to_data)
|
76
|
+
raise SgtnClient::SingletonError, "no expected data in response. Body is: #{parsedbody}" unless data
|
77
|
+
|
78
|
+
data
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_business_error(parsedbody)
|
82
|
+
b_code = parsedbody.dig('response', 'code')
|
83
|
+
unless b_code >= 200 && b_code < 300 || b_code >= 600 && b_code < 700
|
84
|
+
raise SgtnClient::SingletonError, "#{ERROR_BUSINESS_ERROR} #{parsedbody['response']}"
|
85
|
+
end
|
86
|
+
|
87
|
+
# 600 means a successful response, 6xx means partial successful.
|
88
|
+
SgtnClient.logger.warn "#{ERROR_BUSINESS_ERROR} #{parsedbody['response']}" if b_code > 600
|
89
|
+
rescue TypeError, ArgumentError, NoMethodError => e
|
90
|
+
raise SgtnClient::SingletonError, "#{ERROR_ILLEGAL_DATA} #{e}. Body is: #{parsedbody}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
autoload :SingleOperation, 'sgtn-client/common/single_operation'
|
6
|
+
autoload :CacheUtil, 'sgtn-client/util/cache-util'
|
7
|
+
|
8
|
+
module TranslationLoader
|
9
|
+
autoload :CONSTS, 'sgtn-client/loader/consts'
|
10
|
+
|
11
|
+
module SingleLoader
|
12
|
+
def load_bundle(component, locale)
|
13
|
+
SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
|
14
|
+
|
15
|
+
@single_bundle_loader ||= single_loader { |c, l| super(c, l) }
|
16
|
+
id = CacheUtil.get_cachekey(component, locale)
|
17
|
+
@single_bundle_loader.operate(id, component, locale)&.value
|
18
|
+
end
|
19
|
+
|
20
|
+
def available_bundles
|
21
|
+
SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}]"
|
22
|
+
|
23
|
+
@single_available_bundles_loader ||= single_loader { super }
|
24
|
+
@single_available_bundles_loader.operate(CONSTS::AVAILABLE_BUNDLES_KEY)&.value
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def single_loader(&block)
|
30
|
+
loader = nil
|
31
|
+
none_alive = proc { |_, thread| thread.nil? }
|
32
|
+
creator = proc do |id, _, *args|
|
33
|
+
Thread.new do
|
34
|
+
SgtnClient.logger.debug "start single loading #{id}"
|
35
|
+
begin
|
36
|
+
block.call(*args)
|
37
|
+
ensure
|
38
|
+
# delete thread from hash after finish
|
39
|
+
loader.remove_object(id)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
loader = SgtnClient::SingleOperation.new(none_alive, &creator)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module SgtnClient
|
8
|
+
module Common
|
9
|
+
autoload :BundleID, 'sgtn-client/common/data'
|
10
|
+
end
|
11
|
+
|
12
|
+
module TranslationLoader
|
13
|
+
autoload :CONSTS, 'sgtn-client/loader/consts'
|
14
|
+
|
15
|
+
class Source
|
16
|
+
def initialize(config)
|
17
|
+
@source_bundle_path = Pathname.new(config['source_bundle'])
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_bundle(component, locale = nil)
|
21
|
+
return if locale && locale != CONSTS::REAL_SOURCE_LOCALE # return when NOT querying source
|
22
|
+
|
23
|
+
SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component=#{component}"
|
24
|
+
|
25
|
+
total_messages = {}
|
26
|
+
|
27
|
+
(@source_bundle_path + component).glob('**/*.{yml, yaml}') do |f|
|
28
|
+
bundle = YAML.load(File.read(f))
|
29
|
+
messages = bundle&.first&.last # TODO: Warn about inconsistent source locale
|
30
|
+
if messages.is_a?(Hash)
|
31
|
+
total_messages.merge!(messages)
|
32
|
+
else
|
33
|
+
SgtnClient.logger.error "[#{method(__callee__).owner}.#{__callee__}] invalid bundle data in #{f}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise SgtnClient::SingletonError, "no local source messages for component #{component}" if total_messages.empty?
|
38
|
+
|
39
|
+
total_messages
|
40
|
+
end
|
41
|
+
|
42
|
+
def available_bundles
|
43
|
+
SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}]"
|
44
|
+
|
45
|
+
@available_bundles ||= begin
|
46
|
+
@source_bundle_path.children.select(&:directory?).reduce(Set.new) do |bundles, component|
|
47
|
+
component.glob('**/*.{yml, yaml}') do |_|
|
48
|
+
bundles << Common::BundleID.new(component.basename.to_s, SgtnClient::LocaleUtil.get_source_locale)
|
49
|
+
break bundles
|
50
|
+
end || bundles
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
autoload :StringUtil, 'sgtn-client/util/string-util'
|
6
|
+
autoload :LocaleUtil, 'sgtn-client/util/locale-util'
|
7
|
+
|
8
|
+
module TranslationLoader
|
9
|
+
autoload :CONSTS, 'sgtn-client/loader/consts'
|
10
|
+
|
11
|
+
module SourceComparer
|
12
|
+
def load_bundle(component, locale)
|
13
|
+
SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
|
14
|
+
|
15
|
+
# source locale and old source locale don't need comparison because they are bases of comparison
|
16
|
+
real_locale = cache_to_real_map[locale]
|
17
|
+
return super(component, real_locale) if real_locale
|
18
|
+
|
19
|
+
old_source_bundle_thread = Thread.new { load_bundle(component, CONSTS::OLD_SOURCE_LOCALE) }
|
20
|
+
source_bundle_thread = Thread.new { load_bundle(component, LocaleUtil.get_source_locale) }
|
21
|
+
translation_bundle = super(component, locale)
|
22
|
+
|
23
|
+
begin
|
24
|
+
old_source_bundle = old_source_bundle_thread.value
|
25
|
+
source_bundle = source_bundle_thread.value
|
26
|
+
rescue StandardError => e
|
27
|
+
SgtnClient.logger.error "[#{__FILE__}][#{__callee__}] failed to load soruce(or old source) bundle. component:#{component}. error: #{e}"
|
28
|
+
return translation_bundle
|
29
|
+
end
|
30
|
+
|
31
|
+
compare_source(translation_bundle, old_source_bundle, source_bundle)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def compare_source(translation_bundle, old_source_bundle, source_bundle)
|
37
|
+
if !translation_bundle.is_a?(Hash) || !source_bundle.is_a?(Hash) || !old_source_bundle.is_a?(Hash)
|
38
|
+
SgtnClient.logger.warn "can't do source comparison because some bundle data are wrong."
|
39
|
+
return translation_bundle
|
40
|
+
end
|
41
|
+
|
42
|
+
source_bundle.each do |key, value|
|
43
|
+
if old_source_bundle[key] != value || translation_bundle[key].nil?
|
44
|
+
translation_bundle[key] = StringUtil.new(value, LocaleUtil.get_source_locale)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
translation_bundle
|
48
|
+
end
|
49
|
+
|
50
|
+
def cache_to_real_map
|
51
|
+
@cache_to_real_map ||= {
|
52
|
+
LocaleUtil.get_source_locale => CONSTS::REAL_SOURCE_LOCALE,
|
53
|
+
CONSTS::OLD_SOURCE_LOCALE => LocaleUtil.get_source_locale
|
54
|
+
}.freeze
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -17,6 +17,8 @@ module SgtnClient
|
|
17
17
|
autoload :ValidateUtil, "sgtn-client/util/validate-util"
|
18
18
|
autoload :LocaleUtil, "sgtn-client/util/locale-util"
|
19
19
|
autoload :FileUtil, "sgtn-client/util/file-util"
|
20
|
+
autoload :CacheUtil, "sgtn-client/util/cache-util"
|
21
|
+
autoload :StringUtil, "sgtn-client/util/string-util"
|
20
22
|
|
21
23
|
module Formatters
|
22
24
|
autoload :PluralFormatter, "sgtn-client/formatters/plurals/plural_formatter"
|
@@ -1,48 +1,40 @@
|
|
1
1
|
# Copyright 2022 VMware, Inc.
|
2
2
|
# SPDX-License-Identifier: EPL-2.0
|
3
3
|
|
4
|
-
require 'erb'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
4
|
module SgtnClient
|
8
|
-
|
9
5
|
module Core
|
10
|
-
autoload :Cache,
|
6
|
+
autoload :Cache, 'sgtn-client/core/cache'
|
11
7
|
end
|
12
|
-
|
8
|
+
|
13
9
|
class CacheUtil
|
10
|
+
def self.get_cache(cache_key)
|
11
|
+
SgtnClient::Core::Cache.get(cache_key)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.clear_cache
|
15
|
+
SgtnClient::Core::Cache.clear
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.write_cache(cache_key, items)
|
19
|
+
return nil if items.nil? || items.empty?
|
20
|
+
|
21
|
+
env = SgtnClient::Config.default_environment
|
22
|
+
cache_expiry_period = SgtnClient::Config.configurations[env]['cache_expiry_period']
|
23
|
+
# expired after 24 hours
|
24
|
+
cache_expiry_period = 24 * 60 if cache_expiry_period.nil?
|
25
|
+
SgtnClient::Core::Cache.put(cache_key, items, cache_expiry_period)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.get_cachekey(component, locale)
|
29
|
+
env = SgtnClient::Config.default_environment
|
30
|
+
product_name = SgtnClient::Config.configurations[env]['product_name']
|
31
|
+
version = SgtnClient::Config.configurations[env]['version'].to_s
|
32
|
+
product_name + '_' + version + '_' + component + '_' + locale
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.is_expired(cache_item)
|
36
|
+
cache_item[:expiry] < Time.now
|
37
|
+
end
|
14
38
|
|
15
|
-
def self.get_cache(cache_key)
|
16
|
-
expired, items = SgtnClient::Core::Cache.get(cache_key)
|
17
|
-
SgtnClient.logger.debug "[CacheUtil]get cache with key #{cache_key}, expired #{expired}"
|
18
|
-
return expired, items
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.clear_cache()
|
22
|
-
SgtnClient::Core::Cache.clear()
|
23
|
-
SgtnClient.logger.debug "[CacheUtil]clear cache"
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.write_cache(cache_key, items)
|
27
|
-
if items.nil?
|
28
|
-
return nil
|
29
|
-
end
|
30
|
-
env = SgtnClient::Config.default_environment
|
31
|
-
cache_expiry_period = SgtnClient::Config.configurations[env]["cache_expiry_period"]
|
32
|
-
# expired after 24 hours
|
33
|
-
if cache_expiry_period == nil
|
34
|
-
cache_expiry_period = 24*60
|
35
|
-
end
|
36
|
-
SgtnClient.logger.debug "[CacheUtil]write cache with key #{cache_key}, cache_expiry_period #{cache_expiry_period}, itmes #{items}"
|
37
|
-
SgtnClient::Core::Cache.put(cache_key, items, cache_expiry_period)
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.get_cachekey(component, locale)
|
41
|
-
env = SgtnClient::Config.default_environment
|
42
|
-
product_name = SgtnClient::Config.configurations[env]["product_name"]
|
43
|
-
version = SgtnClient::Config.configurations[env]["version"].to_s
|
44
|
-
product_name + "_" + version + "_" + component + "_" + locale
|
45
|
-
end
|
46
39
|
end
|
47
|
-
|
48
40
|
end
|