tr8n_core 4.0.17 → 4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -9
  3. data/lib/tr8n/{api_client.rb → api/client.rb} +66 -30
  4. data/lib/tr8n/api/post_office.rb +71 -0
  5. data/lib/tr8n/application.rb +115 -102
  6. data/lib/tr8n/base.rb +1 -1
  7. data/lib/tr8n/cache.rb +13 -1
  8. data/lib/tr8n/cache_adapters/file.rb +18 -11
  9. data/lib/tr8n/cache_adapters/memcache.rb +5 -5
  10. data/lib/tr8n/cache_adapters/memory.rb +85 -0
  11. data/lib/tr8n/component.rb +4 -4
  12. data/lib/tr8n/config.rb +5 -5
  13. data/lib/tr8n/decorators/base.rb +9 -1
  14. data/lib/tr8n/decorators/default.rb +5 -1
  15. data/lib/tr8n/decorators/html.rb +43 -12
  16. data/lib/tr8n/language.rb +23 -16
  17. data/lib/tr8n/language_case.rb +5 -29
  18. data/lib/tr8n/language_case_rule.rb +8 -26
  19. data/lib/tr8n/language_context.rb +5 -15
  20. data/lib/tr8n/language_context_rule.rb +3 -18
  21. data/lib/tr8n/logger.rb +16 -4
  22. data/lib/tr8n/session.rb +71 -13
  23. data/lib/tr8n/source.rb +45 -11
  24. data/lib/tr8n/translation.rb +1 -44
  25. data/lib/tr8n/translation_key.rb +10 -22
  26. data/lib/tr8n/translator.rb +5 -13
  27. data/lib/tr8n/utils.rb +6 -2
  28. data/lib/tr8n_core.rb +3 -2
  29. data/lib/tr8n_core/ext/array.rb +1 -1
  30. data/lib/tr8n_core/ext/date.rb +1 -2
  31. data/lib/tr8n_core/ext/fixnum.rb +1 -1
  32. data/lib/tr8n_core/ext/hash.rb +1 -1
  33. data/lib/tr8n_core/ext/string.rb +1 -1
  34. data/lib/tr8n_core/ext/time.rb +1 -1
  35. data/lib/tr8n_core/generators/cache/base.rb +40 -18
  36. data/lib/tr8n_core/generators/cache/cdb.rb +1 -1
  37. data/lib/tr8n_core/generators/cache/file.rb +99 -21
  38. data/lib/tr8n_core/languages/{en-US.json → en.json} +2 -2
  39. data/lib/tr8n_core/version.rb +2 -2
  40. metadata +6 -4
@@ -59,12 +59,14 @@ class Tr8n::LanguageCase < Tr8n::Base
59
59
  def apply(value, object = nil, options = {})
60
60
  value = value.to_s
61
61
 
62
+ decorator = Tr8n::Decorators::Base.decorator
63
+
62
64
  options = options.merge(:skip_decorations => true) if value.index('not_translated')
63
65
 
64
66
  html_tokens = value.scan(TR8N_HTML_TAGS_REGEX).uniq
65
- sanitized_value = value.gsub(TR8N_HTML_TAGS_REGEX, "")
67
+ sanitized_value = value.gsub(TR8N_HTML_TAGS_REGEX, '')
66
68
 
67
- if application.to_s == "phrase"
69
+ if application.to_s == 'phrase'
68
70
  words = [sanitized_value]
69
71
  else
70
72
  words = sanitized_value.split(/[\s\/\\]/).uniq
@@ -84,7 +86,7 @@ class Tr8n::LanguageCase < Tr8n::Base
84
86
  words.each do |word|
85
87
  case_rule = find_matching_rule(word, object)
86
88
  case_value = case_rule ? case_rule.apply(word) : word
87
- transformed_words << decorate(word, case_value, case_rule, options)
89
+ transformed_words << decorator.decorate_language_case(self, case_rule, word, case_value, options)
88
90
  end
89
91
 
90
92
  # replace back the temporary placeholders with the html tokens
@@ -100,30 +102,4 @@ class Tr8n::LanguageCase < Tr8n::Base
100
102
  value
101
103
  end
102
104
 
