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
data/lib/tr8n/base.rb CHANGED
@@ -58,7 +58,7 @@ class Tr8n::Base
58
58
  method_name = meth.to_s
59
59
  method_suffix = method_name[-1, 1]
60
60
  method_key = method_name.to_sym
61
- if ['=', '?'].include?(method_suffix)
61
+ if %w(= ?).include?(method_suffix)
62
62
  method_key = method_name[0..-2].to_sym
63
63
  end
64
64
 
data/lib/tr8n/cache.rb CHANGED
@@ -32,7 +32,11 @@
32
32
 
33
33
  module Tr8n
34
34
 
35
- CACHE_VERSION_KEY = "__tr8n_version__"
35
+ CACHE_VERSION_KEY = '__tr8n_version__'
36
+
37
+ def self.memory
38
+ @memory ||= Tr8n::CacheAdapters::Memory.new
39
+ end
36
40
 
37
41
  def self.cache
38
42
  @cache ||= begin
@@ -49,6 +53,8 @@ module Tr8n
49
53
  class Cache
50
54
 
51
55
  def version
56
+ Tr8n.config.cache[:version] ||= 1
57
+
52
58
  @version ||= begin
53
59
  v = fetch(CACHE_VERSION_KEY) do
54
60
  {'version' => Tr8n.config.cache[:version]}
@@ -56,6 +62,8 @@ module Tr8n
56
62
  v['version']
57
63
  end
58
64
 
65
+ @version ||= Tr8n.config.cache[:version]
66
+
59
67
  if Tr8n.config.cache[:version] > @version
60
68
  update_version(Tr8n.config.cache[:version])
61
69
  @version = Tr8n.config.cache[:version]
@@ -86,6 +94,10 @@ module Tr8n
86
94
  end
87
95
 
88
96
  def read_only?
97
+ false
98
+ end
99
+
100
+ def segmented?
89
101
  true
90
102
  end
91
103
 
@@ -33,27 +33,30 @@
33
33
  class Tr8n::CacheAdapters::File < Tr8n::Cache
34
34
 
35
35
  def self.cache_path
36
- "#{Tr8n.config.cache[:path]}/files/current"
37
- end
38
-
39
- def self.file_name(key)
40
- "#{key.gsub(/[\.\/]/, '-')}.json"
36
+ "#{Tr8n.config.cache[:path]}/#{Tr8n.config.cache[:version]}"
41
37
  end
42
38
 
43
39
  def self.file_path(key)
44
- "#{cache_path}/#{file_name(key)}"
40
+ File.join(cache_path, "#{key}.json")
45
41
  end
46
42
 
47
43
  def cache_name
48
- "file"
44
+ 'file'
45
+ end
46
+
47
+ def segmented?
48
+ return true if Tr8n.config.cache[:segmented].nil?
49
+ Tr8n.config.cache[:segmented]
49
50
  end
50
51
 
51
52
  def fetch(key, opts = {})
53
+ info("Fetching key: #{key}")
54
+
52
55
  path = self.class.file_path(key)
53
56
 
54
57
  if File.exists?(path)
55
58
  info("Cache hit: #{key}")
56
- return File.read(path)
59
+ return JSON.parse(File.read(path))
57
60
  end
58
61
 
59
62
  info("Cache miss: #{key}")
@@ -64,11 +67,11 @@ class Tr8n::CacheAdapters::File < Tr8n::Cache
64
67
  end
65
68
 
66
69
  def store(key, data, opts = {})
67
- warn("This is a readonly cache")
70
+ warn('This is a readonly cache')
68
71
  end
69
72
 
70
73
  def delete(key, opts = {})
71
- warn("This is a readonly cache")
74
+ warn('This is a readonly cache')
72
75
  end
73
76
 
74
77
  def exist?(key, opts = {})
@@ -76,7 +79,11 @@ class Tr8n::CacheAdapters::File < Tr8n::Cache
76
79
  end
77
80
 
78
81
  def clear(opts = {})
79
- warn("This is a readonly cache")
82
+ warn('This is a readonly cache')
83
+ end
84
+
85
+ def read_only?
86
+ true
80
87
  end
81
88
 
