tr8n_core 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +69 -0
  4. data/Rakefile +9 -0
  5. data/config/config.yml +34 -0
  6. data/config/tokens/data.yml +45 -0
  7. data/config/tokens/decorations.yml +37 -0
  8. data/lib/tr8n/application.rb +320 -0
  9. data/lib/tr8n/base.rb +123 -0
  10. data/lib/tr8n/cache.rb +144 -0
  11. data/lib/tr8n/cache_adapters/cdb.rb +74 -0
  12. data/lib/tr8n/cache_adapters/file.rb +70 -0
  13. data/lib/tr8n/cache_adapters/memcache.rb +91 -0
  14. data/lib/tr8n/cache_adapters/redis.rb +94 -0
  15. data/lib/tr8n/component.rb +68 -0
  16. data/lib/tr8n/config.rb +291 -0
  17. data/lib/tr8n/decorators/base.rb +35 -0
  18. data/lib/tr8n/decorators/default.rb +30 -0
  19. data/lib/tr8n/decorators/html.rb +63 -0
  20. data/lib/tr8n/exception.rb +26 -0
  21. data/lib/tr8n/language.rb +250 -0
  22. data/lib/tr8n/language_case.rb +116 -0
  23. data/lib/tr8n/language_case_rule.rb +85 -0
  24. data/lib/tr8n/language_context.rb +115 -0
  25. data/lib/tr8n/language_context_rule.rb +62 -0
  26. data/lib/tr8n/logger.rb +83 -0
  27. data/lib/tr8n/rules_engine/evaluator.rb +156 -0
  28. data/lib/tr8n/rules_engine/parser.rb +83 -0
  29. data/lib/tr8n/source.rb +95 -0
  30. data/lib/tr8n/tokens/data.rb +410 -0
  31. data/lib/tr8n/tokens/data_tokenizer.rb +82 -0
  32. data/lib/tr8n/tokens/decoration_tokenizer.rb +200 -0
  33. data/lib/tr8n/tokens/hidden.rb +48 -0
  34. data/lib/tr8n/tokens/method.rb +52 -0
  35. data/lib/tr8n/tokens/transform.rb +191 -0
  36. data/lib/tr8n/translation.rb +104 -0
  37. data/lib/tr8n/translation_key.rb +205 -0
  38. data/lib/tr8n/translator.rb +62 -0
  39. data/lib/tr8n/utils.rb +124 -0
  40. data/lib/tr8n_core/ext/array.rb +74 -0
  41. data/lib/tr8n_core/ext/date.rb +63 -0
  42. data/lib/tr8n_core/ext/fixnum.rb +39 -0
  43. data/lib/tr8n_core/ext/hash.rb +126 -0
  44. data/lib/tr8n_core/ext/string.rb +44 -0
  45. data/lib/tr8n_core/ext/time.rb +71 -0
  46. data/lib/tr8n_core/generators/cache/base.rb +85 -0
  47. data/lib/tr8n_core/generators/cache/cdb.rb +27 -0
  48. data/lib/tr8n_core/generators/cache/file.rb +69 -0
  49. data/lib/tr8n_core/modules/logger.rb +43 -0
  50. data/lib/tr8n_core/version.rb +27 -0
  51. data/lib/tr8n_core.rb +68 -0
  52. data/spec/application_spec.rb +228 -0
  53. data/spec/base_spec.rb +19 -0
  54. data/spec/config_spec.rb +16 -0
  55. data/spec/decorator_spec.rb +10 -0
  56. data/spec/decorators/base_spec.rb +14 -0
  57. data/spec/decorators/default_spec.rb +12 -0
  58. data/spec/decorators/html_spec.rb +50 -0
  59. data/spec/fixtures/application.json +112 -0
  60. data/spec/fixtures/languages/en-US.json +1424 -0
  61. data/spec/fixtures/languages/es.json +291 -0
  62. data/spec/fixtures/languages/ru.json +550 -0
  63. data/spec/fixtures/translations/ru/basic.json +56 -0
  64. data/spec/fixtures/translations/ru/counters.json +43 -0
  65. data/spec/fixtures/translations/ru/genders.json +171 -0
  66. data/spec/fixtures/translations/ru/last_names.txt +200 -0
  67. data/spec/fixtures/translations/ru/names.json +1 -0
  68. data/spec/fixtures/translations/ru/names.txt +458 -0
  69. data/spec/helper.rb +84 -0
  70. data/spec/language_case_rule_spec.rb +57 -0
  71. data/spec/language_case_spec.rb +58 -0
  72. data/spec/language_context_rule_spec.rb +73 -0
  73. data/spec/language_context_spec.rb +331 -0
  74. data/spec/language_spec.rb +16 -0
  75. data/spec/rules_engine/evaluator_spec.rb +148 -0
  76. data/spec/rules_engine/parser_spec.rb +29 -0
  77. data/spec/tokens/data_spec.rb +160 -0
  78. data/spec/tokens/data_tokenizer_spec.rb +29 -0
  79. data/spec/tokens/decoration_tokenizer_spec.rb +81 -0
  80. data/spec/tokens/hidden_spec.rb +24 -0
  81. data/spec/tokens/method_spec.rb +84 -0
  82. data/spec/tokens/transform_spec.rb +50 -0
  83. data/spec/translation_key_spec.rb +96 -0
  84. data/spec/translation_spec.rb +24 -0
  85. data/spec/utils_spec.rb +64 -0
  86. metadata +176 -0
