tr8n_core 4.0.4 → 4.0.5
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/LICENSE +1 -1
- data/README.md +44 -6
- data/lib/tr8n/api_client.rb +109 -0
- data/lib/tr8n/application.rb +14 -96
- data/lib/tr8n/base.rb +7 -2
- data/lib/tr8n/cache.rb +2 -1
- data/lib/tr8n/cache_adapters/cdb.rb +2 -1
- data/lib/tr8n/cache_adapters/file.rb +2 -1
- data/lib/tr8n/cache_adapters/memcache.rb +2 -1
- data/lib/tr8n/cache_adapters/redis.rb +2 -1
- data/lib/tr8n/component.rb +2 -1
- data/lib/tr8n/config.rb +13 -3
- data/lib/tr8n/decorators/base.rb +2 -1
- data/lib/tr8n/decorators/default.rb +2 -1
- data/lib/tr8n/decorators/html.rb +2 -1
- data/lib/tr8n/exception.rb +2 -1
- data/lib/tr8n/language.rb +9 -3
- data/lib/tr8n/language_case.rb +5 -1
- data/lib/tr8n/language_case_rule.rb +2 -1
- data/lib/tr8n/language_context.rb +4 -1
- data/lib/tr8n/language_context_rule.rb +2 -1
- data/lib/tr8n/logger.rb +3 -2
- data/lib/tr8n/rules_engine/evaluator.rb +2 -1
- data/lib/tr8n/rules_engine/parser.rb +2 -1
- data/lib/tr8n/session.rb +21 -7
- data/lib/tr8n/source.rb +3 -2
- data/lib/tr8n/tokens/data.rb +319 -325
- data/lib/tr8n/tokens/data_tokenizer.rb +6 -1
- data/lib/tr8n/tokens/decoration_tokenizer.rb +15 -5
- data/lib/tr8n/tokens/hidden.rb +2 -1
- data/lib/tr8n/tokens/method.rb +4 -3
- data/lib/tr8n/tokens/transform.rb +22 -10
- data/lib/tr8n/translation.rb +6 -8
- data/lib/tr8n/translation_key.rb +11 -9
- data/lib/tr8n/translator.rb +2 -1
- data/lib/tr8n/utils.rb +6 -1
- data/lib/tr8n_core.rb +1 -1
- data/lib/tr8n_core/ext/array.rb +1 -1
- data/lib/tr8n_core/ext/date.rb +1 -1
- data/lib/tr8n_core/ext/fixnum.rb +1 -1
- data/lib/tr8n_core/ext/hash.rb +1 -1
- data/lib/tr8n_core/ext/string.rb +1 -1
- data/lib/tr8n_core/ext/time.rb +1 -1
- data/lib/tr8n_core/generators/cache/base.rb +1 -1
- data/lib/tr8n_core/generators/cache/cdb.rb +1 -1
- data/lib/tr8n_core/generators/cache/file.rb +1 -1
- data/lib/tr8n_core/languages/en-US.json +1362 -0
- data/lib/tr8n_core/version.rb +2 -2
- data/spec/language_case_spec.rb +3 -3
- data/spec/tokens/data_spec.rb +38 -84
- metadata +5 -3
data/lib/tr8n/config.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
# encoding: utf-8
|
1
|
+
# encoding: UTF-8
|
3
2
|
#--
|
4
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
5
4
|
#
|
6
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
7
6
|
# a copy of this software and associated documentation files (the
|
@@ -286,6 +285,17 @@ module Tr8n
|
|
286
285
|
# @default_level
|
287
286
|
#end
|
288
287
|
|
288
|
+
def default_language
|
289
|
+
@default_language ||= begin
|
290
|
+
file = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tr8n_core', 'languages', "#{Tr8n.config.default_locale}.json"))
|
291
|
+
Tr8n::Language.new(JSON.parse(File.read(file)))
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def default_application
|
296
|
+
@default_application ||= Tr8n::Application.new(:host => Tr8n::ApiClient::API_HOST)
|
297
|
+
end
|
298
|
+
|
289
299
|
#########################################################
|
290
300
|
## Decorations
|
291
301
|
#########################################################
|
data/lib/tr8n/decorators/base.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
data/lib/tr8n/decorators/html.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
data/lib/tr8n/exception.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
data/lib/tr8n/language.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -26,7 +27,12 @@ class Tr8n::Language < Tr8n::Base
|
|
26
27
|
attributes :locale, :name, :english_name, :native_name, :right_to_left, :flag_url
|
27
28
|
has_many :contexts, :cases
|
28
29
|
|
29
|
-
def
|
30
|
+
def fetch
|
31
|
+
update_attributes(application.api_client.get("language", {:locale => locale}))
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_attributes(attrs)
|
30
36
|
super
|
31
37
|
|
32
38
|
self.attributes[:contexts] = {}
|
@@ -52,7 +58,7 @@ class Tr8n::Language < Tr8n::Base
|
|
52
58
|
contexts.values.detect{|ctx| ctx.applies_to_token?(token_name)}
|
53
59
|
end
|
54
60
|
|
55
|
-
def
|
61
|
+
def case_by_keyword(keyword)
|
56
62
|
cases[keyword]
|
57
63
|
end
|
58
64
|
|
data/lib/tr8n/language_case.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -49,6 +50,9 @@ class Tr8n::LanguageCase < Tr8n::Base
|
|
49
50
|
|
50
51
|
def apply(value, object = nil, options = {})
|
51
52
|
value = value.to_s
|
53
|
+
|
54
|
+
options = options.merge(:skip_decorations => true) if value.index('not_translated')
|
55
|
+
|
52
56
|
html_tokens = value.scan(TR8N_HTML_TAGS_REGEX).uniq
|
53
57
|
sanitized_value = value.gsub(TR8N_HTML_TAGS_REGEX, "")
|
54
58
|
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -78,6 +79,8 @@ class Tr8n::LanguageContext < Tr8n::Base
|
|
78
79
|
else
|
79
80
|
vars[key] = object.send(method)
|
80
81
|
end
|
82
|
+
elsif obj.is_a?(String)
|
83
|
+
vars[key] = obj
|
81
84
|
else
|
82
85
|
vars[key] = obj.send(method)
|
83
86
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
data/lib/tr8n/logger.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -55,7 +56,7 @@ module Tr8n
|
|
55
56
|
|
56
57
|
def trace_api_call(path, params)
|
57
58
|
[:client_secret, :access_token].each do |param|
|
58
|
-
params = params.merge(param => "##filtered##")
|
59
|
+
params = params.merge(param => "##filtered##") if params[param]
|
59
60
|
end
|
60
61
|
debug("api: [/#{path}] #{params.inspect}")
|
61
62
|
stack.push(caller)
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
data/lib/tr8n/session.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -27,20 +28,26 @@ module Tr8n
|
|
27
28
|
Thread.current[:session] ||= Tr8n::Session.new
|
28
29
|
end
|
29
30
|
|
30
|
-
# Acts as a global singleton that holds all Tr8n configuration
|
31
|
-
# The class can be extended with a different implementation, as long as the interface is supported
|
32
31
|
class Session
|
33
32
|
# Session Attributes - Move to Session
|
34
33
|
attr_accessor :application, :current_user, :current_language, :current_translator,
|
35
34
|
:current_source, :current_component, :block_options
|
36
35
|
|
37
|
-
def init
|
38
|
-
|
36
|
+
def init(key = nil, secret = nil, host = nil)
|
37
|
+
key ||= Tr8n.config.application[:key]
|
38
|
+
secret ||= Tr8n.config.application[:secret]
|
39
|
+
host ||= Tr8n.config.application[:host]
|
39
40
|
|
40
|
-
|
41
|
-
|
41
|
+
Tr8n.cache.reset_version
|
42
|
+
|
43
|
+
self.application = Tr8n.cache.fetch(Tr8n::Application.cache_key(key)) do
|
44
|
+
Tr8n.logger.info("Initializing application...")
|
45
|
+
Tr8n::Application.new(:host => host, :key => key, :secret => secret).fetch
|
42
46
|
end
|
47
|
+
|
43
48
|
self.current_source = "/tr8n/core"
|
49
|
+
|
50
|
+
self.application
|
44
51
|
end
|
45
52
|
|
46
53
|
def reset
|
@@ -53,6 +60,13 @@ module Tr8n
|
|
53
60
|
self.block_options= nil
|
54
61
|
end
|
55
62
|
|
63
|
+
def current_language
|
64
|
+
@current_language ||= Tr8n.config.default_language
|
65
|
+
end
|
66
|
+
|
67
|
+
def application
|
68
|
+
@application ||= Tr8n::Application.new(:host => Tr8n::ApiClient::API_HOST)
|
69
|
+
end
|
56
70
|
#########################################################
|
57
71
|
## Block Options
|
58
72
|
#########################################################
|
data/lib/tr8n/source.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c) 2014 Michael Berkovich,
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -56,7 +57,7 @@ class Tr8n::Source < Tr8n::Base
|
|
56
57
|
def fetch_translations_for_language(language, options = {})
|
57
58
|
return translation_keys if translation_keys
|
58
59
|
|
59
|
-
keys_with_translations = application.get("source/translations",
|
60
|
+
keys_with_translations = application.api_client.get("source/translations",
|
60
61
|
{:source => source, :locale => language.locale},
|
61
62
|
{:class => Tr8n::TranslationKey, :attributes => {:application => application}})
|
62
63
|
|
data/lib/tr8n/tokens/data.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#--
|
2
|
-
# Copyright (c)
|
3
|
+
# Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
|
3
4
|
#
|
4
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
6
|
# a copy of this software and associated documentation files (the
|
@@ -21,390 +22,383 @@
|
|
21
22
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
23
|
#++
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
# {count}
|
29
|
-
# {count:number}
|
30
|
-
# {user:gender}
|
31
|
-
# {today:date}
|
32
|
-
# {user_list:list}
|
33
|
-
# {long_token_name}
|
34
|
-
# {user1}
|
35
|
-
# {user1:user}
|
36
|
-
# {user1:user::pos}
|
37
|
-
#
|
38
|
-
# Data tokens can be associated with any rules through the :dependency
|
39
|
-
# notation or using the naming convention of the token suffix, defined
|
40
|
-
# in the tr8n configuration file
|
41
|
-
#
|
42
|
-
#######################################################################
|
43
|
-
|
44
|
-
class Tr8n::Tokens::Data < Tr8n::Base
|
45
|
-
attr_reader :label, :full_name, :short_name, :case_keys, :context_keys
|
46
|
-
|
47
|
-
def self.expression
|
48
|
-
/(\{[^_:][\w]*(:[\w]+)*(::[\w]+)*\})/
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.parse(label, opts = {})
|
52
|
-
tokens = []
|
53
|
-
label.scan(expression).uniq.each do |token_array|
|
54
|
-
tokens << self.new(label, token_array.first)
|
55
|
-
end
|
56
|
-
tokens
|
57
|
-
end
|
58
|
-
|
59
|
-
def initialize(label, token)
|
60
|
-
@label = label
|
61
|
-
@full_name = token
|
62
|
-
parse_elements
|
63
|
-
end
|
25
|
+
module Tr8n
|
26
|
+
module Tokens
|
27
|
+
class Data
|
64
28
|
|
65
|
-
|
66
|
-
name_without_parens = self.full_name[1..-2]
|
67
|
-
name_without_case_keys = name_without_parens.split('::').first.strip
|
29
|
+
attr_reader :label, :full_name, :short_name, :case_keys, :context_keys
|
68
30
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
def name(opts = {})
|
75
|
-
val = short_name
|
76
|
-
val = "#{val}:#{context_keys.join(':')}" if opts[:context_keys] and context_keys.any?
|
77
|
-
val = "#{val}::#{case_keys.join('::')}" if opts[:case_keys] and case_keys.any?
|
78
|
-
val = "{#{val}}" if opts[:parens]
|
79
|
-
val
|
80
|
-
end
|
31
|
+
def self.expression
|
32
|
+
/(\{[^_:][\w]*(:[\w]+)*(::[\w]+)*\})/
|
33
|
+
end
|
81
34
|
|
82
|
-
|
83
|
-
|
84
|
-
|
35
|
+
def self.parse(label, opts = {})
|
36
|
+
tokens = []
|
37
|
+
label.scan(expression).uniq.each do |token_array|
|
38
|
+
tokens << self.new(label, token_array.first)
|
39
|
+
end
|
40
|
+
tokens
|
41
|
+
end
|
85
42
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
43
|
+
def initialize(label, token)
|
44
|
+
@label = label
|
45
|
+
@full_name = token
|
46
|
+
parse_elements
|
47
|
+
end
|
91
48
|
|
92
|
-
|
93
|
-
|
49
|
+
def parse_elements
|
50
|
+
name_without_parens = @full_name[1..-2]
|
51
|
+
name_without_case_keys = name_without_parens.split('::').first.strip
|
94
52
|
|
95
|
-
|
96
|
-
|
97
|
-
|
53
|
+
@short_name = name_without_parens.split(':').first.strip
|
54
|
+
@case_keys = name_without_parens.scan(/(::\w+)/).flatten.uniq.collect{|c| c.gsub('::', '')}
|
55
|
+
@context_keys = name_without_case_keys.scan(/(:\w+)/).flatten.uniq.collect{|c| c.gsub(':', '')}
|
98
56
|
end
|
99
|
-
end
|
100
57
|
|
101
|
-
|
102
|
-
|
103
|
-
|
58
|
+
def name(opts = {})
|
59
|
+
val = short_name
|
60
|
+
val = "#{val}:#{context_keys.join(':')}" if opts[:context_keys] and context_keys.any?
|
61
|
+
val = "#{val}::#{case_keys.join('::')}" if opts[:case_keys] and case_keys.any?
|
62
|
+
val = "{#{val}}" if opts[:parens]
|
63
|
+
val
|
64
|
+
end
|
104
65
|
|
105
|
-
|
106
|
-
|
107
|
-
|
66
|
+
def key
|
67
|
+
short_name.to_sym
|
68
|
+
end
|
108
69
|
|
109
|
-
|
110
|
-
|
70
|
+
# used by the translator submit dialog
|
71
|
+
def name_for_case_keys(keys)
|
72
|
+
keys = [keys] unless keys.is_a?(Array)
|
73
|
+
"#{name}::#{keys.join('::')}"
|
74
|
+
end
|
111
75
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
76
|
+
def context_for_language(language, opts = {})
|
77
|
+
if context_keys.any?
|
78
|
+
ctx = language.context_by_keyword(context_keys.first)
|
79
|
+
else
|
80
|
+
ctx = language.context_by_token_name(short_name)
|
81
|
+
end
|
118
82
|
|
119
|
-
|
120
|
-
|
121
|
-
|
83
|
+
unless opts[:silent]
|
84
|
+
raise Tr8n::Exception.new("Unknown context for a token: #{full_name} in #{language.locale}") unless ctx
|
85
|
+
end
|
122
86
|
|
123
|
-
|
124
|
-
|
87
|
+
ctx
|
88
|
+
end
|
125
89
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
90
|
+
##############################################################################
|
91
|
+
#
|
92
|
+
# returns token object from tokens param
|
93
|
+
#
|
94
|
+
##############################################################################
|
95
|
+
|
96
|
+
def self.token_object(token_values, token_name)
|
97
|
+
return nil if token_values.nil?
|
98
|
+
token_object = Tr8n::Utils.hash_value(token_values, token_name)
|
99
|
+
return token_object.first if token_object.is_a?(Array)
|
100
|
+
if token_object.is_a?(Hash)
|
101
|
+
object = Tr8n::Utils.hash_value(token_object, :object)
|
102
|
+
return object if object
|
103
|
+
end
|
104
|
+
token_object
|
105
|
+
end
|
142
106
|
|
143
|
-
|
144
|
-
|
145
|
-
|
107
|
+
##############################################################################
|
108
|
+
#
|
109
|
+
# tr("Hello {user_list}!", "", {:user_list => [[user1, user2, user3], :name]}}
|
110
|
+
#
|
111
|
+
# first element is an array, the rest of the elements are similar to the
|
112
|
+
# regular tokens lambda, symbol, string, with parameters that follow
|
113
|
+
#
|
114
|
+
# if you want to pass options, then make the second parameter an array as well
|
115
|
+
#
|
116
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], :name]})
|
117
|
+
#
|
118
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], lambda{|user| user.name}]})
|
119
|
+
#
|
120
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], {:attribute => :name})
|
121
|
+
#
|
122
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], {:attribute => :name, :value => "<strong>{$0}</strong>"})
|
123
|
+
#
|
124
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], "<strong>{$0}</strong>")
|
125
|
+
#
|
126
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], :name, {
|
127
|
+
# :limit => 4,
|
128
|
+
# :separator => ', ',
|
129
|
+
# :joiner => 'and',
|
130
|
+
# :remainder => lambda{|elements| tr("#{count||other}", :count => elements.size)},
|
131
|
+
# :expandable => true,
|
132
|
+
# :collapsable => true
|
133
|
+
# })
|
134
|
+
#
|
135
|
+
#
|
136
|
+
##############################################################################
|
137
|
+
def token_value_from_array(params, language, options)
|
138
|
+
list_options = {
|
139
|
+
:description => "List joiner",
|
140
|
+
:limit => 4,
|
141
|
+
:separator => ", ",
|
142
|
+
:joiner => 'and',
|
143
|
+
:less => '{laquo} less',
|
144
|
+
:expandable => true,
|
145
|
+
:collapsable => true
|
146
|
+
}
|
147
|
+
|
148
|
+
objects = params[0]
|
149
|
+
method = params[1]
|
150
|
+
list_options.merge!(params[2]) if params.size > 2
|
151
|
+
list_options[:expandable] = false if options[:skip_decorations]
|
152
|
+
|
153
|
+
values = objects.collect do |obj|
|
154
|
+
if method.is_a?(String)
|
155
|
+
method.gsub("{$0}", sanitize(obj.to_s, obj, language, options.merge(:safe => false)))
|
156
|
+
elsif method.is_a?(Symbol)
|
157
|
+
if obj.is_a?(Hash)
|
158
|
+
value = Tr8n::Utils.hash_value(obj, method)
|
159
|
+
else
|
160
|
+
value = obj.send(method)
|
161
|
+
end
|
162
|
+
sanitize(value, obj, language, options.merge(:safe => false))
|
163
|
+
elsif method.is_a?(Hash)
|
164
|
+
attr = Tr8n::Utils.hash_value(method, :attribute) || Tr8n::Utils.hash_value(method, :property)
|
165
|
+
if obj.is_a?(Hash)
|
166
|
+
value = Tr8n::Utils.hash_value(obj, attr)
|
167
|
+
else
|
168
|
+
value = obj.send(method)
|
169
|
+
end
|
170
|
+
|
171
|
+
hash_value = Tr8n::Utils.hash_value(method, :value)
|
172
|
+
if hash_value
|
173
|
+
hash_value.gsub("{$0}", sanitize(value, obj, language, options.merge(:safe => false)))
|
174
|
+
else
|
175
|
+
sanitize(value, obj, language, options.merge(:safe => false))
|
176
|
+
end
|
177
|
+
elsif method.is_a?(Proc)
|
178
|
+
sanitize(method.call(obj), obj, language, options.merge(:safe => true))
|
179
|
+
end
|
180
|
+
end
|
146
181
|
|
147
|
-
|
148
|
-
|
149
|
-
# gets the value based on various evaluation methods
|
150
|
-
#
|
151
|
-
# examples:
|
152
|
-
#
|
153
|
-
# tr("Hello {user}", "", {:user => [current_user, current_user.name]}}
|
154
|
-
# tr("Hello {user}", "", {:user => [current_user, "{$0} {$1}", "param1"]}}
|
155
|
-
# tr("Hello {user}", "", {:user => [current_user, :name]}}
|
156
|
-
# tr("Hello {user}", "", {:user => [current_user, :method_name, "param1"]}}
|
157
|
-
# tr("Hello {user}", "", {:user => [current_user, lambda{|user| user.name}]}}
|
158
|
-
# tr("Hello {user}", "", {:user => [current_user, lambda{|user, param1| user.name}, "param1"]}}
|
159
|
-
#
|
160
|
-
##############################################################################
|
161
|
-
def evaluate_token_method_array(object, method_array, options, language)
|
162
|
-
# if single object in the array return string value of the object
|
163
|
-
if method_array.size == 1
|
164
|
-
return sanitize(object, object.to_s, options, language)
|
165
|
-
end
|
182
|
+
return values.first if objects.size == 1
|
183
|
+
return values.join(list_options[:separator]) unless list_options[:joiner]
|
166
184
|
|
167
|
-
|
168
|
-
|
169
|
-
params = method_array[2..-1]
|
170
|
-
params_with_object = [object] + params
|
171
|
-
|
172
|
-
# if the second param is a string, substitute all of the numeric params,
|
173
|
-
# with the original object and all the following params
|
174
|
-
if method.is_a?(String)
|
175
|
-
parametrized_value = method.clone
|
176
|
-
if parametrized_value.index("{$")
|
177
|
-
params_with_object.each_with_index do |val, i|
|
178
|
-
parametrized_value.gsub!("{$#{i}}", sanitize(object, val, options.merge(:skip_decorations => true), language))
|
185
|
+
if values.size <= list_options[:limit]
|
186
|
+
return "#{values[0..-2].join(list_options[:separator])} #{language.translate(list_options[:joiner], list_options[:description], {}, options)} #{values.last}"
|
179
187
|
end
|
180
|
-
end
|
181
|
-
return sanitize(object, parametrized_value, options, language)
|
182
|
-
end
|
183
188
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
189
|
+
display_ary = values[0..(list_options[:limit]-1)]
|
190
|
+
remaining_ary = values[list_options[:limit]..-1]
|
191
|
+
result = "#{display_ary.join(list_options[:separator])}"
|
192
|
+
|
193
|
+
unless list_options[:expandable]
|
194
|
+
result << " " << language.translate(list_options[:joiner], list_options[:description], {}, options) << " "
|
195
|
+
if list_options[:remainder] and list_options[:remainder].is_a?(Proc)
|
196
|
+
result << list_options[:remainder].call(remaining_ary)
|
197
|
+
else
|
198
|
+
result << language.translate("{count||other}", list_options[:description], {:count => remaining_ary.size}, options)
|
199
|
+
end
|
200
|
+
return result
|
201
|
+
end
|
188
202
|
|
189
|
-
|
190
|
-
if method.is_a?(Proc)
|
191
|
-
return sanitize(object, method.call(*params_with_object), options, language)
|
192
|
-
end
|
203
|
+
joiner = language.translate(list_options[:joiner], list_options[:description], {}, options)
|
193
204
|
|
194
|
-
|
195
|
-
|
196
|
-
|
205
|
+
uniq_id = Tr8n::TranslationKey.generate_key(label, values.join(","))
|
206
|
+
result << "<span id=\"tr8n_other_link_#{uniq_id}\">"
|
207
|
+
result << " #{joiner} "
|
197
208
|
|
198
|
-
|
199
|
-
if
|
200
|
-
|
209
|
+
result << "<a href='#' onClick=\"Tr8n.Utils.Effects.hide('tr8n_other_link_#{uniq_id}'); Tr8n.Utils.Effects.show('tr8n_other_elements_#{uniq_id}'); return false;\">"
|
210
|
+
if list_options[:remainder] and list_options[:remainder].is_a?(Proc)
|
211
|
+
result << list_options[:remainder].call(remaining_ary)
|
201
212
|
else
|
202
|
-
|
213
|
+
result << language.translate("{count||other}", list_options[:description], {:count => remaining_ary.size}, options)
|
214
|
+
end
|
215
|
+
result << "</a></span>"
|
216
|
+
|
217
|
+
result << "<span id=\"tr8n_other_elements_#{uniq_id}\" style='display:none'>"
|
218
|
+
result << list_options[:separator] << " "
|
219
|
+
result << remaining_ary[0..-2].join(list_options[:separator])
|
220
|
+
result << " #{joiner} "
|
221
|
+
result << remaining_ary.last
|
222
|
+
|
223
|
+
if list_options[:collapsable]
|
224
|
+
result << "<a href='#' style='font-size:smaller;white-space:nowrap' onClick=\"Tr8n.Utils.Effects.show('tr8n_other_link_#{uniq_id}'); Tr8n.Utils.Effects.hide('tr8n_other_elements_#{uniq_id}'); return false;\"> "
|
225
|
+
result << language.translate(list_options[:less], list_options[:description], {}, options)
|
226
|
+
result << "</a>"
|
203
227
|
end
|
204
|
-
end
|
205
228
|
|
206
|
-
|
207
|
-
return raise Tr8n::Exception.new("Hash object is missing a value or attribute key for a token: #{full_name}")
|
229
|
+
result << "</span>"
|
208
230
|
end
|
209
231
|
|
210
|
-
|
211
|
-
|
232
|
+
##############################################################################
|
233
|
+
#
|
234
|
+
# gets the value based on various evaluation methods
|
235
|
+
#
|
236
|
+
# examples:
|
237
|
+
#
|
238
|
+
# tr("Hello {user}", {:user => [current_user, current_user.name]}}
|
239
|
+
# tr("Hello {user}", {:user => [current_user, :name]}}
|
240
|
+
#
|
241
|
+
# tr("Hello {user}", {:user => [{:name => "Michael", :gender => :male}, current_user.name]}}
|
242
|
+
# tr("Hello {user}", {:user => [{:name => "Michael", :gender => :male}, :name]}}
|
243
|
+
#
|
244
|
+
##############################################################################
|
245
|
+
|
246
|
+
def token_value_from_param_array(array, language, options)
|
247
|
+
# if you provided an array, it better have some values
|
248
|
+
if array.size < 2
|
249
|
+
Tr8n.logger.error("Invalid value for array token #{full_name} in #{label}")
|
250
|
+
return full_name
|
251
|
+
end
|
212
252
|
|
213
|
-
|
214
|
-
|
253
|
+
# if the first value of an array is an array handle it here
|
254
|
+
if array[0].is_a?(Array)
|
255
|
+
return token_value_from_array(array, language, options)
|
256
|
+
end
|
215
257
|
|
216
|
-
|
217
|
-
|
218
|
-
|
258
|
+
if array[1].is_a?(String)
|
259
|
+
return sanitize(array[1], array[0], language, options.merge(:safe => true))
|
260
|
+
end
|
219
261
|
|
220
|
-
|
221
|
-
|
262
|
+
if array[1].is_a?(Hash)
|
263
|
+
if array[1].is_a?(Symbol)
|
264
|
+
return sanitize(Tr8n::Utils.hash_value(array[0], array[1]), array[0], language, options.merge(:safe => false))
|
265
|
+
end
|
222
266
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
return sub_object if sub_object
|
227
|
-
end
|
267
|
+
Tr8n.logger.error("Invalid value for array token #{full_name} in #{label}")
|
268
|
+
return full_name
|
269
|
+
end
|
228
270
|
|
229
|
-
|
230
|
-
|
271
|
+
# if second param is symbol, invoke the method on the object with the remaining values
|
272
|
+
if array[1].is_a?(Symbol)
|
273
|
+
return sanitize(array[0].send(array[1]), array[0], language, options.merge(:safe => false))
|
274
|
+
end
|
231
275
|
|
232
|
-
|
233
|
-
|
234
|
-
# tr("Hello {user_list}!", "", {:user_list => [[user1, user2, user3], :name]}}
|
235
|
-
#
|
236
|
-
# first element is an array, the rest of the elements are similar to the
|
237
|
-
# regular tokens lambda, symbol, string, with parameters that follow
|
238
|
-
#
|
239
|
-
# if you want to pass options, then make the second parameter an array as well
|
240
|
-
# tr("{user_list} joined the site", "",
|
241
|
-
# {:user_list => [[user1, user2, user3],
|
242
|
-
# [:name], # this can be any of the value methods
|
243
|
-
# { :expandable => true,
|
244
|
-
# :to_sentence => true,
|
245
|
-
# :limit => 4,
|
246
|
-
# :separator => ',',
|
247
|
-
# :andor => 'and',
|
248
|
-
# :translate_items => false,
|
249
|
-
# :minimizable => true
|
250
|
-
# }
|
251
|
-
# ]
|
252
|
-
# ]})
|
253
|
-
#
|
254
|
-
# acceptable params: expandable,
|
255
|
-
# to_sentence,
|
256
|
-
# limit,
|
257
|
-
# andor,
|
258
|
-
# more_label,
|
259
|
-
# less_label,
|
260
|
-
# separator,
|
261
|
-
# translate_items,
|
262
|
-
# minimizable
|
263
|
-
#
|
264
|
-
##############################################################################
|
265
|
-
def token_array_value(token_value, options, language)
|
266
|
-
objects = token_value.first
|
267
|
-
|
268
|
-
objects = objects.collect do |obj|
|
269
|
-
if token_value[1].is_a?(Array)
|
270
|
-
evaluate_token_method_array(obj, [obj] + token_value[1], options, language)
|
271
|
-
else
|
272
|
-
evaluate_token_method_array(obj, token_value, options, language)
|
276
|
+
Tr8n.logger.error("Invalid value for array token #{full_name} in #{label}")
|
277
|
+
full_name
|
273
278
|
end
|
274
|
-
end
|
275
|
-
|
276
|
-
list_options = {
|
277
|
-
:translate_items => false,
|
278
|
-
:expandable => true,
|
279
|
-
:minimizable => true,
|
280
|
-
:to_sentence => true,
|
281
|
-
:limit => 4,
|
282
|
-
:separator => ", ",
|
283
|
-
:andor => 'and'
|
284
|
-
}
|
285
|
-
|
286
|
-
if token_value[1].is_a?(Array) and token_value.size == 3
|
287
|
-
list_options.merge!(token_value.last)
|
288
|
-
end
|
289
279
|
|
290
|
-
|
280
|
+
##############################################################################
|
281
|
+
#
|
282
|
+
# examples:
|
283
|
+
#
|
284
|
+
# tr("Hello {user}", {:user => {:value => "Michael", :gender => :male}}}
|
285
|
+
#
|
286
|
+
# tr("Hello {user}", {:user => {:object => {:gender => :male}, :value => "Michael"}}}
|
287
|
+
# tr("Hello {user}", {:user => {:object => {:name => "Michael", :gender => :male}, :property => :name}}}
|
288
|
+
# tr("Hello {user}", {:user => {:object => {:name => "Michael", :gender => :male}, :attribute => :name}}}
|
289
|
+
#
|
290
|
+
# tr("Hello {user}", {:user => {:object => user, :value => "Michael"}}}
|
291
|
+
# tr("Hello {user}", {:user => {:object => user, :property => :name}}}
|
292
|
+
# tr("Hello {user}", {:user => {:object => user, :attribute => :name}}}
|
293
|
+
#
|
294
|
+
##############################################################################
|
295
|
+
|
296
|
+
def token_value_from_param_hash(hash, language, options)
|
297
|
+
value = Tr8n::Utils.hash_value(hash, :value)
|
298
|
+
object = Tr8n::Utils.hash_value(hash, :object)
|
299
|
+
|
300
|
+
unless value.nil?
|
301
|
+
return sanitize(value, object || hash, language, options.merge(:safe => true))
|
302
|
+
end
|
291
303
|
|
292
|
-
|
293
|
-
|
304
|
+
if object.nil?
|
305
|
+
Tr8n.logger.error("Missing value for hash token #{full_name} in #{label}")
|
306
|
+
return full_name
|
307
|
+
end
|
294
308
|
|
295
|
-
|
309
|
+
attr = Tr8n::Utils.hash_value(hash, :attribute) || Tr8n::Utils.hash_value(hash, :property)
|
296
310
|
|
297
|
-
|
311
|
+
if object.is_a?(Hash)
|
312
|
+
unless attr.nil?
|
313
|
+
return sanitize(Tr8n::Utils.hash_value(object, attr), object, language, options.merge(:safe => false))
|
314
|
+
end
|
298
315
|
|
299
|
-
|
300
|
-
|
301
|
-
|
316
|
+
Tr8n.logger.error("Missing value for hash token #{full_name} in #{label}")
|
317
|
+
return full_name
|
318
|
+
end
|
302
319
|
|
303
|
-
|
304
|
-
|
305
|
-
result = "#{display_ary.join(list_options[:separator])}"
|
320
|
+
sanitize(object.send(attr), object, language, options.merge(:safe => false))
|
321
|
+
end
|
306
322
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
323
|
+
# evaluate all possible methods for the token value and return sanitized result
|
324
|
+
def token_value(object, language, options = {})
|
325
|
+
return token_value_from_param_array(object, language, options) if object.is_a?(Array)
|
326
|
+
return token_value_from_param_hash(object, language, options) if object.is_a?(Hash)
|
327
|
+
sanitize(object, object, language, options)
|
328
|
+
end
|
312
329
|
|
313
|
-
|
314
|
-
|
315
|
-
result << "<a href='#' onClick=\"Tr8n.Utils.Effects.hide('tr8n_other_link_#{uniq_id}'); Tr8n.Utils.Effects.show('tr8n_other_elements_#{uniq_id}'); return false;\">"
|
316
|
-
result << (list_options[:more_label] ? list_options[:more_label] : "{num|| other}".translate("List elements joiner", {:num => remaining_ary.size}, options))
|
317
|
-
result << "</a></span>"
|
318
|
-
result << "<span id=\"tr8n_other_elements_#{uniq_id}\" style='display:none'>" << list_options[:separator]
|
319
|
-
result << "#{remaining_ary[0..-2].join(list_options[:separator])} #{list_options[:andor].translate("", {}, options)} #{remaining_ary.last}"
|
320
|
-
|
321
|
-
if list_options[:minimizable]
|
322
|
-
result << "<a href='#' style='font-size:smaller;white-space:nowrap' onClick=\"Tr8n.Utils.Effects.show('tr8n_other_link_#{uniq_id}'); Tr8n.Utils.Effects.hide('tr8n_other_elements_#{uniq_id}'); return false;\"> "
|
323
|
-
result << (list_options[:less_label] ? list_options[:less_label] : "{laquo} less".translate("List elements joiner", {}, options))
|
324
|
-
result << "</a>"
|
325
|
-
end
|
330
|
+
def sanitize(value, object, language, options)
|
331
|
+
value = value.to_s
|
326
332
|
|
327
|
-
|
328
|
-
|
333
|
+
unless Tr8n.session.block_options[:skip_html_escaping]
|
334
|
+
if options[:safe] == false
|
335
|
+
value = ERB::Util.html_escape(value)
|
336
|
+
end
|
337
|
+
end
|
329
338
|
|
330
|
-
|
331
|
-
|
332
|
-
# token is an array
|
333
|
-
if object.is_a?(Array)
|
334
|
-
# if you provided an array, it better have some values
|
335
|
-
if object.empty?
|
336
|
-
return raise Tr8n::Exception.new("Invalid array value for a token: #{full_name}")
|
339
|
+
return value unless language_cases_enabled?
|
340
|
+
apply_language_cases(value, object, language, options)
|
337
341
|
end
|
338
342
|
|
339
|
-
|
340
|
-
|
341
|
-
return token_array_value(object, options, language)
|
343
|
+
def language_cases_enabled?
|
344
|
+
Tr8n.session.application and Tr8n.session.application.feature_enabled?(:language_cases)
|
342
345
|
end
|
343
346
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
#
|
350
|
-
#
|
351
|
-
|
352
|
-
|
347
|
+
##############################################################################
|
348
|
+
#
|
349
|
+
# chooses the appropriate case for the token value. case is identified with ::
|
350
|
+
#
|
351
|
+
# examples:
|
352
|
+
#
|
353
|
+
# tr("Hello {user::nom}", "", :user => current_user)
|
354
|
+
# tr("{actor} gave {target::dat} a present", "", :actor => user1, :target => user2)
|
355
|
+
# tr("This is {user::pos} toy", "", :user => current_user)
|
356
|
+
#
|
357
|
+
##############################################################################
|
358
|
+
def apply_case(key, value, object, language, options)
|
359
|
+
lcase = language.case_by_keyword(key)
|
360
|
+
return value unless lcase
|
361
|
+
lcase.apply(value, object, options)
|
353
362
|
end
|
354
363
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
unless attr.nil?
|
360
|
-
if obj.is_a?(Hash)
|
361
|
-
value = hash_value(obj, attr)
|
362
|
-
else
|
363
|
-
value = obj.send(attr)
|
364
|
+
def apply_language_cases(value, object, language, options)
|
365
|
+
case_keys.each do |key|
|
366
|
+
value = apply_case(key, value, object, language, options)
|
364
367
|
end
|
365
|
-
end
|
366
368
|
|
367
|
-
|
368
|
-
return raise Tr8n::Exception.new("Hash object is missing a value or attribute key for a token: #{full_name}")
|
369
|
+
value
|
369
370
|
end
|
370
371
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
# simple token
|
375
|
-
sanitize(object, object.to_s, options, language)
|
376
|
-
end
|
372
|
+
def substitute(label, context, language, options = {})
|
373
|
+
# get the object from the values
|
374
|
+
object = Tr8n::Utils.hash_value(context, key)
|
377
375
|
|
378
|
-
|
379
|
-
|
380
|
-
end
|
376
|
+
# see if the token is a default html token
|
377
|
+
object = Tr8n.config.default_token_value(key) if object.nil?
|
381
378
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
def substitute(label, context, language, options = {})
|
387
|
-
# get the object from the values
|
388
|
-
object = hash_value(context, key, :whole => true)
|
389
|
-
|
390
|
-
# see if the token is a default html token
|
391
|
-
object = Tr8n.config.default_token_value(key) if object.nil?
|
379
|
+
if object.nil? and not context.key?(key)
|
380
|
+
Tr8n.logger.error("Missing value for #{full_name} in #{label}")
|
381
|
+
return label
|
382
|
+
end
|
392
383
|
|
393
|
-
|
394
|
-
|
395
|
-
|
384
|
+
if object.nil? and not Tr8n::Config.allow_nil_token_values?
|
385
|
+
Tr8n.logger.error("Token value is nil for #{full_name} in #{label}")
|
386
|
+
return label
|
387
|
+
end
|
396
388
|
|
397
|
-
|
389
|
+
return label.gsub(full_name, "") if object.nil?
|
398
390
|
|
399
|
-
|
400
|
-
|
401
|
-
|
391
|
+
value = token_value(object, language, options)
|
392
|
+
label.gsub(full_name, value)
|
393
|
+
end
|
402
394
|
|
403
|
-
|
404
|
-
|
405
|
-
|
395
|
+
def sanitized_name
|
396
|
+
name(:parens => true)
|
397
|
+
end
|
406
398
|
|
407
|
-
|
408
|
-
|
399
|
+
def to_s
|
400
|
+
full_name
|
401
|
+
end
|
402
|
+
end
|
409
403
|
end
|
410
404
|
end
|