82
89
  end
@@ -64,7 +64,7 @@ class Tr8n::CacheAdapters::Memcache < Tr8n::Cache
64
64
 
65
65
  data
66
66
  rescue Exception => ex
67
- warn("Failed to retrieve data: #{ex.message}")
67
+ warn("Failed to retrieve data: #{key}")
68
68
  return nil unless block_given?
69
69
  yield
70
70
  end
@@ -75,7 +75,7 @@ class Tr8n::CacheAdapters::Memcache < Tr8n::Cache
75
75
  @cache.set(versioned_key(key, opts), data, ttl)
76
76
  data
77
77
  rescue Exception => ex
78
- warn("Failed to store data: #{ex.message}")
78
+ warn("Failed to store data: #{key}")
79
79
  data
80
80
  end
81
81
 
@@ -84,7 +84,7 @@ class Tr8n::CacheAdapters::Memcache < Tr8n::Cache
84
84
  @cache.delete(versioned_key(key, opts))
85
85
  key
86
86
  rescue Exception => ex
87
- warn("Failed to delete data: #{ex.message}")
87
+ warn("Failed to delete data: #{key}")
88
88
  key
89
89
  end
90
90
 
@@ -92,13 +92,13 @@ class Tr8n::CacheAdapters::Memcache < Tr8n::Cache
92
92
  data = @cache.get(versioned_key(key, opts))
93
93
  not data.nil?
94
94
  rescue Exception => ex
95
- warn("Failed to check if key exists: #{ex.message}")
95
+ warn("Failed to check if key exists: #{key}")
96
96
  false
97
97
  end
98
98
 
99
99
  def clear(opts = {})
100
100
  info("Cache clear")
101
101
  rescue Exception => ex
102
- warn("Failed to clear cache: #{ex.message}")
102
+ warn("Failed to clear cache: #{key}")
103
103
  end
104
104
  end
@@ -0,0 +1,85 @@
1
+ # encoding: UTF-8
2
+ #--
3
+ # Copyright (c) 2014 Michael Berkovich, TranslationExchange.com
4
+ #
5
+ # _______ _ _ _ ______ _
6
+ # |__ __| | | | | (_) | ____| | |
7
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
8
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
9
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
10
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
11
+ # __/ |
12
+ # |___/
13
+ # Permission is hereby granted, free of charge, to any person obtaining
14
+ # a copy of this software and associated documentation files (the
15
+ # "Software"), to deal in the Software without restriction, including
16
+ # without limitation the rights to use, copy, modify, merge, publish,
17
+ # distribute, sublicense, and/or sell copies of the Software, and to
18
+ # permit persons to whom the Software is furnished to do so, subject to
19
+ # the following conditions:
20
+ #
21
+ # The above copyright notice and this permission notice shall be
22
+ # included in all copies or substantial portions of the Software.
23
+ #
24
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ #++
32
+
33
+ class Tr8n::CacheAdapters::Memory < Tr8n::Cache
34
+
35
+ def self.cache
36
+ @cache ||= {}
37
+ end
38
+
39
+ def self.clear_cache
40
+ @cache = {}
41
+ end
42
+
43
+ def cache_name
44
+ 'memory'
45
+ end
46
+
47
+ def fetch(key, opts = {})
48
+ return yield if Tr8n.session.inline_mode?
49
+ return yield unless Tr8n.cache.read_only?
50
+
51
+ data = self.class.cache[key]
52
+
53
+ if data
54
+ info("Cache hit: #{key}")
55
+ return data
56
+ end
57
+
58
+ info("Cache miss: #{key}")
59
+
60
+ return nil unless block_given?
61
+
62
+ data = yield
63
+
64
+ store(key, data)
65
+
66
+ data
67
+ end
68
+
69
+ def store(key, data, opts = {})
70
+ self.class.cache[key] = data
71
+ end
72
+
73
+ def delete(key, opts = {})
74
+ self.class.cache[key] = nil
75
+ end
76
+
77
+ def exist?(key, opts = {})
78
+ not self.class.cache[key].nil?
79
+ end
80
+
81
+ def clear(opts = {})
82
+ self.class.clear_cache
83
+ end
84
+
85
+ end
@@ -35,19 +35,19 @@ class Tr8n::Component < Tr8n::Base
35
35
  attributes :key, :name, :description, :state
