tr8n_core 4.0.17 → 4.2

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.
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