103
- #######################################################################################################
104
- ## Decoration Methods - TODO: move to decorators
105
- #######################################################################################################
106
-
107
- def decorate(word, case_value, case_rule, options = {})
108
- return case_value if options[:skip_decorations]
109
- return case_value if language.default?
110
- return case_value unless Tr8n.session.current_translator
111
- return case_value unless Tr8n.session.current_translator.inline?
112
-
113
- "<span class='tr8n_language_case' data-case_id='#{id}' data-rule_id='#{case_rule ? case_rule.id : ''}' data-case_key='#{word.gsub("'", "\'")}'>#{case_value}</span>"
114
- end
115
-
116
- #######################################################################################################
117
- ## Cache Methods
118
- #######################################################################################################
119
-
120
- def to_cache_hash
121
- hash = to_hash(:id, :keyword, :description, :latin_name, :native_name, :application)
122
- hash["rules"] = []
123
- rules.each do |rule|
124
- hash["rules"] << rule.to_cache_hash
125
- end
126
- hash
127
- end
128
-
129
105
  end
@@ -35,39 +35,32 @@ class Tr8n::LanguageCaseRule < Tr8n::Base
35
35
  attributes :id, :description, :examples, :conditions, :conditions_expression, :operations, :operations_expression
36
36
 
37
37
  def conditions_expression
38
- self.attributes[:conditions_expression] ||= Tr8n::RulesEngine::Parser.new(conditions).parse
38
+ self.attributes[:conditions_expression] ||= Tr8n::RulesEngine::Parser.new(self.conditions).parse
39
39
  end
40
40
 
41
41
  def operations_expression
42
- self.attributes[:operations_expression] ||= Tr8n::RulesEngine::Parser.new(operations).parse
42
+ self.attributes[:operations_expression] ||= Tr8n::RulesEngine::Parser.new(self.operations).parse
43
43
  end
44
44
 
45
45
  def gender_variables(object)
46
- return {} unless conditions.index('@gender')
47
- return {"@gender" => "unknown"} unless object
46
+ return {} unless self.conditions.index('@gender')
47
+ return {'@gender' => 'unknown'} unless object
48
48
  context = language_case.language.context_by_keyword(:gender)
49
- return {"@gender" => "unknown"} unless context
49
+ return {'@gender' => 'unknown'} unless context
50
50
  context.vars(object)
51
51
  end
52
52
 
53
- #######################################################################################################
54
- ## Evaluation Methods
55
- #######################################################################################################
56
-
57
53
  def evaluate(value, object = nil)
58
54
  return false if conditions.nil?
59
55
 
60
56
  re = Tr8n::RulesEngine::Evaluator.new
61
- re.evaluate(["let", "@value", value])
57
+ re.evaluate(['let', '@value', value])
62
58
 
63
59
  gender_variables(object).each do |key, value|
64
- re.evaluate(["let", key, value])
60
+ re.evaluate(['let', key, value])
65
61
  end
66
62
 
67
63
  re.evaluate(conditions_expression)
68
- rescue Exception => ex
69
- Tr8n.logger.error("Failed to evaluate language case #{conditions}: #{ex.message}")
70
- value
71
64
  end
72
65
 
73
66
  def apply(value)
@@ -75,20 +68,9 @@ class Tr8n::LanguageCaseRule < Tr8n::Base
75
68
  return value if operations.nil?
76
69
 
77
70
  re = Tr8n::RulesEngine::Evaluator.new
78
- re.evaluate(["let", "@value", value])
71
+ re.evaluate(['let', '@value', value])
79
72
 
80
73
  re.evaluate(operations_expression)
81
- rescue Exception => ex
82
- Tr8n.logger.error("Failed to apply language case rule [case: #{language_case.id}] [rule: #{id}] [conds: #{conditions_expression}] [opers: #{operations_expression}]: #{ex.message}")
83
- value
84
- end
85
-
86
- #######################################################################################################
87
- ## Cache Methods
88
- #######################################################################################################
89
-
90
- def to_cache_hash
91
- to_hash(:id, :description, :examples, :conditions, :conditions_expression, :operations, :operations_expression)
92
74
  end
93
75
 
94
76
  end
@@ -41,14 +41,17 @@ class Tr8n::LanguageContext < Tr8n::Base
41
41
  self.attributes[:rules] = {}
42
42
  if hash_value(attrs, :rules)
43
43
  hash_value(attrs, :rules).each do |key, rule|
44
- self.attributes[:rules][key] = Tr8n::LanguageContextRule.new(rule.merge(:language_context => self))
44
+ self.attributes[:rules][key] = Tr8n::LanguageContextRule.new(rule.merge(
45
+ :keyword => key,
46
+ :language_context => self
47
+ ))
45
48
  end
46
49
  end
47
50
  end
48
51
 
49
52
  def config
50
53
  context_rules = Tr8n.config.context_rules