36
36
 
37
37
  def sources
38
- application.get("component/sources", {:key => key}, {:class => Tr8n::Source, :application => application})
38
+ application.api_client.get("component/sources", {:key => key}, {:class => Tr8n::Source, :application => application})
39
39
  end
40
40
 
41
41
  def translators
42
- application.get("component/translators", {:key => key}, {:class => Tr8n::Translator, :application => application})
42
+ application.api_client.get("component/translators", {:key => key}, {:class => Tr8n::Translator, :application => application})
43
43
  end
44
44
 
45
45
  def languages
46
- application.get("component/languages", {:key => key}, {:class => Tr8n::Language, :application => application})
46
+ application.api_client.get("component/languages", {:key => key}, {:class => Tr8n::Language, :application => application})
47
47
  end
48
48
 
49
49
  def register_source(source)
50
- application.post("component/register_source", {:key => key, :source => source.source})
50
+ application.api_client.post("component/register_source", {:key => key, :source => source.source})
51
51
  end
52
52
 
53
53
  def restricted?
data/lib/tr8n/config.rb CHANGED
@@ -85,7 +85,7 @@ module Tr8n
85
85
 
86
86
  def initialize
87
87
  @enabled = true
88
- @default_locale = 'en-US'
88
+ @default_locale = 'en'
89
89
  @default_level = 0
90
90
  @format = :html
91
91
 
@@ -340,7 +340,7 @@ module Tr8n
340
340
  end
341
341
 
342
342
  def default_application
343
- @default_application ||= Tr8n::Application.new(:host => Tr8n::ApiClient::API_HOST)
343
+ @default_application ||= Tr8n::Application.new(:host => Tr8n::Api::Client::API_HOST)
344
344
  end
345
345
 
346
346
  #########################################################
@@ -383,7 +383,7 @@ module Tr8n
383
383
  end
384
384
 
385
385
  def default_abbr_day_name(index)
386
- "" + default_abbr_day_names[index]
386
+ '' + default_abbr_day_names[index]
387
387
  end
388
388
 
389
389
  def default_month_names
@@ -391,7 +391,7 @@ module Tr8n
391
391
  end
392
392
 
393
393
  def default_month_name(index)
394
- "" + default_month_names[index]
394
+ '' + default_month_names[index]
395
395
  end
396
396
 
397
397
  def default_abbr_month_names
@@ -399,7 +399,7 @@ module Tr8n
399
399
  end
400
400
 
401
401
  def default_abbr_month_name(index)
402
- "" + default_abbr_month_names[index]
402
+ '' + default_abbr_month_names[index]
403
403
  end
404
404
 
405
405
  def default_date_formats
@@ -38,7 +38,15 @@ class Tr8n::Decorators::Base < Tr8n::Base
38
38
  end
39
39
 
40
40
  def decorate(translated_label, translation_language, target_language, translation_key, options = {})
41
- raise Tr8n::Exception.new("Must be implemented by the extending class")
41
+ raise Tr8n::Exception.new('Must be implemented by the extending class')
42
42
  end
43
43
 
44
+ def inline_mode?
45
+ Tr8n.session.current_translator and Tr8n.session.current_translator.inline?
46
+ end
47
+
48
+ def decorate_language_case(language_case, rule, original, transformed, options = {})
49
+ raise Tr8n::Exception.new('Must be implemented by the extending class')
50
+ end
51
+
44
52
  end
@@ -35,5 +35,9 @@ class Tr8n::Decorators::Default < Tr8n::Decorators::Base
35
35
  def decorate(translated_label, translation_language, target_language, translation_key, options = {})
36
36
  translated_label
37
37
  end
38
-
38
+
39
+ def decorate_language_case(language_case, rule, original, transformed, options = {})
40
+ transformed
41
+ end
42
+
39
43
  end
@@ -33,26 +33,21 @@
33
33
  class Tr8n::Decorators::Html < Tr8n::Decorators::Base
34
34
 
