singleton-client-test 0.7.0.32 → 0.7.0.33
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/lib/sgtn-client/api/source.rb +15 -64
- data/lib/sgtn-client/api/t.rb +16 -17
- data/lib/sgtn-client/api/translation.rb +83 -131
- data/lib/sgtn-client/common/single_operation.rb +33 -0
- data/lib/sgtn-client/core/cache.rb +12 -81
- data/lib/sgtn-client/loader/cache.rb +47 -0
- data/lib/sgtn-client/loader/local_bundle.rb +13 -0
- data/lib/sgtn-client/loader/local_source_bundle.rb +29 -0
- data/lib/sgtn-client/loader/server_bundle.rb +22 -0
- data/lib/sgtn-client/loader/source_comparer.rb +38 -0
- data/lib/sgtn-client/util/cache-util.rb +33 -38
- data/lib/sgtn-client/util/file-util.rb +8 -21
- data/lib/sgtn-client/util/locale-util.rb +41 -39
- data/lib/sgtn-client/util/string-util.rb +12 -0
- metadata +23 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2a32a268d9bc9b395baeb2d296049cbbb29a40c081968d6e8a5ae364c39444c
|
4
|
+
data.tar.gz: fdf427aaf792fbff1bf6afa404d6e0aeeea030b9fa90b7eab578cbe46659656e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ecfa3b8fbc1ef494e41a517a102856f4717d408845eeac43cd751c24df2a590404082cd00670b280fd8c404b3dd95ecd2ae5c06f7b0c0b23172b9eb7ab2fff7
|
7
|
+
data.tar.gz: 0a8aee2dd03eaa02c981cdfaa097ffbddebc2118c7c7a63625ad19ca332ecf582fac19a812339ef9920dac37765a03b30d9800d69cfe9dd2b854762ee0796170
|
@@ -1,73 +1,24 @@
|
|
1
1
|
# Copyright 2022 VMware, Inc.
|
2
2
|
# SPDX-License-Identifier: EPL-2.0
|
3
3
|
|
4
|
+
require 'sgtn-client/loader/local_source_bundle'
|
5
|
+
|
4
6
|
module SgtnClient
|
5
|
-
|
6
|
-
autoload :CacheUtil, "sgtn-client/util/cache-util"
|
7
|
+
autoload :CacheUtil, 'sgtn-client/util/cache-util'
|
7
8
|
|
8
9
|
class Source
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
s = (items.nil? || items[locale].nil?)? nil : items[locale][key]
|
21
|
-
if items.nil? || s.nil?
|
22
|
-
SgtnClient.logger.debug "[Source][getSource]source not found, return key: " + key
|
23
|
-
#return key
|
24
|
-
return nil
|
25
|
-
else
|
26
|
-
return s
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.getSources(component, locale)
|
31
|
-
SgtnClient.logger.debug "[Source][getSources]component=#{component}, locale=#{locale}"
|
32
|
-
cache_key = SgtnClient::CacheUtil.get_cachekey(component, locale)
|
33
|
-
expired, items = SgtnClient::CacheUtil.get_cache(cache_key)
|
34
|
-
if items.nil? || expired
|
35
|
-
items = getBundle(component, locale)
|
36
|
-
SgtnClient::CacheUtil.write_cache(cache_key, items)
|
37
|
-
else
|
38
|
-
SgtnClient.logger.debug "[Source][getSources]getting sources from cache with key: " + cache_key
|
39
|
-
end
|
40
|
-
return items
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.loadBundles(locale)
|
44
|
-
SgtnClient.logger.debug "[Source][loadBundles]locale=#{locale}"
|
45
|
-
env = SgtnClient::Config.default_environment
|
46
|
-
SgtnClient::Config.configurations.default = locale
|
47
|
-
source_bundle = SgtnClient::Config.configurations[env]["source_bundle"]
|
48
|
-
Dir.foreach(source_bundle) do |component|
|
49
|
-
next if component == '.' || component == '..'
|
50
|
-
yamlfile = File.join(source_bundle, component + "/" + locale + ".yml")
|
51
|
-
bundle = SgtnClient::FileUtil.read_yml(yamlfile)
|
52
|
-
cachekey = SgtnClient::CacheUtil.get_cachekey(component, locale)
|
53
|
-
SgtnClient::CacheUtil.write_cache(cachekey,bundle)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
def self.getBundle(component, locale)
|
59
|
-
SgtnClient.logger.debug "[Source][getBundle]component=#{component}, locale=#{locale}"
|
60
|
-
env = SgtnClient::Config.default_environment
|
61
|
-
source_bundle = SgtnClient::Config.configurations[env]["source_bundle"]
|
62
|
-
bundlepath = source_bundle + "/" + component + "/" + locale + ".yml"
|
63
|
-
begin
|
64
|
-
bundle = SgtnClient::FileUtil.read_yml(bundlepath)
|
65
|
-
rescue => exception
|
66
|
-
SgtnClient.logger.error exception.message
|
67
|
-
end
|
68
|
-
return bundle
|
10
|
+
def self.loadBundles(locale)
|
11
|
+
SgtnClient.logger.debug "[Source][loadBundles]locale=#{locale}"
|
12
|
+
env = SgtnClient::Config.default_environment
|
13
|
+
SgtnClient::Config.configurations.default = locale
|
14
|
+
source_bundle = SgtnClient::Config.configurations[env]['source_bundle']
|
15
|
+
Dir.foreach(source_bundle) do |component|
|
16
|
+
next if %w[. ..].include? component
|
17
|
+
|
18
|
+
bundle = load_bundle(component)
|
19
|
+
cachekey = SgtnClient::CacheUtil.get_cachekey(component, LocaleUtil.get_source_locale)
|
20
|
+
SgtnClient::CacheUtil.write_cache(cachekey, bundle)
|
69
21
|
end
|
70
|
-
|
22
|
+
end
|
71
23
|
end
|
72
|
-
|
73
24
|
end
|
data/lib/sgtn-client/api/t.rb
CHANGED
@@ -2,24 +2,23 @@
|
|
2
2
|
# SPDX-License-Identifier: EPL-2.0
|
3
3
|
|
4
4
|
module SgtnClient
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
5
|
+
class T
|
6
|
+
def self.s(key)
|
7
|
+
locale = RequestStore.store[:locale]
|
8
|
+
component = RequestStore.store[:component]
|
9
|
+
Translation.getString(component, key, locale)
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
def self.s_f(key, args)
|
13
|
+
locale = RequestStore.store[:locale]
|
14
|
+
component = RequestStore.store[:component]
|
15
|
+
Translation.getString_f(component, key, args, locale)
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
18
|
+
def self.c
|
19
|
+
locale = RequestStore.store[:locale]
|
20
|
+
component = RequestStore.store[:component]
|
21
|
+
Translation.getStrings(component, locale)
|
24
22
|
end
|
23
|
+
end
|
25
24
|
end
|
@@ -1,158 +1,110 @@
|
|
1
1
|
# Copyright 2022 VMware, Inc.
|
2
2
|
# SPDX-License-Identifier: EPL-2.0
|
3
3
|
|
4
|
-
require 'multi_json'
|
5
|
-
|
6
4
|
module SgtnClient
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
5
|
+
autoload :StringUtil, 'sgtn-client/util/string-util'
|
6
|
+
|
7
|
+
module TranslationLoader
|
8
|
+
autoload :LocalBundle, 'sgtn-client/loader/local_bundle'
|
9
|
+
autoload :ServerBundle, 'sgtn-client/loader/server_bundle'
|
10
|
+
autoload :SourceComparer, 'sgtn-client/loader/source_comparer'
|
11
|
+
autoload :SingleLoader, 'sgtn-client/loader/single_loader'
|
12
|
+
autoload :Cache, 'sgtn-client/loader/cache'
|
13
13
|
end
|
14
|
-
|
15
|
-
class Translation
|
16
|
-
|
17
|
-
def self.getString(component, key, locale)
|
18
|
-
SgtnClient.logger.debug "[Translation.getString]component: #{component}, key: #{key}, locale: #{locale}"
|
19
|
-
str = getTranslation(component, key, locale)
|
20
|
-
if str.nil?
|
21
|
-
str = SgtnClient::Source.getSource(component, key, SgtnClient::Config.configurations.default)
|
22
|
-
if str.nil?
|
23
|
-
SgtnClient.logger.debug "[Translation][getString] Missing source string with key: #{key}, component: #{component}, locale: #{locale}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
str
|
27
|
-
end
|
28
14
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
15
|
+
module Translation
|
16
|
+
def self.getString(component, key, locale)
|
17
|
+
SgtnClient.logger.debug "[Translation.getString]component: #{component}, key: #{key}, locale: #{locale}"
|
18
|
+
str = getTranslation(component, key, locale)
|
19
|
+
if str.nil? && !LocaleUtil.is_source_locale(locale)
|
20
|
+
str = getTranslation(component, key, LocaleUtil.get_source_locale)
|
21
|
+
end
|
22
|
+
str
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.getString_p(component, key, plural_args, locale)
|
26
|
+
SgtnClient.logger.debug "[Translation][getString_p]component=#{component}, key=#{key}, locale=#{locale}"
|
27
|
+
str = getTranslation(component, key, locale)
|
28
|
+
if str.nil?
|
29
|
+
unless LocaleUtil.is_source_locale(locale)
|
30
|
+
str = getTranslation(component, key, LocaleUtil.get_source_locale)
|
31
|
+
str.to_plural_s(LocaleUtil.get_source_locale, plural_args) if str
|
41
32
|
end
|
33
|
+
else
|
34
|
+
locale = str.locale if str.is_a?(SgtnClient::StringUtil)
|
35
|
+
str.to_plural_s(locale, plural_args)
|
42
36
|
end
|
37
|
+
end
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
return nil
|
49
|
-
end
|
50
|
-
if args.is_a?(Hash)
|
51
|
-
args.each do |source, arg|
|
52
|
-
s.gsub! "{#{source}}", arg
|
53
|
-
end
|
54
|
-
elsif args.is_a?(Array)
|
55
|
-
s = sprintf s % args
|
56
|
-
end
|
57
|
-
return s
|
58
|
-
end
|
39
|
+
def self.getString_f(component, key, args, locale, *optionals)
|
40
|
+
SgtnClient.logger.debug "[Translation][getString_f]component=#{component}, key=#{key}, locale=#{locale}"
|
41
|
+
s = getString(component, key, locale, *optionals)
|
42
|
+
return nil if s.nil?
|
59
43
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
items = get_cs(component, locale)
|
64
|
-
default = SgtnClient::Config.configurations.default
|
65
|
-
if items.nil? || items["messages"] == nil
|
66
|
-
items = {}
|
67
|
-
s = SgtnClient::Source.getSources(component, default)
|
68
|
-
if s.nil?
|
69
|
-
SgtnClient.logger.error "[Translation][getStrings] Missing component: #{component}, locale: #{locale}"
|
70
|
-
else
|
71
|
-
default_component, value = s.first
|
72
|
-
items["component"] = component
|
73
|
-
items["messages"] = value
|
74
|
-
items["locale"] = 'source'
|
75
|
-
end
|
44
|
+
if args.is_a?(Hash)
|
45
|
+
args.each do |source, arg|
|
46
|
+
s.gsub! "{#{source}}", arg
|
76
47
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
48
|
+
elsif args.is_a?(Array)
|
49
|
+
s = s % args
|
50
|
+
end
|
51
|
+
s
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.getStrings(component, locale)
|
55
|
+
SgtnClient.logger.debug "[Translation][getStrings]component=#{component}, locale=#{locale}"
|
56
|
+
locale = SgtnClient::LocaleUtil.get_best_locale(locale)
|
57
|
+
items = get_cs(component, locale)
|
58
|
+
if (items.nil? || items['messages'].nil?) && !LocaleUtil.is_source_locale(locale)
|
59
|
+
items = get_cs(component, LocaleUtil.get_source_locale)
|
60
|
+
end
|
80
61
|
|
81
|
-
|
62
|
+
items
|
63
|
+
end
|
82
64
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
else
|
89
|
-
items["messages"][key]
|
90
|
-
end
|
91
|
-
end
|
65
|
+
def self.getTranslation(component, key, locale)
|
66
|
+
locale = SgtnClient::LocaleUtil.get_best_locale(locale)
|
67
|
+
items = get_cs(component, locale)
|
68
|
+
items&.dig('messages', key)
|
69
|
+
end
|
92
70
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
messages = sources&.first&.last
|
98
|
-
return {'locale' => locale, 'component' => component, 'messages' => messages} if messages
|
99
|
-
end
|
71
|
+
def self.get_cs(component, locale)
|
72
|
+
cache_item = load_bundle(component, locale)
|
73
|
+
cache_item&.dig(:items)
|
74
|
+
end
|
100
75
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
items = load(component, locale)
|
106
|
-
if items.nil?
|
107
|
-
items = SgtnClient::Source.getSources(component, SgtnClient::Config.configurations.default)
|
108
|
-
SgtnClient::Core::Cache.put(cache_key, items, 60)
|
109
|
-
else
|
110
|
-
SgtnClient::CacheUtil.write_cache(cache_key, items)
|
111
|
-
end
|
112
|
-
else
|
113
|
-
SgtnClient.logger.debug "[Translation]get translations from cache with key: " + cache_key
|
114
|
-
end
|
76
|
+
def self.load_bundle(component, locale)
|
77
|
+
init_translations unless initialized?
|
78
|
+
super
|
79
|
+
end
|
115
80
|
|
116
|
-
|
117
|
-
|
81
|
+
class << self
|
82
|
+
def initialized?
|
83
|
+
@initialized ||= false
|
84
|
+
end
|
118
85
|
|
119
|
-
def
|
86
|
+
def init_translations
|
87
|
+
# TODO: Lock to initialize?
|
120
88
|
env = SgtnClient::Config.default_environment
|
121
|
-
mode = SgtnClient::Config.configurations[env][
|
122
|
-
SgtnClient.logger.debug "[Translation][
|
89
|
+
mode = SgtnClient::Config.configurations[env]['bundle_mode']
|
90
|
+
SgtnClient.logger.debug "[Translation][init_translations]mode=#{mode}"
|
91
|
+
|
123
92
|
if mode == 'offline'
|
124
|
-
|
93
|
+
extend SgtnClient::TranslationLoader::LocalBundle
|
125
94
|
else
|
126
|
-
|
95
|
+
extend SgtnClient::TranslationLoader::ServerBundle
|
127
96
|
end
|
128
|
-
end
|
129
97
|
|
130
|
-
|
131
|
-
|
132
|
-
product_name = SgtnClient::Config.configurations[env]["product_name"]
|
133
|
-
version = SgtnClient::Config.configurations[env]["version"].to_s
|
134
|
-
translation_bundle = SgtnClient::Config.configurations[env]["translation_bundle"]
|
135
|
-
bundlepath = translation_bundle + "/" + product_name + "/" + version + "/" + component + "/messages_" + locale + ".json"
|
136
|
-
SgtnClient::FileUtil.read_json(bundlepath)
|
137
|
-
end
|
98
|
+
extend SgtnClient::TranslationLoader::SourceComparer
|
99
|
+
extend SgtnClient::TranslationLoader::Cache
|
138
100
|
|
139
|
-
|
140
|
-
|
141
|
-
product_name = SgtnClient::Config.configurations[env]["product_name"]
|
142
|
-
vip_server = SgtnClient::Config.configurations[env]["vip_server"]
|
143
|
-
version = SgtnClient::Config.configurations[env]["version"].to_s
|
144
|
-
url = vip_server + "/i18n/api/v2/translation/products/" + product_name + "/versions/" + version + "/locales/" + locale + "/components/" + component+ "?checkTranslationStatus=false&machineTranslation=false&pseudo=false"
|
145
|
-
begin
|
146
|
-
obj = SgtnClient::Core::Request.get(url)
|
147
|
-
rescue => exception
|
148
|
-
SgtnClient.logger.error exception.message
|
149
|
-
end
|
150
|
-
if obj != nil
|
151
|
-
obj = obj["data"]
|
152
|
-
end
|
153
|
-
return obj
|
101
|
+
load_translations
|
102
|
+
@initialized = true
|
154
103
|
end
|
155
104
|
|
156
|
-
|
105
|
+
def load_translations; end
|
106
|
+
end
|
157
107
|
|
108
|
+
private_class_method :getTranslation, :get_cs, :load_bundle, :initialized?, :load_translations
|
109
|
+
end
|
158
110
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
class SingleOperation
|
6
|
+
def initialize(*conditions, &block)
|
7
|
+
raise 'no way to create a new obj' unless block
|
8
|
+
|
9
|
+
@lock = Mutex.new
|
10
|
+
@hash = {}
|
11
|
+
|
12
|
+
@conditions = conditions
|
13
|
+
@creator = block
|
14
|
+
end
|
15
|
+
|
16
|
+
# return new created object
|
17
|
+
def operate(id, *args)
|
18
|
+
@lock.synchronize do
|
19
|
+
obj = @hash[id]
|
20
|
+
@conditions.each do |con|
|
21
|
+
return obj unless con.call(id, obj, *args)
|
22
|
+
end
|
23
|
+
@hash[id] = @creator.call(id, obj, *args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_object(id)
|
28
|
+
@lock.synchronize do
|
29
|
+
@hash.delete(id)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -5,99 +5,30 @@ require 'date'
|
|
5
5
|
|
6
6
|
module SgtnClient::Core
|
7
7
|
class Cache
|
8
|
-
Entry = Struct.new(:expiry, :
|
8
|
+
Entry = Struct.new(:expiry, :items)
|
9
9
|
|
10
10
|
def self.initialize(disabled=false, opts={})
|
11
11
|
@@opts = opts
|
12
|
-
|
13
|
-
|
14
|
-
@@data = Hash.new
|
15
|
-
SgtnClient.logger.debug "[Cache][initialize]cache is enabled!"
|
16
|
-
else
|
17
|
-
@@data = nil
|
18
|
-
SgtnClient.logger.debug "[Cache][initialize]cache is disabled!"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.keys
|
23
|
-
if @@data == nil
|
24
|
-
return nil
|
25
|
-
end
|
26
|
-
SgtnClient.logger.debug "[Cache][keys]get cache keys"
|
27
|
-
@@data.keys
|
12
|
+
SgtnClient.logger.debug "[Cache][initialize] Disable cache? #{disabled}"
|
13
|
+
@@data = Hash.new
|
28
14
|
end
|
29
15
|
|
30
16
|
def self.get(key)
|
31
|
-
if @@data == nil
|
32
|
-
return nil, nil
|
33
|
-
end
|
34
17
|
SgtnClient.logger.debug "[Cache][get]get cache for key: " + key
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.has(key)
|
39
|
-
if @@data == nil
|
40
|
-
return nil
|
41
|
-
end
|
42
|
-
SgtnClient.logger.debug "[Cache][has]check if the cache has key: #{(@@data.has_key? key)}"
|
43
|
-
@@data.has_key? key
|
18
|
+
return @@data&.dig(key)
|
44
19
|
end
|
45
20
|
|
46
|
-
def self.put(key,
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
ttl ||= @@opts[:ttl]
|
52
|
-
# hours from new
|
53
|
-
SgtnClient.logger.debug "[Cache][put]put cache for key '" + key + "' with expired time at'" + (Time.now + ttl*60).to_s
|
54
|
-
@@data[key] = Entry.new(Time.now + ttl*60, value)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.delete(key)
|
59
|
-
@mutex.synchronize do
|
60
|
-
if @@data == nil
|
61
|
-
return nil
|
62
|
-
end
|
63
|
-
SgtnClient.logger.debug "[Cache][delete]delete cache for key: " + key
|
64
|
-
@@data.delete key
|
65
|
-
end
|
21
|
+
def self.put(key, items, ttl=nil)
|
22
|
+
ttl ||= @@opts[:ttl]
|
23
|
+
# hours from new
|
24
|
+
SgtnClient.logger.debug "[Cache][put]put cache for key '" + key + "' with expired time at'" + (Time.now + ttl*60).to_s
|
25
|
+
@@data[key] = Entry.new(Time.now + ttl*60, items)
|
66
26
|
end
|
67
27
|
|
68
28
|
def self.clear
|
69
|
-
|
70
|
-
|
71
|
-
return nil
|
72
|
-
end
|
73
|
-
SgtnClient.logger.debug "[Cache][clear]clear cache!"
|
74
|
-
@@data = Hash.new
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.invalidate(key)
|
79
|
-
@mutex.synchronize do
|
80
|
-
if @@data == nil
|
81
|
-
return nil, nil
|
82
|
-
end
|
83
|
-
SgtnClient.logger.debug "[Cache][invalidate]invalidate expired cache......"
|
84
|
-
now = Time.now
|
85
|
-
if has(key)
|
86
|
-
v = @@data[key]
|
87
|
-
expired = false
|
88
|
-
SgtnClient.logger.debug "[Cache][invalidate]check cache: key=#{key}, expiredtime=#{v[:expiry]}, now=#{now}, expired=#{(v[:expiry] < now)}"
|
89
|
-
if v[:expiry] < now
|
90
|
-
SgtnClient.logger.debug "[Cache][invalidate]before deleting the cache: data=#{@@data}"
|
91
|
-
@@data.delete(key)
|
92
|
-
SgtnClient.logger.debug "[Cache][invalidate]after deleting the cache: data=#{@@data}"
|
93
|
-
expired = true
|
94
|
-
end
|
95
|
-
return expired, v[:value]
|
96
|
-
else
|
97
|
-
return nil, nil
|
98
|
-
end
|
99
|
-
end
|
29
|
+
SgtnClient.logger.debug "[Cache][clear]clear cache!"
|
30
|
+
@@data = Hash.new
|
100
31
|
end
|
101
32
|
end
|
102
33
|
|
103
|
-
end
|
34
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
autoload :CacheUtil, 'sgtn-client/util/cache-util'
|
6
|
+
autoload :SingleOperation, 'sgtn-client/common/single_operation'
|
7
|
+
end
|
8
|
+
module SgtnClient::TranslationLoader::Cache
|
9
|
+
def load_bundle(component, locale)
|
10
|
+
cache_key = SgtnClient::CacheUtil.get_cachekey(component, locale)
|
11
|
+
SgtnClient.logger.debug "[#{self.to_s}][#{__FILE__}][#{__method__}] cache_key=#{cache_key}"
|
12
|
+
cache_item = SgtnClient::CacheUtil.get_cache(cache_key)
|
13
|
+
if cache_item.nil?
|
14
|
+
# refresh synchronously if not in cache
|
15
|
+
SgtnClient.logger.debug "[#{self.to_s}][#{__FILE__}][#{__method__}] Cache miss. cache_key=#{cache_key}"
|
16
|
+
cache_item = (single_loader { |c, l| super(c, l) }).operate(cache_key, component, locale).value
|
17
|
+
# TODO: if an error occurs when requesting a bundle, need to avoid more requests
|
18
|
+
elsif SgtnClient::CacheUtil.is_expired(cache_item) && locale != SgtnClient::LocaleUtil.get_source_locale # local source never expires.
|
19
|
+
SgtnClient.logger.debug "[#{self.to_s}][#{__FILE__}][#{__method__}] Bundle cache is expired. cache_key=#{cache_key}"
|
20
|
+
@single_loader.operate(cache_key, component, locale) # refresh in background
|
21
|
+
end
|
22
|
+
cache_item
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def single_loader
|
28
|
+
@single_loader ||= begin
|
29
|
+
none_alive = proc { |_, thread| thread.nil? || thread.alive? == false }
|
30
|
+
to_run = proc do |id|
|
31
|
+
cache_item = SgtnClient::CacheUtil.get_cache(id)
|
32
|
+
cache_item&.dig(:items).nil? || SgtnClient::CacheUtil.is_expired(cache_item)
|
33
|
+
end
|
34
|
+
creator = proc do |id, _, c, l|
|
35
|
+
Thread.new do
|
36
|
+
SgtnClient.logger.debug "Refreshing cache for #{c}/#{l}"
|
37
|
+
cache_item = SgtnClient::CacheUtil.write_cache(id, yield(c, l))
|
38
|
+
# delete thread from hash after finish
|
39
|
+
Thread.new { @single_loader.remove_object(id) }
|
40
|
+
cache_item
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
SgtnClient::SingleOperation.new(none_alive, to_run, &creator)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient::TranslationLoader::LocalBundle
|
5
|
+
def load_bundle(component, locale)
|
6
|
+
env = SgtnClient::Config.default_environment
|
7
|
+
product_name = SgtnClient::Config.configurations[env]['product_name']
|
8
|
+
version = SgtnClient::Config.configurations[env]['version'].to_s
|
9
|
+
translation_bundle = SgtnClient::Config.configurations[env]['translation_bundle']
|
10
|
+
bundlepath = translation_bundle + '/' + product_name + '/' + version + '/' + component + '/messages_' + locale + '.json'
|
11
|
+
SgtnClient::FileUtil.read_json(bundlepath)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
class Source
|
6
|
+
def self.load_bundle(component)
|
7
|
+
SgtnClient.logger.debug "[Source][getBundle]component=#{component}"
|
8
|
+
env = SgtnClient::Config.default_environment
|
9
|
+
source_bundle = SgtnClient::Config.configurations[env]['source_bundle']
|
10
|
+
component_path = "#{source_bundle}/#{component}"
|
11
|
+
total_messages = nil
|
12
|
+
Dir.glob('**/*.{yml, yaml}', base: component_path) do |f|
|
13
|
+
bundle = SgtnClient::FileUtil.read_yml("#{component_path}/#{f}")
|
14
|
+
if total_messages.nil?
|
15
|
+
total_messages = bundle&.first&.last
|
16
|
+
else
|
17
|
+
total_messages.merge!(bundle&.first&.last)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if total_messages
|
22
|
+
return { 'component' => component, 'locale' => SgtnClient::LocaleUtil.get_source_locale, 'messages' => total_messages }
|
23
|
+
end
|
24
|
+
rescue StandardError => e
|
25
|
+
SgtnClient.logger.error e.message
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient::Core
|
5
|
+
autoload :Request, 'sgtn-client/core/request'
|
6
|
+
end
|
7
|
+
|
8
|
+
module SgtnClient::TranslationLoader::ServerBundle
|
9
|
+
def load_bundle(component, locale)
|
10
|
+
env = SgtnClient::Config.default_environment
|
11
|
+
product_name = SgtnClient::Config.configurations[env]['product_name']
|
12
|
+
vip_server = SgtnClient::Config.configurations[env]['vip_server']
|
13
|
+
version = SgtnClient::Config.configurations[env]['version'].to_s
|
14
|
+
url = vip_server + '/i18n/api/v2/translation/products/' + product_name + '/versions/' + version + '/locales/' + locale + '/components/' + component + '?checkTranslationStatus=false&machineTranslation=false&pseudo=false'
|
15
|
+
begin
|
16
|
+
obj = SgtnClient::Core::Request.get(url)
|
17
|
+
rescue StandardError => e
|
18
|
+
SgtnClient.logger.error e.message
|
19
|
+
end
|
20
|
+
obj && obj['data']
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Copyright 2022 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
|
4
|
+
module SgtnClient
|
5
|
+
autoload :Source, 'sgtn-client/loader/local_source_bundle'
|
6
|
+
end
|
7
|
+
module SgtnClient::TranslationLoader::SourceComparer
|
8
|
+
def load_bundle(component, locale)
|
9
|
+
# source locale always uses source bundles
|
10
|
+
return SgtnClient::Source.load_bundle(component) if SgtnClient::LocaleUtil.is_source_locale(locale)
|
11
|
+
|
12
|
+
translation_bundle_thread = Thread.new { super(component, locale) }
|
13
|
+
old_source_bundle = super(component, SgtnClient::LocaleUtil.get_source_locale)
|
14
|
+
source_bundle = get_cs(component, SgtnClient::LocaleUtil.get_source_locale)
|
15
|
+
translation_bundle = translation_bundle_thread.value
|
16
|
+
|
17
|
+
compare_source(translation_bundle, old_source_bundle, source_bundle)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def compare_source(translation_bundle, old_source_bundle, source_bundle)
|
23
|
+
return translation_bundle if translation_bundle.nil? || source_bundle.nil? || old_source_bundle.nil?
|
24
|
+
|
25
|
+
old_source_messages = old_source_bundle['messages']
|
26
|
+
translation_messages = translation_bundle['messages']
|
27
|
+
translation_bundle['messages'] = new_translation_messages = {}
|
28
|
+
source_bundle['messages'].each do |key, value|
|
29
|
+
translation = translation_messages[key]
|
30
|
+
new_translation_messages[key] = if old_source_messages[key] == value && !translation.nil?
|
31
|
+
translation
|
32
|
+
else
|
33
|
+
SgtnClient::StringUtil.new(value, SgtnClient::LocaleUtil.get_source_locale)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
translation_bundle
|
37
|
+
end
|
38
|
+
end
|
@@ -1,48 +1,43 @@
|
|
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.logger.debug "[CacheUtil]get cache with key #{cache_key}"
|
12
|
+
SgtnClient::Core::Cache.get(cache_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.clear_cache
|
16
|
+
SgtnClient::Core::Cache.clear
|
17
|
+
SgtnClient.logger.debug '[CacheUtil]clear cache'
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.write_cache(cache_key, items)
|
21
|
+
return nil if items.nil? || items.empty?
|
22
|
+
|
23
|
+
env = SgtnClient::Config.default_environment
|
24
|
+
cache_expiry_period = SgtnClient::Config.configurations[env]['cache_expiry_period']
|
25
|
+
# expired after 24 hours
|
26
|
+
cache_expiry_period = 24 * 60 if cache_expiry_period.nil?
|
27
|
+
SgtnClient.logger.debug "[CacheUtil]write cache with key #{cache_key}, cache_expiry_period #{cache_expiry_period}"
|
28
|
+
SgtnClient::Core::Cache.put(cache_key, items, cache_expiry_period)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_cachekey(component, locale)
|
32
|
+
env = SgtnClient::Config.default_environment
|
33
|
+
product_name = SgtnClient::Config.configurations[env]['product_name']
|
34
|
+
version = SgtnClient::Config.configurations[env]['version'].to_s
|
35
|
+
product_name + '_' + version + '_' + component + '_' + locale
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.is_expired(cache_item)
|
39
|
+
cache_item[:expiry] < Time.now
|
40
|
+
end
|
14
41
|
|
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
42
|
end
|
47
|
-
|
48
43
|
end
|
@@ -1,37 +1,24 @@
|
|
1
1
|
# Copyright 2022 VMware, Inc.
|
2
2
|
# SPDX-License-Identifier: EPL-2.0
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
autoload :YAML, 'yaml'
|
5
|
+
autoload :MultiJson, 'multi_json'
|
6
6
|
|
7
7
|
module SgtnClient
|
8
8
|
|
9
9
|
class FileUtil
|
10
|
-
|
11
|
-
@mutex = Mutex.new
|
12
|
-
|
13
10
|
def self.read_json(bundlepath)
|
14
11
|
SgtnClient.logger.debug "[FileUtil]read json file from: " + bundlepath
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
data_hash = MultiJson.load(file)
|
20
|
-
rescue => exception
|
21
|
-
SgtnClient.logger.error exception.message
|
22
|
-
end
|
23
|
-
return data_hash
|
12
|
+
begin
|
13
|
+
return MultiJson.load(File.read(bundlepath))
|
14
|
+
rescue => exception
|
15
|
+
SgtnClient.logger.error exception.message
|
24
16
|
end
|
17
|
+
nil
|
25
18
|
end
|
26
19
|
|
27
20
|
def self.read_yml(file_name)
|
28
|
-
|
29
|
-
@mutex.synchronize do
|
30
|
-
erb = ERB.new(File.read(file_name))
|
31
|
-
erb.filename = file_name
|
32
|
-
YAML.load(erb.result)
|
33
|
-
end
|
21
|
+
YAML::load(File.read(file_name))
|
34
22
|
end
|
35
23
|
end
|
36
|
-
|
37
24
|
end
|
@@ -1,46 +1,48 @@
|
|
1
1
|
# Copyright 2022 VMware, Inc.
|
2
2
|
# SPDX-License-Identifier: EPL-2.0
|
3
|
+
require 'set'
|
3
4
|
|
4
5
|
module SgtnClient
|
6
|
+
SUPPORTED_LOCALES = %w[en de es fr ko ja zh-Hans zh-Hant zh de-CH].freeze # TODO get this from service in online mode
|
7
|
+
|
8
|
+
MAP_LOCALES = {
|
9
|
+
'zh-CN' => 'zh-Hans',
|
10
|
+
'zh-TW' => 'zh-Hant',
|
11
|
+
'zh-Hans-CN' => 'zh-Hans',
|
12
|
+
'zh-Hant-TW' => 'zh-Hant'
|
13
|
+
}.freeze
|
5
14
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
return found[0]
|
27
|
-
end
|
28
|
-
if SgtnClient::MAP_LOCALES.key?(locale)
|
29
|
-
return SgtnClient::MAP_LOCALES[locale]
|
30
|
-
end
|
31
|
-
parts = locale.split("-")
|
32
|
-
if parts.size > 1
|
33
|
-
f = SgtnClient::DEFAULT_LOCALES.select {|e| e == parts[0]}
|
34
|
-
if !f.empty?
|
35
|
-
return f[0]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
return locale
|
39
|
-
end
|
40
|
-
def self.get_source_locale
|
41
|
-
env = SgtnClient::Config.default_environment
|
42
|
-
source_locale = SgtnClient::Config.configurations[env]["default_language"]
|
43
|
-
source_locale || 'en'
|
44
|
-
end
|
15
|
+
class LocaleUtil
|
16
|
+
def self.get_best_locale(locale)
|
17
|
+
return get_default_locale if locale.nil?
|
18
|
+
|
19
|
+
locale = locale.to_s
|
20
|
+
return get_default_locale if locale.empty?
|
21
|
+
|
22
|
+
get_best_match(locale)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.is_source_locale(locale = nil)
|
26
|
+
locale == get_source_locale
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_best_match(locale)
|
30
|
+
locale = locale.gsub('_', '-')
|
31
|
+
locale = SgtnClient::MAP_LOCALES[locale] if SgtnClient::MAP_LOCALES.key?(locale)
|
32
|
+
return locale if SUPPORTED_LOCALES.include?(locale)
|
33
|
+
return LocaleUtil.get_source_locale if locale.index('-').nil?
|
34
|
+
get_best_match(locale.slice(0..(locale.rindex('-')-1)) )
|
45
35
|
end
|
36
|
+
|
37
|
+
def self.get_source_locale
|
38
|
+
'en'
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.get_default_locale
|
42
|
+
env = SgtnClient::Config.default_environment
|
43
|
+
SgtnClient::Config.configurations[env]['default_language'] || 'en'
|
44
|
+
end
|
45
|
+
|
46
|
+
private_class_method :get_best_match
|
47
|
+
end
|
46
48
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: singleton-client-test
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.33
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- VMware G11n Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rest-client
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '2.0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '2.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: multi_json
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,34 +80,6 @@ dependencies:
|
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
82
|
version: '3.0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: pry
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: pry-doc
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
83
|
- !ruby/object:Gem::Dependency
|
126
84
|
name: rdoc
|
127
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -244,6 +202,20 @@ dependencies:
|
|
244
202
|
- - "~>"
|
245
203
|
- !ruby/object:Gem::Version
|
246
204
|
version: '1.0'
|
205
|
+
- !ruby/object:Gem::Dependency
|
206
|
+
name: rest-client
|
207
|
+
requirement: !ruby/object:Gem::Requirement
|
208
|
+
requirements:
|
209
|
+
- - "~>"
|
210
|
+
- !ruby/object:Gem::Version
|
211
|
+
version: '2.0'
|
212
|
+
type: :runtime
|
213
|
+
prerelease: false
|
214
|
+
version_requirements: !ruby/object:Gem::Requirement
|
215
|
+
requirements:
|
216
|
+
- - "~>"
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: '2.0'
|
247
219
|
- !ruby/object:Gem::Dependency
|
248
220
|
name: twitter_cldr
|
249
221
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,16 +246,23 @@ files:
|
|
274
246
|
- lib/sgtn-client/cldr/localized_datetime.rb
|
275
247
|
- lib/sgtn-client/cldr/localized_str.rb
|
276
248
|
- lib/sgtn-client/cldr/localized_time.rb
|
249
|
+
- lib/sgtn-client/common/single_operation.rb
|
277
250
|
- lib/sgtn-client/core/cache.rb
|
278
251
|
- lib/sgtn-client/core/config.rb
|
279
252
|
- lib/sgtn-client/core/exceptions.rb
|
280
253
|
- lib/sgtn-client/core/logging.rb
|
281
254
|
- lib/sgtn-client/core/request.rb
|
282
255
|
- lib/sgtn-client/formatters/plurals/plural_formatter.rb
|
256
|
+
- lib/sgtn-client/loader/cache.rb
|
257
|
+
- lib/sgtn-client/loader/local_bundle.rb
|
258
|
+
- lib/sgtn-client/loader/local_source_bundle.rb
|
259
|
+
- lib/sgtn-client/loader/server_bundle.rb
|
260
|
+
- lib/sgtn-client/loader/source_comparer.rb
|
283
261
|
- lib/sgtn-client/sgtn-client.rb
|
284
262
|
- lib/sgtn-client/util/cache-util.rb
|
285
263
|
- lib/sgtn-client/util/file-util.rb
|
286
264
|
- lib/sgtn-client/util/locale-util.rb
|
265
|
+
- lib/sgtn-client/util/string-util.rb
|
287
266
|
- lib/sgtn-client/util/validate-util.rb
|
288
267
|
- lib/singleton-ruby.rb
|
289
268
|
- lib/version.rb
|