tr8n_core 4.0.4 → 4.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|