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