51
- hash_value(context_rules, keyword.to_sym) || {}
54
+ hash_value(context_rules, self.keyword.to_sym) || {}
52
55
  end
53
56
 
54
57
  def token_expression
@@ -111,17 +114,4 @@ class Tr8n::LanguageContext < Tr8n::Base
111
114
  fallback_rule
112
115
  end
113
116
 
114
- #######################################################################################################
115
- ## Cache Methods
116
- #######################################################################################################
117
-
118
- def to_cache_hash
119
- hash = to_hash(:keyword, :description, :keys, :default_key, :token_expression, :variables, :token_mapping)
120
- hash[:rules] = {}
121
- rules.each do |key, rule|
122
- hash[:rules][key] = rule.to_cache_hash
123
- end
124
- hash
125
- end
126
-
127
117
  end
@@ -35,37 +35,22 @@ class Tr8n::LanguageContextRule < Tr8n::Base
35
35
  attributes :keyword, :description, :examples, :conditions, :conditions_expression
36
36
 
37
37
  def fallback?
38
- keyword.to_s.to_sym == :other
38
+ self.keyword.to_s.to_sym == :other
39
39
  end
40
40
 
41
41
  def conditions_expression
42
- self.attributes[:conditions_expression] ||= Tr8n::RulesEngine::Parser.new(conditions).parse
42
+ self.attributes[:conditions_expression] ||= Tr8n::RulesEngine::Parser.new(self.conditions).parse
43
43
  end
44
44
 
45
- #######################################################################################################
46
- ## Evaluation Methods
47
- #######################################################################################################
48
-
49
45
  def evaluate(vars = {})
50
46
  return true if fallback?
51
47
 
52
48
  re = Tr8n::RulesEngine::Evaluator.new
53
49
  vars.each do |key, value|
54
- re.evaluate(["let", key, value])
50
+ re.evaluate(['let', key, value])
55
51
  end
56
52
 
57
53
  re.evaluate(conditions_expression)
58
- #rescue Exception => ex
59
- # Tr8n.logger.error("Failed to evaluate settings context rule #{conditions_expression}: #{ex.message}")
60
- # false
61
- end
62
-
63
- #######################################################################################################
64
- ## Cache Methods
65
- #######################################################################################################
66
-
67
- def to_cache_hash
68
- to_hash(:keyword, :description, :examples, :conditions, :conditions_expression)
69
54
  end
70
55
 
71
56
  end
data/lib/tr8n/logger.rb CHANGED
@@ -62,11 +62,15 @@ module Tr8n
62
62
  @stack ||= []
63
63
  end
64
64
 
65
- def trace_api_call(path, params)
66
- [:client_secret, :access_token].each do |param|
67
- params = params.merge(param => "##filtered##") if params[param]
65
+ def trace_api_call(path, params, opts = {})
66
+ #[:client_secret, :access_token].each do |param|
67
+ # params = params.merge(param => "##filtered##") if params[param]
68
+ #end
69
+ if opts[:method] == :post
70
+ debug("post: [#{path}] #{params.inspect}")
71
+ else
72
+ debug("get: #{path}?#{to_query(params)}")
68
73
  end
69
- debug("api: [/#{path}] #{params.inspect}")
70
74
  stack.push(caller)
71
75
  t0 = Time.now
72
76
  if block_given?
@@ -78,6 +82,14 @@ module Tr8n
78
82
  ret
79
83
  end
80
84
 
85
+ def to_query(hash)
86
+ query = []
87
+ hash.each do |key, value|
88
+ query << "#{key}=#{value}"
89
+ end
90
+ query.join('&')
91
+ end
92
+
81
93
  def trace(message)
82
94
  debug(message)
83
95
  stack.push(caller)
data/lib/tr8n/session.rb CHANGED
@@ -38,19 +38,65 @@ module Tr8n
38
38
 
39
39
  class Session
40
40
  # Session Attributes - Move to Session
41
- attr_accessor :application, :current_user, :current_language, :current_translator,
42
- :current_source, :current_component, :block_options
41
+ attr_accessor :application, :current_user, :current_locale, :current_language, :current_translator,
42
+ :current_source, :current_component, :block_options, :cookie_params, :access_token
43
43
 
44
- def init(key = nil, secret = nil, host = nil)
44
+ def self.access_token
45
+ @access_token
46
+ end
47
+
48
+ def self.access_token=(token)
49
+ @access_token = token
50
+ end
51
+
52
+ def init(opts = {})
45
53
  return unless Tr8n.config.enabled? and Tr8n.config.application
46
54
 
