tr8n_core 4.0.1

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