tml 4.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +243 -0
- data/Rakefile +9 -0
- data/lib/tml.rb +56 -0
- data/lib/tml/api/client.rb +206 -0
- data/lib/tml/api/post_office.rb +71 -0
- data/lib/tml/application.rb +254 -0
- data/lib/tml/base.rb +116 -0
- data/lib/tml/cache.rb +143 -0
- data/lib/tml/cache_adapters/file.rb +89 -0
- data/lib/tml/cache_adapters/memcache.rb +104 -0
- data/lib/tml/cache_adapters/memory.rb +85 -0
- data/lib/tml/cache_adapters/redis.rb +108 -0
- data/lib/tml/config.rb +410 -0
- data/lib/tml/decorators/base.rb +52 -0
- data/lib/tml/decorators/default.rb +43 -0
- data/lib/tml/decorators/html.rb +102 -0
- data/lib/tml/exception.rb +35 -0
- data/lib/tml/ext/array.rb +86 -0
- data/lib/tml/ext/date.rb +99 -0
- data/lib/tml/ext/fixnum.rb +47 -0
- data/lib/tml/ext/hash.rb +99 -0
- data/lib/tml/ext/string.rb +56 -0
- data/lib/tml/ext/time.rb +89 -0
- data/lib/tml/generators/cache/base.rb +117 -0
- data/lib/tml/generators/cache/file.rb +159 -0
- data/lib/tml/language.rb +175 -0
- data/lib/tml/language_case.rb +105 -0
- data/lib/tml/language_case_rule.rb +76 -0
- data/lib/tml/language_context.rb +117 -0
- data/lib/tml/language_context_rule.rb +56 -0
- data/lib/tml/languages/en.json +1363 -0
- data/lib/tml/logger.rb +109 -0
- data/lib/tml/rules_engine/evaluator.rb +162 -0
- data/lib/tml/rules_engine/parser.rb +65 -0
- data/lib/tml/session.rb +199 -0
- data/lib/tml/source.rb +106 -0
- data/lib/tml/tokenizers/data.rb +96 -0
- data/lib/tml/tokenizers/decoration.rb +204 -0
- data/lib/tml/tokenizers/dom.rb +346 -0
- data/lib/tml/tokens/data.rb +403 -0
- data/lib/tml/tokens/method.rb +61 -0
- data/lib/tml/tokens/transform.rb +223 -0
- data/lib/tml/translation.rb +67 -0
- data/lib/tml/translation_key.rb +178 -0
- data/lib/tml/translator.rb +47 -0
- data/lib/tml/utils.rb +130 -0
- data/lib/tml/version.rb +34 -0
- metadata +121 -0
data/lib/tml/ext/hash.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
|
3
|
+
#
|
4
|
+
# _______ _ _ _ ______ _
|
5
|
+
# |__ __| | | | | (_) | ____| | |
|
6
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
7
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
8
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
9
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
10
|
+
# __/ |
|
11
|
+
# |___/
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
# a copy of this software and associated documentation files (the
|
14
|
+
# "Software"), to deal in the Software without restriction, including
|
15
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
# the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be
|
21
|
+
# included in all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
26
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
27
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
28
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
29
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
#++
|
31
|
+
|
32
|
+
class Hash
|
33
|
+
#
|
34
|
+
# = Hash Recursive Merge
|
35
|
+
#
|
36
|
+
# Merges a Ruby Hash recursively, Also known as deep merge.
|
37
|
+
# Recursive version of Hash#merge and Hash#merge!.
|
38
|
+
#
|
39
|
+
# Category:: Ruby
|
40
|
+
# Package:: Hash
|
41
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
42
|
+
# Copyright:: 2007-2008 The Authors
|
43
|
+
# License:: MIT License
|
44
|
+
# Link:: http://www.simonecarletti.com/
|
45
|
+
# Source:: http://gist.github.com/gists/6391/
|
46
|
+
|
47
|
+
#
|
48
|
+
# Recursive version of Hash#merge!
|
49
|
+
#
|
50
|
+
# Adds the contents of +other_hash+ to +hsh+,
|
51
|
+
# merging entries in +hsh+ with duplicate keys with those from +other_hash+.
|
52
|
+
#
|
53
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
54
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
55
|
+
# it merges and returns the values from both arrays.
|
56
|
+
#
|
57
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
58
|
+
# h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
|
59
|
+
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
60
|
+
#
|
61
|
+
# Simply using Hash#merge! would return
|
62
|
+
#
|
63
|
+
# h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
64
|
+
#
|
65
|
+
def rmerge!(other_hash)
|
66
|
+
merge!(other_hash) do |key, oldval, newval|
|
67
|
+
oldval.class == self.class ? oldval.rmerge!(newval) : newval
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Recursive version of Hash#merge
|
73
|
+
#
|
74
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
75
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
76
|
+
# it merges and returns the values from both arrays.
|
77
|
+
#
|
78
|
+
# Compared with Hash#merge, this method provides a different approch
|
79
|
+
# for merging nasted hashes.
|
80
|
+
# If the value of a given key is an Hash and both +other_hash+ abd +hsh
|
81
|
+
# includes the same key, the value is merged instead replaced with
|
82
|
+
# +other_hash+ value.
|
83
|
+
#
|
84
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
85
|
+
# h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
|
86
|
+
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
87
|
+
#
|
88
|
+
# Simply using Hash#merge would return
|
89
|
+
#
|
90
|
+
# h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
91
|
+
#
|
92
|
+
def rmerge(other_hash)
|
93
|
+
r = {}
|
94
|
+
merge(other_hash) do |key, oldval, newval|
|
95
|
+
r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
|
3
|
+
#
|
4
|
+
# _______ _ _ _ ______ _
|
5
|
+
# |__ __| | | | | (_) | ____| | |
|
6
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
7
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
8
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
9
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
10
|
+
# __/ |
|
11
|
+
# |___/
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
# a copy of this software and associated documentation files (the
|
14
|
+
# "Software"), to deal in the Software without restriction, including
|
15
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
# the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be
|
21
|
+
# included in all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
26
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
27
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
28
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
29
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
#++
|
31
|
+
|
32
|
+
class String
|
33
|
+
|
34
|
+
def translate(desc = '', tokens = {}, options = {})
|
35
|
+
Tml.session.target_language.translate(
|
36
|
+
Tml::Utils.normalize_tr_params(self, desc, tokens, options)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def trl(desc = '', tokens = {}, options = {})
|
41
|
+
Tml.session.target_language.translate(
|
42
|
+
Tml::Utils.normalize_tr_params(self, desc, tokens, options.merge(:skip_decorations => true))
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def tml_translated
|
47
|
+
return self if frozen?
|
48
|
+
@tml_translated = true
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def tml_translated?
|
53
|
+
@tml_translated
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
data/lib/tml/ext/time.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
|
3
|
+
#
|
4
|
+
# _______ _ _ _ ______ _
|
5
|
+
# |__ __| | | | | (_) | ____| | |
|
6
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
7
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
8
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
9
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
10
|
+
# __/ |
|
11
|
+
# |___/
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
# a copy of this software and associated documentation files (the
|
14
|
+
# "Software"), to deal in the Software without restriction, including
|
15
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
# the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be
|
21
|
+
# included in all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
26
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
27
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
28
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
29
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
#++
|
31
|
+
|
32
|
+
class Time
|
33
|
+
|
34
|
+
def translate(format = :default, options = {})
|
35
|
+
language = Tml.session.target_language
|
36
|
+
|
37
|
+
label = (format.is_a?(String) ? format.clone : Tml.config.default_date_formats[format].clone)
|
38
|
+
symbols = label.scan(/(%\w)/).flatten.uniq
|
39
|
+
|
40
|
+
selected_tokens = []
|
41
|
+
using_tokens = label.index('{')
|
42
|
+
|
43
|
+
if using_tokens
|
44
|
+
selected_tokens = Tml::Tokens::Data.parse(label).collect{ |token| token.name(:parens => true) }
|
45
|
+
else
|
46
|
+
symbols.each do |symbol|
|
47
|
+
token = Tml.config.strftime_symbol_to_token(symbol)
|
48
|
+
next unless token
|
49
|
+
selected_tokens << token
|
50
|
+
label.gsub!(symbol, token)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
tokens = {}
|
55
|
+
selected_tokens.each do |token|
|
56
|
+
case token
|
57
|
+
when '{days}' then tokens[:days] = options[:with_leading_zero] ? day.with_leading_zero : day.to_s
|
58
|
+
when '{year_days}' then tokens[:year_days] = options[:with_leading_zero] ? yday.with_leading_zero : yday.to_s
|
59
|
+
when '{months}' then tokens[:months] = options[:with_leading_zero] ? month.with_leading_zero : month.to_s
|
60
|
+
when '{week_num}' then tokens[:week_num] = wday
|
61
|
+
when '{week_days}' then tokens[:week_days] = strftime('%w')
|
62
|
+
when '{short_years}' then tokens[:short_years] = strftime('%y')
|
63
|
+
when '{years}' then tokens[:years] = year
|
64
|
+
when '{short_week_day_name}' then tokens[:short_week_day_name] = language.tr(Tml.config.default_abbr_day_name(wday), 'Short name for a day of a week', {}, options)
|
65
|
+
when '{week_day_name}' then tokens[:week_day_name] = language.tr(Tml.config.default_day_name(wday), 'Day of a week', {}, options)
|
66
|
+
when '{short_month_name}' then tokens[:short_month_name] = language.tr(Tml.config.default_abbr_month_name(month - 1), 'Short month name', {}, options)
|
67
|
+
when '{month_name}' then tokens[:month_name] = language.tr(Tml.config.default_month_name(month - 1), 'Month name', {}, options)
|
68
|
+
when '{am_pm}' then tokens[:am_pm] = language.tr(strftime('%p'), 'Meridian indicator', {}, options)
|
69
|
+
when '{full_hours}' then tokens[:full_hours] = hour
|
70
|
+
when '{short_hours}' then tokens[:short_hours] = strftime('%I')
|
71
|
+
when '{trimed_hour}' then tokens[:trimed_hour] = strftime('%l')
|
72
|
+
when '{minutes}' then tokens[:minutes] = strftime('%M')
|
73
|
+
when '{seconds}' then tokens[:seconds] = strftime('%S')
|
74
|
+
when '{since_epoch}' then tokens[:since_epoch] = strftime('%s')
|
75
|
+
when '{day_of_month}' then tokens[:day_of_month] = strftime('%e')
|
76
|
+
else
|
77
|
+
''
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
language.tr(label, nil, tokens, options)
|
82
|
+
end
|
83
|
+
alias :tr :translate
|
84
|
+
|
85
|
+
def trl(format = :default, options = {})
|
86
|
+
tr(format, options.merge!(:skip_decorations => true))
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
|
3
|
+
#
|
4
|
+
# _______ _ _ _ ______ _
|
5
|
+
# |__ __| | | | | (_) | ____| | |
|
6
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
7
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
8
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
9
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
10
|
+
# __/ |
|
11
|
+
# |___/
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
# a copy of this software and associated documentation files (the
|
14
|
+
# "Software"), to deal in the Software without restriction, including
|
15
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
# the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be
|
21
|
+
# included in all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
26
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
27
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
28
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
29
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
#++
|
31
|
+
|
32
|
+
class Tml::Generators::Cache::Base
|
33
|
+
|
34
|
+
attr_accessor :started_at, :finished_at
|
35
|
+
|
36
|
+
def log(msg)
|
37
|
+
msg = "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}: #{msg}"
|
38
|
+
puts msg
|
39
|
+
Tml.logger.debug(msg)
|
40
|
+
end
|
41
|
+
|
42
|
+
def cache_path
|
43
|
+
raise Tml::Exception.new('Must be implemented by the subclass')
|
44
|
+
end
|
45
|
+
|
46
|
+
def cache_version
|
47
|
+
@started_at.strftime('%Y%m%d%H%M%S')
|
48
|
+
end
|
49
|
+
|
50
|
+
def cache(key, data)
|
51
|
+
raise Tml::Exception.new('Must be implemented by the subclass')
|
52
|
+
end
|
53
|
+
|
54
|
+
def execute
|
55
|
+
raise Tml::Exception.new('Must be implemented by the subclass')
|
56
|
+
end
|
57
|
+
|
58
|
+
def run
|
59
|
+
prepare
|
60
|
+
execute
|
61
|
+
finalize
|
62
|
+
end
|
63
|
+
|
64
|
+
def prepare
|
65
|
+
@started_at = Time.now
|
66
|
+
Tml.session.init
|
67
|
+
cache_path
|
68
|
+
end
|
69
|
+
|
70
|
+
def api_client
|
71
|
+
Tml.session.application.api_client
|
72
|
+
end
|
73
|
+
|
74
|
+
def application
|
75
|
+
@application ||= api_client.get('applications/current', {:definition => true})
|
76
|
+
end
|
77
|
+
|
78
|
+
def languages
|
79
|
+
application['languages']
|
80
|
+
end
|
81
|
+
|
82
|
+
def finalize
|
83
|
+
@finished_at = Time.now
|
84
|
+
log("Cache has been stored in #{cache_path}")
|
85
|
+
log("Cache generation took #{@finished_at - @started_at} mls.")
|
86
|
+
log('Done.')
|
87
|
+
end
|
88
|
+
|
89
|
+
def cache_application
|
90
|
+
log('Downloading application...')
|
91
|
+
cache(Tml::Application.cache_key, application)
|
92
|
+
log('Application has been cached.')
|
93
|
+
end
|
94
|
+
|
95
|
+
def cache_languages
|
96
|
+
log('Downloading languages...')
|
97
|
+
unless application['languages']
|
98
|
+
log('No languages are available...')
|
99
|
+
return
|
100
|
+
end
|
101
|
+
languages.each do |lang|
|
102
|
+
language = api_client.get("languages/#{lang['locale']}", :definition => true)
|
103
|
+
cache(Tml::Language.cache_key(language['locale']), language)
|
104
|
+
end
|
105
|
+
log("#{application['languages'].count} languages have been cached.")
|
106
|
+
end
|
107
|
+
|
108
|
+
def symlink_path
|
109
|
+
"#{Tml.config.cache[:path]}/current"
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_symlink
|
113
|
+
FileUtils.rm(symlink_path) if File.exist?(symlink_path)
|
114
|
+
FileUtils.ln_s(cache_version, symlink_path)
|
115
|
+
log('Symlink has been updated.')
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
|
3
|
+
#
|
4
|
+
# _______ _ _ _ ______ _
|
5
|
+
# |__ __| | | | | (_) | ____| | |
|
6
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
7
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
8
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
9
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
10
|
+
# __/ |
|
11
|
+
# |___/
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
# a copy of this software and associated documentation files (the
|
14
|
+
# "Software"), to deal in the Software without restriction, including
|
15
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
# the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be
|
21
|
+
# included in all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
26
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
27
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
28
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
29
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
#++
|
31
|
+
|
32
|
+
class Tml::Generators::Cache::File < Tml::Generators::Cache::Base
|
33
|
+
|
34
|
+
def cache_path
|
35
|
+
@cache_path ||= begin
|
36
|
+
path = "#{Tml.config.cache[:path]}/#{cache_version}"
|
37
|
+
log("Cache will be stored in #{path}")
|
38
|
+
FileUtils.mkdir_p(path)
|
39
|
+
FileUtils.chmod(0777, path)
|
40
|
+
path
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def file_path(key)
|
45
|
+
path = key.split('/')
|
46
|
+
if path.count > 1
|
47
|
+
filename = path.pop
|
48
|
+
path = File.join(cache_path, path)
|
49
|
+
FileUtils.mkdir_p(path)
|
50
|
+
File.join(path, "#{filename}.json")
|
51
|
+
else
|
52
|
+
File.join(cache_path, "#{path.first}.json")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cache(key, data)
|
57
|
+
File.open(file_path(key), 'w') { |file| file.write(JSON.pretty_generate(data)) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def execute
|
61
|
+
cache_application
|
62
|
+
cache_languages
|
63
|
+
cache_translations
|
64
|
+
generate_symlink
|
65
|
+
end
|
66
|
+
|
67
|
+
def cache_translations
|
68
|
+
log('Downloading translations...')
|
69
|
+
|
70
|
+
languages.each do |language|
|
71
|
+
log("Downloading #{language['english_name']} language...")
|
72
|
+
|
73
|
+
if Tml.config.cache[:segmented]
|
74
|
+
api_client.paginate('applications/current/sources') do |source|
|
75
|
+
next unless source['source']
|
76
|
+
|
77
|
+
cache_path = Tml::Source.cache_key(language['locale'], source['source'])
|
78
|
+
log("Downloading #{source['source']} in #{language['locale']} to #{cache_path}...")
|
79
|
+
|
80
|
+
data = api_client.get("sources/#{source['key']}/translations", {:locale => language['locale'], :original => true, :per_page => 1000})
|
81
|
+
cache(cache_path, data)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
cache_path = Tml::Application.translations_cache_key(language['locale'])
|
85
|
+
log("Downloading translations in #{language['locale']} to #{cache_path}...")
|
86
|
+
data = {}
|
87
|
+
api_client.paginate('applications/current/translations', {:locale => language['locale'], :original => true, :per_page => 1000}) do |translations|
|
88
|
+
data.merge!(translations)
|
89
|
+
end
|
90
|
+
cache(cache_path, data)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def rollback
|
96
|
+
folders = Dir["#{Tml.config.cache[:path]}/*"]
|
97
|
+
folders.delete_if{|e| e.index('current')}.sort!
|
98
|
+
|
99
|
+
if File.exist?(symlink_path)
|
100
|
+
current_dest = File.readlink("#{Tml.config.cache[:path]}/current")
|
101
|
+
current_dest = "#{Tml.config.cache[:path]}/#{current_dest}"
|
102
|
+
else
|
103
|
+
current_dest = 'undefined'
|
104
|
+
end
|
105
|
+
|
106
|
+
index = folders.index(current_dest)
|
107
|
+
|
108
|
+
if index == 0
|
109
|
+
log('There are no earlier cache versions')
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
if index.nil?
|
114
|
+
new_version_path = folders[folders.size-1]
|
115
|
+
else
|
116
|
+
new_version_path = folders[index-1]
|
117
|
+
end
|
118
|
+
|
119
|
+
new_version_path = new_version_path.split('/').last
|
120
|
+
|
121
|
+
FileUtils.rm(symlink_path) if File.exist?(symlink_path)
|
122
|
+
FileUtils.ln_s(new_version_path, symlink_path)
|
123
|
+
|
124
|
+
log("Cache has been rolled back to version #{new_version_path}.")
|
125
|
+
end
|
126
|
+
|
127
|
+
def rollup
|
128
|
+
folders = Dir["#{Tml.config.cache[:path]}/*"]
|
129
|
+
folders.delete_if{|e| e.index('current')}.sort!
|
130
|
+
|
131
|
+
if File.exist?(symlink_path)
|
132
|
+
current_dest = File.readlink("#{Tml.config.cache[:path]}/current")
|
133
|
+
current_dest = "#{Tml.config.cache[:path]}/#{current_dest}"
|
134
|
+
else
|
135
|
+
current_dest = 'undefined'
|
136
|
+
end
|
137
|
+
|
138
|
+
index = folders.index(current_dest)
|
139
|
+
|
140
|
+
if index == (folders.size - 1)
|
141
|
+
log('You are on the latest version of the cache already. No further versions are available')
|
142
|
+
return
|
143
|
+
end
|
144
|
+
|
145
|
+
if index.nil?
|
146
|
+
new_version_path = folders[0]
|
147
|
+
else
|
148
|
+
new_version_path = folders[index+1]
|
149
|
+
end
|
150
|
+
|
151
|
+
new_version_path = new_version_path.split('/').last
|
152
|
+
|
153
|
+
FileUtils.rm(symlink_path) if File.exist?(symlink_path)
|
154
|
+
FileUtils.ln_s(new_version_path, symlink_path)
|
155
|
+
|
156
|
+
log("Cache has been upgraded to version #{new_version_path}.")
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|