@@ -0,0 +1,205 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'digest/md5'
25
+
26
+ class Tr8n::TranslationKey < Tr8n::Base
27
+ belongs_to :application, :language
28
+ attributes :id, :key, :label, :description, :locale, :level, :locked
29
+ has_many :translations # hashed by language
30
+
31
+ def initialize(attrs = {})
32
+ super
33
+
34
+ self.attributes[:key] ||= self.class.generate_key(label, description)
35
+ self.attributes[:locale] ||= Tr8n.config.block_options[:locale] || application.default_locale
36
+ self.attributes[:language] ||= application.language(locale)
37
+ self.attributes[:translations] = {}
38
+
39
+ if hash_value(attrs, :translations)
40
+ hash_value(attrs, :translations).each do |locale, translations|
41
+ language = application.language(locale)
42
+
43
+ self.attributes[:translations][locale] ||= []
44
+
45
+ translations.each do |translation_hash|
46
+ translation = Tr8n::Translation.new(translation_hash.merge(:translation_key => self, :locale => language.locale))
47
+ self.attributes[:translations][locale] << translation
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.generate_key(label, desc = "")
54
+ "#{Digest::MD5.hexdigest("#{label};;;#{desc}")}~"[0..-2].to_s
55
+ end
56
+
57
+ def has_translations_for_language?(language)
58
+ translations and translations[language.locale] and translations[language.locale].any?
59
+ end
60
+
61
+ def fetch_translations(language, options = {})
62
+ return self if self.id and has_translations_for_language?(language)
63
+
64
+ if options[:dry] or Tr8n.config.block_options[:dry]
65
+ return application.cache_translation_key(self)
66
+ end
67
+
68
+ tkey = application.post("translation_key/translations",
69
+ {:key => key, :label => label, :description => description, :locale => language.locale},
70
+ {:class => Tr8n::TranslationKey, :attributes => {:application => application}})
71
+
72
+ application.cache_translation_key(tkey)
73
+ end
74
+
75
+ # switches to a new application
76
+ def set_application(app)
77
+ self.application = app
78
+ translations.values.each do |locale_translations|
79
+ locale_translations.each do |t|
80
+ t.set_translation_key(self)
81
+ end
82
+ end
83
+ self
84
+ end
85
+
86
+ def set_language_translations(language, translations)
87
+ translations.each do |translation|
88
+ translation.locale = language.locale
89
+ translation.set_translation_key(self)
90
+ end
91
+ self.translations[language.locale] = translations
92
+ end
93
+
94
+ ###############################################################
95
+ ## Translation Methods
96
+ ###############################################################
97
+
98
+ def translations_for_language(language)
99
+ return [] unless self.translations
100
+ self.translations[language.locale] || []
101
+ end
102
+
103
+ def find_first_valid_translation(language, token_values)
104
+ translations = translations_for_language(language)
105
+
106
+ translations.sort! { |x,y| x.precedence <=> y.precedence }
107
+
108
+ translations.each do |translation|
109
+ return translation if translation.matches_rules?(token_values)
110
+ end
111
+
112
+ nil
113
+ end
114
+
115
+ def translate(language, token_values = {}, options = {})
116
+ if Tr8n.config.disabled? or language.locale == self.attributes[:locale]
117
+ return substitute_tokens(label, token_values, language, options.merge(:fallback => false))
118
+ end
119
+
120
+ translation = find_first_valid_translation(language, token_values)
121
+ decorator = Tr8n::Decorators::Base.decorator
122
+
123
+ if translation
124
+ translated_label = substitute_tokens(translation.label, token_values, translation.language, options)
125
+ return decorator.decorate(translated_label, translation.language, language, self, options)
126
+ end
127
+
128
+ #Tr8n.logger.info("Translating: #{label}")
129
+
130
+ translated_label = substitute_tokens(label, token_values, self.attributes[:language], options)
131
+ decorator.decorate(translated_label, self.attributes[:language], language, self, options)
132
+ end
133
+
134
+ ###############################################################
135
+ ## Token Substitution Methods
136
+ ###############################################################
137
+
138
+ # Returns an array of decoration tokens from the translation key
139
+ def decoration_tokens
140
+ @decoration_tokens ||= begin
141
+ dt = Tr8n::Tokens::DecorationTokenizer.new(label)
142
+ dt.parse
143
+ dt.tokens
144
+ end
145
+ end
146
+
147
+ # Returns an array of data tokens from the translation key
148
+ def data_tokens
149
+ @data_tokens ||= begin
150
+ dt = Tr8n::Tokens::DataTokenizer.new(label)
151
+ dt.tokens
152
+ end
153
+ end
154
+
155
+ def data_tokens_names_map
156
+ @data_tokens_names_map ||= begin
157
+ map = {}
158
+ data_tokens.each do |token|
159
+ map[token.name] = token
160
+ end
161
+ map
162
+ end
163
+ end
164
+
165
+ # if the translations engine is disabled
166
+ def self.substitute_tokens(label, token_values, language, options = {})
167
+ return label.to_s if options[:skip_substitution]
168
+ Tr8n::TranslationKey.new(:label => label.to_s).substitute_tokens(label.to_s, token_values, language, options)
169
+ end
170
+
171
+ def substitute_tokens(translated_label, token_values, language, options = {})
172
+ if translated_label.index('{')
173
+ dt = Tr8n::Tokens::DataTokenizer.new(translated_label, token_values, :allowed_tokens => data_tokens_names_map)
174
+ translated_label = dt.substitute(language, options)
175
+ end
176
+
177
+ return translated_label unless translated_label.index('[')
178
+ dt = Tr8n::Tokens::DecorationTokenizer.new(translated_label, token_values, :allowed_tokens => decoration_tokens)
179
+ dt.substitute
180
+ end
181
+
182
+ #######################################################################################################
183
+ ## Cache Methods
184
+ #######################################################################################################
185
+
186
+ def self.cache_prefix
187
+ 't@'
188
+ end
189
+
190
+ def self.cache_key(label, description, locale)
191
+ "#{cache_prefix}_[#{locale}]_[#{generate_key(label, description)}]";
192
+ end
193
+
194
+ def to_cache_hash
195
+ hash = to_hash(:id, :key, :label, :description, :locale, :level)
196
+ if translations and translations.any?
197
+ hash["translations"] = {}
198
+ translations.each do |locale, locale_translations|
199
+ hash["translations"][locale] = locale_translations.collect{|t| t.to_cache_hash}
200
+ end
201
+ end
202
+ hash
203
+ end
204
+
205
+ end
@@ -0,0 +1,62 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'digest/md5'
25
+
26
+ class Tr8n::Translator < Tr8n::Base
27
+ belongs_to :application
28
+ attributes :id, :name, :email, :gender, :mugshot, :link, :inline, :features
29
+ attributes :voting_power, :rank, :level, :locale, :manager, :code, :access_token
30
+
31
+ def self.authorize(application, username, password, options = {})
32
+ data = application.get('oauth/request_token', {:grant_type => :password, :username => username, :password => password})
33
+ init(application, data['access_token'])
34
+ end
35
+
36
+ def self.init(application, access_token)
37
+ application.get('translator', {:access_token => access_token}, {:class => Tr8n::Translator, :attributes => {
38
+ :application => application,
39
+ :access_token => access_token
40
+ }})
41
+ end
42
+
43
+ def applications
44
+ application.get("translator/applications", {:access_token => access_token}, {:class => Tr8n::Application})
45
+ end
46
+
47
+ def translations
48
+ application.get("translator/translations", {:access_token => access_token}, {:class => Tr8n::Application})
49
+ end
50
+
51
+ def feature_enabled?(key)
52
+ return false unless features
53
+ hash_value(features, key)
54
+ end
55
+
56
+ def mugshot_url
57
+ return nil unless email
58
+ gravatar_id = Digest::MD5.hexdigest(email.downcase)
59
+ "http://gravatar.com/avatar/#{gravatar_id}.png?s=48"
60
+ end
61
+
62
+ end
data/lib/tr8n/utils.rb ADDED
@@ -0,0 +1,124 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'yaml'
25
+ require 'base64'
26
+ require 'openssl'
27
+ require 'json'
28
+ require 'uri'
29
+
30
+ module Tr8n
31
+ class Utils
32
+ def self.normalize_tr_params(label, description, tokens, options)
33
+ return label if label.is_a?(Hash)
34
+
35
+ if description.is_a?(Hash)
36
+ return {
37
+ :label => label,
38
+ :description => nil,
39
+ :tokens => description,
40
+ :options => tokens
41
+ }
42
+ end
43
+
44
+ {
45
+ :label => label,
46
+ :description => description,
47
+ :tokens => tokens,
48
+ :options => options
49
+ }
50
+ end
51
+
52
+ def self.guid
53
+ (0..16).to_a.map{|a| rand(16).to_s(16)}.join
54
+ end
55
+
56
+ def self.split_by_sentence(text)
57
+ sentence_regex = /[^.!?\s][^.!?]*(?:[.!?](?![\'"]?\s|$)[^.!?]*)*[.!?]?[\'"]?(?=\s|$)/
58
+
59
+ sentences = []
60
+ text.scan(sentence_regex).each do |s|
61
+ sentences << s
62
+ end
63
+
64
+ sentences
65
+ end
66
+
67
+ def self.load_json(file_path, env = nil)
68
+ json = JSON.parse(File.read(file_path))
69
+ return json if env.nil?
70
+ return yml['defaults'] if env == 'defaults'
71
+ yml['defaults'].rmerge(yml[env] || {})
72
+ end
73
+
74
+ def self.load_yaml(file_path, env = nil)
75
+ yaml = YAML.load_file(file_path)
76
+ return yaml if env.nil?
77
+ return yaml['defaults'] if env == 'defaults'
78
+ yaml['defaults'].rmerge(yaml[env] || {})
79
+ end
80
+
81
+ def self.sign_and_encode_params(params, secret)
82
+ payload = params.merge(:algorithm => 'HMAC-SHA256', :ts => Time.now.to_i).to_json
83
+ payload = Base64.encode64(payload)
84
+
85
+ sig = OpenSSL::HMAC.digest('sha256', secret, payload)
86
+ encoded_sig = Base64.encode64(sig)
87
+
88
+ URI::encode(Base64.encode64("#{encoded_sig}.#{payload}"))
89
+ end
90
+
91
+ def self.decode_and_verify_params(signed_request, secret)
92
+ signed_request = URI::decode(signed_request)
93
+ signed_request = Base64.decode64(signed_request)
94
+
95
+ encoded_sig, payload = signed_request.split('.', 2)
96
+ expected_sig = OpenSSL::HMAC.digest('sha256', secret, payload)
97
+ expected_sig = Base64.encode64(expected_sig)
98
+ if expected_sig != encoded_sig
99
+ raise Tr8n::Exception.new("Bad signature")
100
+ end
101
+
102
+ JSON.parse(Base64.decode64(payload))
103
+ end
104
+
105
+ ######################################################################
106
+ # Author: Iain Hecker
107
+ # reference: http://github.com/iain/http_accept_language
108
+ ######################################################################
109
+ def self.browser_accepted_locales(request)
110
+ request.env['HTTP_ACCEPT_LANGUAGE'].split(/\s*,\s*/).collect do |l|
111
+ l += ';q=1.0' unless l =~ /;q=\d+\.\d+$/
112
+ l.split(';q=')
113
+ end.sort do |x,y|
114
+ raise Tr8n::Exception.new("Not correctly formatted") unless x.first =~ /^[a-z\-]+$/i
115
+ y.last.to_f <=> x.last.to_f
116
+ end.collect do |l|
117
+ l.first.downcase.gsub(/-[a-z]+$/i) { |x| x.upcase }
118
+ end
119
+ rescue
120
+ []
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,74 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Array
25
+
26
+ # translates an array of options for a select tag
27
+ def tro(description = "", options = {}, language = Tr8n.config.current_language)
28
+ return [] if empty?
29
+
30
+ collect do |opt|
31
+ if opt.is_a?(Array) and opt.first.is_a?(String)
32
+ [opt.first.trl(description, {}, options, language), opt.last]
33
+ elsif opt.is_a?(String)
34
+ [opt.trl(description, {}, options, language), opt]
35
+ else
36
+ opt
37
+ end
38
+ end
39
+ end
40
+
41
+ # translate array values
42
+ def trl(description = "", options = {}, language = Tr8n.config.current_language)
43
+ return [] if empty?
44
+
45
+ collect do |opt|
46
+ if opt.is_a?(String)
47
+ opt.trl(description, {}, options, language)
48
+ else
49
+ opt
50
+ end
51
+ end
52
+ end
53
+
54
+ # creates a sentence with tr "and" joiner
55
+ def tr_sentence(options = {}, language = Tr8n.config.current_language)
56
+ return "" if empty?
57
+ return first if size == 1
58
+
59
+ result = "#{self[0..-2].join(", ")}"
60
+ result << " " << "and".translate("List elements joiner", {}, options, language) << " "
61
+ result << self.last
62
+ end
63
+
64
+ def tr8n_translated
65
+ return self if frozen?
66
+ @tr8n_translated = true
67
+ self
68
+ end
69
+
70
+ def tr8n_translated?
71
+ @tr8n_translated
72
+ end
73
+
74
+ end
@@ -0,0 +1,63 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Date
25
+
26
+ def translate(format = :default, language = Tr8n.config.current_language, options = {})
27
+ label = (format.is_a?(String) ? format.clone : Tr8n.config.default_date_formats[format].clone)
28
+ symbols = label.scan(/(%\w)/).flatten.uniq
29
+
30
+ selected_tokens = []
31
+ symbols.each do |symbol|
32
+ token = Tr8n.config.strftime_symbol_to_token(symbol)
33
+ next unless token
34
+ selected_tokens << token
35
+ label.gsub!(symbol, token)
36
+ end
37
+
38
+ tokens = {}
39
+ selected_tokens.each do |token|
40
+ case token
41
+ when "{days}" then tokens[:days] = options[:with_leading_zero] ? day.with_leading_zero : day.to_s
42
+ when "{year_days}" then tokens[:year_days] = options[:with_leading_zero] ? yday.with_leading_zero : yday.to_s
43
+ when "{months}" then tokens[:months] = options[:with_leading_zero] ? month.with_leading_zero : month.to_s
44
+ when "{week_num}" then tokens[:week_num] = wday
45
+ when "{week_days}" then tokens[:week_days] = strftime("%w")
46
+ when "{short_years}" then tokens[:short_years] = strftime("%y")
47
+ when "{years}" then tokens[:years] = year
48
+ when "{short_week_day_name}" then tokens[:short_week_day_name] = language.tr(Tr8n.config.default_abbr_day_names[wday], "Short name for a day of a week", {}, options)
49
+ when "{week_day_name}" then tokens[:week_day_name] = language.tr(Tr8n.config.default_day_names[wday], "Day of a week", {}, options)
50
+ when "{short_month_name}" then tokens[:short_month_name] = language.tr(Tr8n.config.default_abbr_month_names[month - 1], "Short month name", {}, options)
51
+ when "{month_name}" then tokens[:month_name] = language.tr(Tr8n.config.default_month_names[month - 1], "Month name", {}, options)
52
+ when "{day_of_month}" then tokens[:day_of_month] = strftime("%e")
53
+ end
54
+ end
55
+
56
+ language.tr(label, nil, tokens, options)
57
+ end
58
+ alias :tr :translate
59
+
60
+ def trl(format = :default, language = Tr8n.config.current_language, options = {})
61
+ tr(format, language, options.merge!(:skip_decorations => true))
62
+ end
63
+ end
@@ -0,0 +1,39 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Fixnum
25
+
26
+ def with_leading_zero
27
+ (to_i < 10 ? "0#{to_s}" : to_s)
28
+ end
29
+
30
+ def translate(desc = "", tokens = {}, options = {}, language = Tr8n.config.current_language)
31
+ to_s.translate(desc, tokens, options, language)
32
+ end
33
+ alias tr translate
34
+
35
+ def trl(desc = "", tokens = {}, options = {}, language = Tr8n.config.current_language)
36
+ to_s.trl(desc, tokens, options, language)
37
+ end
38
+
39
+ end
@@ -0,0 +1,126 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Hash
25
+
26
+ # Return all combinations of a hash.
27
+ #
28
+ # Example:
29
+ # {
30
+ # :a => [1, 2]
31
+ # :b => [1, 2]
32
+ # }.combinations #=> [{:a=>1, :b=>1}, {:a=>1, :b=>2}, {:a=>2, :b=>1}, {:a=>2, :b=>2}]
33
+ #
34
+ def combinations
35
+ return [{}] if empty?
36
+
37
+ copy = dup
38
+ values = copy.delete(key = keys.first)
39
+
40
+ result = []
41
+ copy.combinations.each do |tail|
42
+ values.each do |value|
43
+ result << tail.merge(key=>value)
44
+ end
45
+ end
46
+
47
+ result
48
+ end
49
+
50
+ def tr8n_translated
51
+ return self if frozen?
52
+ @tr8n_translated = true
53
+ self
54
+ end
55
+
56
+ def tr8n_translated?
57
+ @tr8n_translated
58
+ end
59
+
60
+ #
61
+ # = Hash Recursive Merge
62
+ #
63
+ # Merges a Ruby Hash recursively, Also known as deep merge.
64
+ # Recursive version of Hash#merge and Hash#merge!.
65
+ #
66
+ # Category:: Ruby
67
+ # Package:: Hash
68
+ # Author:: Simone Carletti <weppos@weppos.net>
69
+ # Copyright:: 2007-2008 The Authors
70
+ # License:: MIT License
71
+ # Link:: http://www.simonecarletti.com/
72
+ # Source:: http://gist.github.com/gists/6391/
73
+
74
+ #
75
+ # Recursive version of Hash#merge!
76
+ #
77
+ # Adds the contents of +other_hash+ to +hsh+,
78
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
79
+ #
80
+ # Compared with Hash#merge!, this method supports nested hashes.
81
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
82
+ # it merges and returns the values from both arrays.
83
+ #
84
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
85
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
86
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
87
+ #
88
+ # Simply using Hash#merge! would return
89
+ #
90
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
91
+ #
92
+ def rmerge!(other_hash)
93
+ merge!(other_hash) do |key, oldval, newval|
94
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
95
+ end
96
+ end
97
+
98
+ #
99
+ # Recursive version of Hash#merge
100
+ #
101
+ # Compared with Hash#merge!, this method supports nested hashes.
102
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
103
+ # it merges and returns the values from both arrays.
104
+ #
105
+ # Compared with Hash#merge, this method provides a different approch
106
+ # for merging nasted hashes.
107
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
108
+ # includes the same key, the value is merged instead replaced with
109
+ # +other_hash+ value.
110
+ #
111
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
112
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
113
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
114
+ #
115
+ # Simply using Hash#merge would return
116
+ #
117
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
118
+ #
119
+ def rmerge(other_hash)
120
+ r = {}
121
+ merge(other_hash) do |key, oldval, newval|
122
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
123
+ end
124
+ end
125
+
126
+ end