35
35
  def decorate(translated_label, translation_language, target_language, translation_key, options = {})
36
- # Tr8n.logger.info("Decorating #{translated_label} of #{translation_language.locale} to #{target_language.locale}")
36
+ #Tr8n.logger.info("Decorating #{translated_label} of #{translation_language.locale} to #{target_language.locale}")
37
+
37
38
  # skip decoration if instructed so
38
39
  return translated_label if options[:skip_decorations]
40
+
39
41
  # if translation key language is the same as target language - skip decorations
40
42
  return translated_label if translation_key.language == target_language
41
- return translated_label unless Tr8n.session.current_translator and Tr8n.session.current_translator.inline?
43
+ return translated_label unless inline_mode?
42
44
  return translated_label if translation_key.locked? and not Tr8n.session.current_translator.manager?
43
45
 
44
- element = 'tr8n:tr'
45
- if options[:use_div]
46
- element = 'div'
47
- elsif options[:use_span]
48
- element = 'span'
49
- end
50
-
51
- classes = ['tr8n_translatable']
46
+ classes = %w(tr8n_translatable)
52
47
 
53
48
  if translation_key.locked?
54
49
  # must be a manager and enabling locking feature
55
- return translated_label unless Tr8n.session.current_translator.feature_enabled?(:show_locked_keys)
50
+ # return translated_label unless Tr8n.session.current_translator.feature_enabled?(:show_locked_keys)
56
51
  classes << 'tr8n_locked'
57
52
  elsif translation_language == translation_key.language
58
53
  classes << 'tr8n_not_translated'
@@ -62,10 +57,46 @@ class Tr8n::Decorators::Html < Tr8n::Decorators::Base
62
57
  classes << 'tr8n_fallback'
63
58
  end
64
59
 
60
+ element = 'span'
61
+ element = 'div' if options[:use_div]
62
+
65
63
  html = "<#{element} class='#{classes.join(' ')}' data-translation_key='#{translation_key.key}' data-target_locale='#{target_language.locale}'>"
66
64
  html << translated_label
67
65
  html << "</#{element}>"
68
66
  html
69
- end
67
+ end
68
+
69
+ def decorate_language_case(language_case, rule, original, transformed, options = {})
70
+ return transformed if options[:skip_decorations] or language_case.language.default?
71
+ return transformed unless inline_mode?
72
+
73
+ data = {
74
+ 'keyword' => language_case.keyword,
75
+ 'language_name' => language_case.language.english_name,
76
+ 'latin_name' => language_case.latin_name,
77
+ 'native_name' => language_case.native_name,
78
+ 'conditions' => (rule ? rule.conditions : ''),
79
+ 'operations' => (rule ? rule.operations : ''),
80
+ 'original' => original,
81
+ 'transformed' => transformed
82
+ }
83
+
84
+ attrs = []
85
+ {
86
+ 'class' => 'tr8n_language_case',
87
+ 'data-locale' => language_case.language.locale,
88
+ 'data-rule' => CGI::escape(Base64.encode64(data.to_json).gsub("\n", ''))
89
+ }.each do |key, value|
90
+ attrs << "#{key}=\"#{value.to_s.gsub('"', "\"")}\""
91
+ end
92
+
93
+ element = 'span'
94
+ element = 'div' if options[:use_div]
95
+
96
+ html = "<#{element} #{attrs.join(' ')}>"
97
+ html << transformed
98
+ html << "</#{element}>"
99
+ html
100
+ end
70
101
 
71
102
  end
data/lib/tr8n/language.rb CHANGED
@@ -35,8 +35,12 @@ class Tr8n::Language < Tr8n::Base
35
35
  attributes :locale, :name, :english_name, :native_name, :right_to_left, :flag_url
36
36
  has_many :contexts, :cases
37
37
 
38
+ def self.cache_key(locale)
39
+ File.join(locale, 'language')
40
+ end
41
+
38
42
  def fetch
39
- update_attributes(application.api_client.get("language", {:locale => locale}, {:cache_key => "#{locale}/language"}))
43
+ update_attributes(application.api_client.get("language/#{locale}", {}, {:cache_key => self.class.cache_key(locale)}))
40
44
  rescue Tr8n::Exception => ex
