tml 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/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
@@ -0,0 +1,223 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
#######################################################################
|
34
|
+
#
|
35
|
+
# Transform Token Form
|
36
|
+
#
|
37
|
+
# {count:number || one: message, many: messages}
|
38
|
+
# {count:number || one: сообщение, few: сообщения, many: сообщений, other: много сообщений} in other case the number is not displayed#
|
39
|
+
#
|
40
|
+
# {count | message} - will not include {count}, resulting in "messages" with implied {count}
|
41
|
+
# {count | message, messages}
|
42
|
+
#
|
43
|
+
# {count:number | message, messages}
|
44
|
+
#
|
45
|
+
# {user:gender | he, she, he/she}
|
46
|
+
#
|
47
|
+
# {user:gender | male: he, female: she, other: he/she}
|
48
|
+
#
|
49
|
+
# {now:date | did, does, will do}
|
50
|
+
# {users:list | all male, all female, mixed genders}
|
51
|
+
#
|
52
|
+
# {count || message, messages} - will include count: "5 messages"
|
53
|
+
#
|
54
|
+
#######################################################################
|
55
|
+
|
56
|
+
class Tml::Tokens::Transform < Tml::Tokens::Data
|
57
|
+
attr_reader :pipe_separator, :piped_params
|
58
|
+
|
59
|
+
def self.expression
|
60
|
+
/(\{[^_:|][\w]*(:[\w]+)*(::[\w]+)*\s*\|\|?[^{^}]+\})/
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_elements
|
64
|
+
name_without_parens = @full_name[1..-2]
|
65
|
+
name_without_pipes = name_without_parens.split('|').first.strip
|
66
|
+
name_without_case_keys = name_without_pipes.split('::').first.strip
|
67
|
+
|
68
|
+
@short_name = name_without_pipes.split(':').first.strip
|
69
|
+
@case_keys = name_without_pipes.scan(/(::\w+)/).flatten.uniq.collect{|c| c.gsub('::', '')}
|
70
|
+
@context_keys = name_without_case_keys.scan(/(:\w+)/).flatten.uniq.collect{|c| c.gsub(':', '')}
|
71
|
+
|
72
|
+
@pipe_separator = (full_name.index("||") ? "||" : "|")
|
73
|
+
@piped_params = name_without_parens.split(pipe_separator).last.split(",").collect{|param| param.strip}
|
74
|
+
end
|
75
|
+
|
76
|
+
def displayed_in_translation?
|
77
|
+
pipe_separator == "||"
|
78
|
+
end
|
79
|
+
|
80
|
+
def implied?
|
81
|
+
not displayed_in_translation?
|
82
|
+
end
|
83
|
+
|
84
|
+
def prepare_label_for_suggestion(label, index, language)
|
85
|
+
context = context_for_language(language)
|
86
|
+
values = generate_value_map(piped_params, context)
|
87
|
+
|
88
|
+
label.gsub(full_name, values[context.default_rule] || values.values.first)
|
89
|
+
end
|
90
|
+
|
91
|
+
# token: {count|| one: message, many: messages}
|
92
|
+
# results in: {"one": "message", "many": "messages"}
|
93
|
+
#
|
94
|
+
# token: {count|| message}
|
95
|
+
# transform: [{"one": "{$0}", "other": "{$0::plural}"}, {"one": "{$0}", "other": "{$1}"}]
|
96
|
+
# results in: {"one": "message", "other": "messages"}
|
97
|
+
#
|
98
|
+
# token: {count|| message, messages}
|
99
|
+
# transform: [{"one": "{$0}", "other": "{$0::plural}"}, {"one": "{$0}", "other": "{$1}"}]
|
100
|
+
# results in: {"one": "message", "other": "messages"}
|
101
|
+
#
|
102
|
+
# token: {user| Dorogoi, Dorogaya}
|
103
|
+
# transform: ["unsupported", {"male": "{$0}", "female": "{$1}", "other": "{$0}/{$1}"}]
|
104
|
+
# results in: {"male": "Dorogoi", "female": "Dorogaya", "other": "Dorogoi/Dorogaya"}
|
105
|
+
#
|
106
|
+
# token: {actors:|| likes, like}
|
107
|
+
# transform: ["unsupported", {"one": "{$0}", "other": "{$1}"}]
|
108
|
+
# results in: {"one": "likes", "other": "like"}
|
109
|
+
def generate_value_map(params, context)
|
110
|
+
values = {}
|
111
|
+
|
112
|
+
if params.first.index(':')
|
113
|
+
params.each do |p|
|
114
|
+
nv = p.split(':')
|
115
|
+
values[nv.first.strip] = nv.last.strip
|
116
|
+
end
|
117
|
+
return values
|
118
|
+
end
|
119
|
+
|
120
|
+
unless context.token_mapping
|
121
|
+
error("The token context #{context.keyword} does not support transformation for unnamed params: #{full_name}")
|
122
|
+
return nil
|
123
|
+
end
|
124
|
+
|
125
|
+
token_mapping = context.token_mapping
|
126
|
+
|
127
|
+
# "unsupported"
|
128
|
+
if token_mapping.is_a?(String)
|
129
|
+
error("The token mapping #{token_mapping} does not support #{params.size} params: #{full_name}")
|
130
|
+
return nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# ["unsupported", "unsupported", {}]
|
134
|
+
if token_mapping.is_a?(Array)
|
135
|
+
if params.size > token_mapping.size
|
136
|
+
error("The token mapping #{token_mapping} does not support #{params.size} params: #{full_name}")
|
137
|
+
return nil
|
138
|
+
end
|
139
|
+
token_mapping = token_mapping[params.size-1]
|
140
|
+
if token_mapping.is_a?(String)
|
141
|
+
error("The token mapping #{token_mapping} does not support #{params.size} params: #{full_name}")
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# {}
|
147
|
+
token_mapping.each do |key, value|
|
148
|
+
values[key] = value
|
149
|
+
value.scan(/({\$\d(::\w+)*})/).each do |matches|
|
150
|
+
token = matches.first
|
151
|
+
parts = token[1..-2].split('::')
|
152
|
+
index = parts.first.gsub('$', '').to_i
|
153
|
+
|
154
|
+
if params.size < index
|
155
|
+
error("The index inside #{context.token_mapping} is out of bound: #{full_name}")
|
156
|
+
return nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# apply settings cases
|
160
|
+
value = params[index]
|
161
|
+
if language_cases_enabled?
|
162
|
+
parts[1..-1].each do |case_key|
|
163
|
+
lcase = context.language.case_by_keyword(case_key)
|
164
|
+
unless lcase
|
165
|
+
error("Language case #{case_key} for context #{context.keyword} is not defined: #{full_name}")
|
166
|
+
return nil
|
167
|
+
end
|
168
|
+
value = lcase.apply(value)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
values[key] = values[key].gsub(token, value)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
values
|
176
|
+
end
|
177
|
+
|
178
|
+
def substitute(label, context, language, options = {})
|
179
|
+
object = self.class.token_object(context, key)
|
180
|
+
|
181
|
+
unless object
|
182
|
+
return error("Missing value for a token \"#{key}\" in \"#{label}\"", false)
|
183
|
+
end
|
184
|
+
|
185
|
+
if piped_params.empty?
|
186
|
+
return error("Piped params may not be empty for token \"#{key}\" in \"#{label}\"", false)
|
187
|
+
end
|
188
|
+
|
189
|
+
language_context = context_for_language(language)
|
190
|
+
|
191
|
+
unless language_context
|
192
|
+
return error("Unknown context for a token: #{full_name} in #{language.locale}", false)
|
193
|
+
end
|
194
|
+
|
195
|
+
piped_values = generate_value_map(piped_params, language_context)
|
196
|
+
|
197
|
+
unless piped_values
|
198
|
+
return error("Failed to generate value map for: #{full_name} in #{language.locale}", false)
|
199
|
+
end
|
200
|
+
|
201
|
+
rule = language_context.find_matching_rule(object)
|
202
|
+
return label unless rule
|
203
|
+
|
204
|
+
value = piped_values[rule.keyword]
|
205
|
+
if value.nil? and language_context.fallback_rule
|
206
|
+
value = piped_values[language_context.fallback_rule.keyword]
|
207
|
+
end
|
208
|
+
|
209
|
+
return label unless value
|
210
|
+
|
211
|
+
substitution_value = []
|
212
|
+
if displayed_in_translation?
|
213
|
+
substitution_value << token_value(Tml::Utils.hash_value(context, key), language, options)
|
214
|
+
substitution_value << ' '
|
215
|
+
else
|
216
|
+
value = value.gsub("##{short_name}#", token_value(Tml::Utils.hash_value(context, key), language, options))
|
217
|
+
end
|
218
|
+
substitution_value << value
|
219
|
+
|
220
|
+
label.gsub(full_name, substitution_value.join(''))
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
class Tml::Translation < Tml::Base
|
34
|
+
belongs_to :translation_key, :language
|
35
|
+
attributes :locale, :label, :context, :precedence
|
36
|
+
|
37
|
+
def has_context_rules?
|
38
|
+
context and context.any?
|
39
|
+
end
|
40
|
+
|
41
|
+
# checks if the translation is valid for the given tokens
|
42
|
+
#{
|
43
|
+
# "count" => {"number":"one"},
|
44
|
+
# "user" => {"gender":"male"}
|
45
|
+
#}
|
46
|
+
def matches_rules?(token_values)
|
47
|
+
return true unless has_context_rules?
|
48
|
+
|
49
|
+
context.each do |token_name, rules|
|
50
|
+
token_object = Tml::Tokens::Data.token_object(token_values, token_name)
|
51
|
+
return false unless token_object
|
52
|
+
|
53
|
+
rules.each do |context_key, rule_key|
|
54
|
+
next if rule_key == 'other'
|
55
|
+
|
56
|
+
context = language.context_by_keyword(context_key)
|
57
|
+
return false unless context
|
58
|
+
|
59
|
+
rule = context.find_matching_rule(token_object)
|
60
|
+
return false if rule.nil? or rule.keyword != rule_key
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
require 'digest/md5'
|
34
|
+
|
35
|
+
class Tml::TranslationKey < Tml::Base
|
36
|
+
belongs_to :application, :language
|
37
|
+
attributes :id, :key, :label, :description, :locale, :level, :locked
|
38
|
+
has_many :translations # hashed by language
|
39
|
+
|
40
|
+
def initialize(attrs = {})
|
41
|
+
super
|
42
|
+
|
43
|
+
self.attributes[:key] ||= self.class.generate_key(label, description)
|
44
|
+
self.attributes[:locale] ||= Tml.session.block_options[:locale] || (application ? application.default_locale : Tml.config.default_locale)
|
45
|
+
self.attributes[:language] ||= application ? application.language(locale) : Tml.config.default_language
|
46
|
+
self.attributes[:translations] = {}
|
47
|
+
|
48
|
+
if hash_value(attrs, :translations)
|
49
|
+
hash_value(attrs, :translations).each do |locale, translations|
|
50
|
+
language = application.language(locale)
|
51
|
+
|
52
|
+
self.attributes[:translations][locale] ||= []
|
53
|
+
|
54
|
+
translations.each do |translation_hash|
|
55
|
+
translation = Tml::Translation.new(translation_hash.merge(:translation_key => self, :locale => language.locale))
|
56
|
+
self.attributes[:translations][locale] << translation
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.generate_key(label, desc = '')
|
63
|
+
"#{Digest::MD5.hexdigest("#{label};;;#{desc}")}~"[0..-2].to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_translations_for_language?(language)
|
67
|
+
translations and translations[language.locale] and translations[language.locale].any?
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_translations(locale, translations)
|
71
|
+
translations.each do |translation|
|
72
|
+
translation.locale ||= locale
|
73
|
+
translation.translation_key = self
|
74
|
+
translation.language = self.application.language(translation.locale)
|
75
|
+
end
|
76
|
+
self.translations[locale] = translations
|
77
|
+
end
|
78
|
+
|
79
|
+
# switches to a new application
|
80
|
+
def set_application(app)
|
81
|
+
self.application = app
|
82
|
+
translations.values.each do |locale_translations|
|
83
|
+
locale_translations.each do |translation|
|
84
|
+
translation.translation_key = self
|
85
|
+
translation.language = self.application.language(translation.locale)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
###############################################################
|
92
|
+
## Translation Methods
|
93
|
+
###############################################################
|
94
|
+
|
95
|
+
def translations_for_language(language)
|
96
|
+
return [] unless self.translations
|
97
|
+
self.translations[language.locale] || []
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_first_valid_translation(language, token_values)
|
101
|
+
translations = translations_for_language(language)
|
102
|
+
|
103
|
+
translations.sort! { |x,y| x.precedence <=> y.precedence }
|
104
|
+
|
105
|
+
translations.each do |translation|
|
106
|
+
return translation if translation.matches_rules?(token_values)
|
107
|
+
end
|
108
|
+
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def translate(language, token_values = {}, options = {})
|
113
|
+
if Tml.config.disabled? or language.locale == self.locale
|
114
|
+
return substitute_tokens(label, token_values, language, options.merge(:fallback => false))
|
115
|
+
end
|
116
|
+
|
117
|
+
translation = find_first_valid_translation(language, token_values)
|
118
|
+
decorator = Tml::Decorators::Base.decorator
|
119
|
+
|
120
|
+
if translation
|
121
|
+
translated_label = substitute_tokens(translation.label, token_values, translation.language, options)
|
122
|
+
return decorator.decorate(translated_label, translation.language, language, self, options)
|
123
|
+
end
|
124
|
+
|
125
|
+
translated_label = substitute_tokens(label, token_values, self.language, options)
|
126
|
+
decorator.decorate(translated_label, self.language, language, self, options)
|
127
|
+
end
|
128
|
+
|
129
|
+
###############################################################
|
130
|
+
## Token Substitution Methods
|
131
|
+
###############################################################
|
132
|
+
|
133
|
+
# Returns an array of decoration tokens from the translation key
|
134
|
+
def decoration_tokens
|
135
|
+
@decoration_tokens ||= begin
|
136
|
+
dt = Tml::Tokenizers::Decoration.new(label)
|
137
|
+
dt.parse
|
138
|
+
dt.tokens
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns an array of data tokens from the translation key
|
143
|
+
def data_tokens
|
144
|
+
@data_tokens ||= begin
|
145
|
+
dt = Tml::Tokenizers::Data.new(label)
|
146
|
+
dt.tokens
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def data_tokens_names_map
|
151
|
+
@data_tokens_names_map ||= begin
|
152
|
+
map = {}
|
153
|
+
data_tokens.each do |token|
|
154
|
+
map[token.name] = token
|
155
|
+
end
|
156
|
+
map
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# if the translations engine is disabled
|
161
|
+
def self.substitute_tokens(label, token_values, language, options = {})
|
162
|
+
return label.to_s if options[:skip_substitution]
|
163
|
+
Tml::TranslationKey.new(:label => label.to_s).substitute_tokens(label.to_s, token_values, language, options)
|
164
|
+
end
|
165
|
+
|
166
|
+
def substitute_tokens(translated_label, token_values, language, options = {})
|
167
|
+
if Tml::Tokenizers::Data.required?(translated_label)
|
168
|
+
translated_label = Tml::Tokenizers::Data.new(translated_label, token_values, :allowed_tokens => data_tokens_names_map).substitute(language, options)
|
169
|
+
end
|
170
|
+
|
171
|
+
if Tml::Tokenizers::Decoration.required?(translated_label)
|
172
|
+
translated_label = Tml::Tokenizers::Decoration.new(translated_label, token_values, :allowed_tokens => decoration_tokens).substitute
|
173
|
+
end
|
174
|
+
|
175
|
+
translated_label
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|