47
- key ||= Tr8n.config.application[:key]
48
- secret ||= Tr8n.config.application[:secret]
49
- host ||= Tr8n.config.application[:host]
55
+ key = opts[:key] || Tr8n.config.application[:key]
56
+ secret = opts[:secret] || Tr8n.config.application[:secret]
57
+ host = opts[:host] || Tr8n.config.application[:host]
58
+
59
+ self.cookie_params = begin
60
+ cookie_name = "tr8n_#{key}"
61
+ if opts[:cookies] and opts[:cookies][cookie_name]
62
+ begin
63
+ HashWithIndifferentAccess.new(Tr8n::Utils.decode_and_verify_params(opts[:cookies][cookie_name], secret))
64
+ rescue Exception => ex
65
+ Tr8n.logger.error("Failed to parse tr8n cookie: #{ex.message}")
66
+ {}
67
+ end
68
+ else
69
+ {}
70
+ end
71
+ end
72
+
73
+ # Tr8n.logger.info(self.cookie_params.inspect)
74
+
75
+ self.access_token = opts[:access_token]
76
+ self.current_user = opts[:user]
77
+ self.current_source = opts[:source] || '/tr8n/core'
78
+ self.current_component = opts[:component]
79
+ self.current_locale = self.cookie_params[:locale] || opts[:locale] || Tr8n.config.default_locale
80
+
81
+ if self.cookie_params['translator']
82
+ self.current_translator = Tr8n::Translator.new(self.cookie_params['translator'])
83
+ end
50
84
 
51
85
  Tr8n.cache.reset_version
52
- self.current_source = '/tr8n/core' # default source
53
- self.application = Tr8n::Application.new(:host => host, :key => key, :secret => secret).fetch
86
+
87
+ self.application = Tr8n.memory.fetch(Tr8n::Application.cache_key) do
88
+ Tr8n::Application.new(:host => host, :key => key, :secret => secret, :access_token => self.class.access_token).fetch
89
+ end
90
+
91
+ if Tr8n.cache.read_only?
92
+ self.class.access_token = self.application.access_token
93
+ end
94
+
95
+ if self.current_translator
96
+ self.current_translator.application = self.application
97
+ end
98
+
99
+ self.current_language = self.application.language(self.current_locale)
54
100
  end
55
101
 
56
102
  def reset
@@ -68,7 +114,7 @@ module Tr8n
68
114
  end
69
115
 
70
116
  def application
71
- @application ||= Tr8n::Application.new(:host => Tr8n::ApiClient::API_HOST)
117
+ @application ||= Tr8n::Application.new(:host => Tr8n::Api::Client::API_HOST)
72
118
  end
73
119
 
74
120
  def source_language
@@ -82,26 +128,38 @@ module Tr8n
82
128
  def target_language
83
129
  arr = @block_options || []
84
130
  arr.reverse.each do |opts|
85
- return application.language(opts[:target_locale]) unless opts[:target_locale].blank?
131
+ return application.language(opts[:target_locale]) unless opts[:target_locale].nil?
86
132
  end
87
133
  current_language
88
134
  end
89
135
 
136
+ def inline_mode?
137
+ current_translator and current_translator.inline?
138
+ end
139
+
90
140
  #########################################################
91
141
  ## Block Options
92
142
  #########################################################
93
143
 
144
+ def push_block_options(opts)
145
+ (@block_options ||= []).push(opts)
146
+ end
147
+
148
+ def pop_block_options
149
+ return unless @block_options
150
+ @block_options.pop
151
+ end
152
+
94
153
  def block_options
95
154
  (@block_options ||= []).last || {}
96
155
  end
97
156
 
98
157
  def with_block_options(opts)
99
- @block_options ||= []
100
- @block_options.push(opts)
158
+ push_block_options(opts)
101
159
  if block_given?
102
160
  ret = yield
103
161
  end
104
- @block_options.pop if @block_options
162
+ pop_block_options
105
163
  ret
106
164
  end
107
165
 
data/lib/tr8n/source.rb CHANGED
@@ -30,10 +30,12 @@
30
30
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
31
  #++
32
32
 
33
+ require 'digest/md5'
34
+
33
35
  class Tr8n::Source < Tr8n::Base
34
36
  belongs_to :application
35
- attributes :source, :url, :name, :description
36
- has_many :translation_keys
37
+ attributes :key, :source, :url, :name, :description
38
+ has_many :translations
37
39
 
38
40
  def self.normalize(url)
39
41
  return nil if url.nil? or url == ''
@@ -49,24 +51,56 @@ class Tr8n::Source < Tr8n::Base
49
51
  path