41
45
  Tr8n.logger.error("Failed to load language: #{ex}")
42
46
  self
@@ -48,14 +52,14 @@ class Tr8n::Language < Tr8n::Base
48
52
  self.attributes[:contexts] = {}
49
53
  if hash_value(attrs, :contexts)
50
54
  hash_value(attrs, :contexts).each do |key, context|
51
- self.attributes[:contexts][key] = Tr8n::LanguageContext.new(context.merge(:language => self))
55
+ self.attributes[:contexts][key] = Tr8n::LanguageContext.new(context.merge(:keyword => key, :language => self))
52
56
  end
53
57
  end
54
58
 
55
59
  self.attributes[:cases] = {}
56
60
  if hash_value(attrs, :cases)
57
61
  hash_value(attrs, :cases).each do |key, lcase|
58
- self.attributes[:cases][key] = Tr8n::LanguageCase.new(lcase.merge(:language => self))
62
+ self.attributes[:cases][key] = Tr8n::LanguageCase.new(lcase.merge(:keyword => key, :language => self))
59
63
  end
60
64
  end
61
65
  end
@@ -82,7 +86,7 @@ class Tr8n::Language < Tr8n::Base
82
86
  end
83
87
 
84
88
  def dir
85
- right_to_left? ? "rtl" : "ltr"
89
+ right_to_left? ? 'rtl' : 'ltr'
86
90
  end
87
91
 
88
92
  def align(dest)
@@ -96,7 +100,7 @@ class Tr8n::Language < Tr8n::Base
96
100
  end
97
101
 
98
102
  def current_source(options)
99
- options[:source] || Tr8n.session.block_options[:source] || Tr8n.session.current_source || 'undefined'
103
+ (options[:source] || Tr8n.session.block_options[:source] || Tr8n.session.current_source || 'undefined').to_s
100
104
  end
101
105
 
102
106
  #######################################################################################################
@@ -129,7 +133,7 @@ class Tr8n::Language < Tr8n::Base
129
133
  :translations => []
130
134
  })
131
135
 
132
- # Tr8n.logger.info("Translating " + params[:label] + " from: " + translation_key.locale.inspect + " to " + locale.inspect)
136
+ #Tr8n.logger.info("Translating " + params[:label] + " from: " + translation_key.locale.inspect + " to " + locale.inspect)
133
137
 
134
138
  params[:tokens] ||= {}
135
139
  params[:tokens][:viewing_user] ||= Tr8n.session.current_user
@@ -143,22 +147,25 @@ class Tr8n::Language < Tr8n::Base
143
147
  ).tr8n_translated
144
148
  end
145
149
 
146
- # should this be done only for testing?
147
- cached_key = application.translation_key(translation_key.key)
148
- if cached_key
149
- return cached_key.translate(self, params[:tokens], params[:options]).tr8n_translated
150
+ cached_translations = application.cached_translations(locale, translation_key.key)
151
+ if cached_translations
152
+ translation_key.set_translations(locale, cached_translations)
153
+ return translation_key.translate(self, params[:tokens], params[:options]).tr8n_translated
150
154
  end
151
155
 
152
156
  source_key = current_source(options)
153
- source = application.source(source_key, locale)
154
157
 
155
- cached_key = source ? source.translation_key(translation_key.key) : nil
156
- if cached_key
157
- translation_key = cached_key
158
+ if Tr8n.cache.segmented? or Tr8n.session.inline_mode?
159
+ source = application.source(source_key, locale)
160
+ cached_translations = source.cached_translations(locale, translation_key.key)
161
+ else
162
+ cached_translations = application.fetch_translations(locale)[translation_key.key]
163
+ end
164
+
165
+ if cached_translations
166
+ translation_key.set_translations(locale, cached_translations)
158
167
  else
159
168
  application.register_missing_key(source_key, translation_key)
160
- memory_cached_translation_key = application.translation_key(translation_key.key)
161
- translation_key = memory_cached_translation_key if memory_cached_translation_key
162
169
  end
163
170
 
164
171
  translation_key.translate(self, params[:tokens], params[:options]).tr8n_translated