50
52
  end
51
53
 
52
- def self.cache_key(key, locale)
53
- "#{locale}/[#{key.gsub(/[\.\/]/, '-')}]"
54
+ def self.generate_key(source)
55
+ "#{Digest::MD5.hexdigest("#{source}")}~"[0..-2]
56
+ end
57
+
58
+ def self.cache_key(locale, source)
59
+ File.join(locale, 'sources', source.split('/'))
54
60
  end
55
61
 
56
62
  def initialize(attrs = {})
57
63
  super
64
+ self.key ||= Tr8n::Source.generate_key(attrs[:source])
65
+ end
66
+
67
+ def fetch_translations(locale)
68
+ self.translations ||= {}
69
+ return self if self.translations[locale]
58
70
 
59
- self.translation_keys = {}
60
- if hash_value(attrs, :translation_keys)
61
- hash_value(attrs, :translation_keys).each do |tk|
62
- tkey = Tr8n::TranslationKey.new(tk.merge(:application => application))
63
- self.translation_keys[tkey.key] = application.cache_translation_key(tkey)
71
+ self.translations[locale] = {}
72
+
73
+ results = self.application.api_client.get(
74
+ "sources/#{self.key}/translations",
75
+ {:locale => locale, :per_page => 10000},
76
+ {:cache_key => Tr8n::Source.cache_key(locale, self.source)}
77
+ )
78
+
79
+ results.each do |key, data|
80
+ translations_data = data.is_a?(Hash) ? data['translations'] : data
81
+ self.translations[locale][key] = translations_data.collect do |t|
82
+ Tr8n::Translation.new(
83
+ :locale => t['locale'] || locale,
84
+ :label => t['label'],
85
+ :context => t['context']
86
+ )
64
87
  end
65
88
  end
89
+
90
+ self
91
+ rescue Tr8n::Exception => ex
92
+ self
66
93
  end
67
94
 
68
- def translation_key(key)
69
- self.translation_keys[key]
95
+ def cached_translations(locale, key)
96
+ self.translations ||= {}
97
+ self.translations[locale] ||= {}
98
+ self.translations[locale][key]
70
99
  end
71
100
 
101
+ def reset_cache
102
+ application.languages.each do |lang|
103
+ Tr8n.cache.delete(Tr8n::Source.cache_key(lang.locale, self.source))
104
+ end
105
+ end
72
106
  end
@@ -34,45 +34,10 @@ class Tr8n::Translation < Tr8n::Base
34
34
  belongs_to :translation_key, :language
35
35
  attributes :locale, :label, :context, :precedence
36
36
 
37
- def initialize(attrs = {})
38
- super
39
-
40
- if locale
41
- self.language = self.translation_key.application.language(locale)
42
- end
43
-
44
- calculate_precedence
45
- end
46
-
47
- # switches to a new translation key
48
- def set_translation_key(tkey)
49
- self.translation_key = tkey
50
- self.language = tkey.application.language(locale)
51
- end
52
-
53
37
  def has_context_rules?
54
38
  context and context.any?
55
39
  end
56
40
 
57
- #
58
- # the precedence is based on the number of fallback rules in the context.
59
- # a fallback rule is indicated by the keyword "other"
60
- # the more "others" are used the lower the precedence will be
61
- #
62
- # 0 indicates the highest precedence
63
- #
64
- # deprecated - this is now done by the service
65
- def calculate_precedence
66
- self.precedence = 0
67
- return unless has_context_rules?
68
-
69
- context.values.each do |rules|
70
- rules.values.each do |rule_key|
71
- self.precedence += 1 if rule_key == "other"
72
- end
73
- end
74
- end
75
-
76
41
  # checks if the translation is valid for the given tokens
77
42
  #{
78
43
  # "count" => {"number":"one"},
@@ -86,7 +51,7 @@ class Tr8n::Translation < Tr8n::Base
86
51
  return false unless token_object
87
52
 
88
53
  rules.each do |context_key, rule_key|
89
- next if rule_key == "other"
54
+ next if rule_key == 'other'
90
55
 
91
56
  context = language.context_by_keyword(context_key)
92
57
  return false unless context
@@ -99,12 +64,4 @@ class Tr8n::Translation < Tr8n::Base
99
64
  true
100
65
  end
101
66
 
102
- #######################################################################################################
103
- ## Cache Methods
104
- #######################################################################################################
105
-
106
- def to_cache_hash
107
- to_hash(:locale, :label, :context, :precedence)
108
- end
109
-
110
67
  end