fast_gettext 1.8.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fast_gettext/vendor/mofile'
2
4
  module FastGettext
3
5
  # Responsibility:
4
6
  # - abstract mo files for Mo Repository
5
7
  class MoFile
6
- PLURAL_SEPERATOR = "\000"
8
+ PLURAL_SEPARATOR = "\000"
9
+ CONTEXT_SEPARATOR = "\004"
7
10
 
8
11
  # file => path or FastGettext::GetText::MOFile
9
- def initialize(file, options={})
12
+ def initialize(file, options = {})
10
13
  @filename = file
11
14
  load_data if options[:eager_load]
12
15
  end
@@ -15,17 +18,21 @@ module FastGettext
15
18
  data[key]
16
19
  end
17
20
 
18
- #returns the plural forms or all singular translations that where found
21
+ # returns the plural forms or all singular translations that where found
19
22
  # Car, Cars => [Auto,Autos] or []
20
23
  def plural(*msgids)
21
- split_plurals(self[msgids*PLURAL_SEPERATOR].to_s)
24
+ split_plurals(self[msgids * PLURAL_SEPARATOR].to_s)
22
25
  end
23
26
 
24
27
  def pluralisation_rule
25
- #gettext uses 0 as default rule, which would turn off all pluralisation, very clever...
26
- #additionally parsing fails when directly accessing po files, so this line was taken from gettext/mofile
27
- (data['']||'').split("\n").each do |line|
28
- return lambda{|n|eval($2)} if /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
28
+ # gettext uses 0 as default rule, which would turn off all pluralisation, very clever...
29
+ # additionally parsing fails when directly accessing po files, so this line was taken from gettext/mofile
30
+ (data[''] || '').split("\n").each do |line|
31
+ if /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
32
+ return ->(n) do # rubocop:disable Lint/UnusedBlockArgument
33
+ eval($2) # rubocop:disable Security/Eval
34
+ end
35
+ end
29
36
  end
30
37
  nil
31
38
  end
@@ -36,35 +43,37 @@ module FastGettext
36
43
  end
37
44
 
38
45
  def self.empty
39
- MoFile.new(File.join(File.dirname(__FILE__),'vendor','empty.mo'))
46
+ MoFile.new(File.join(File.dirname(__FILE__), 'vendor', 'empty.mo'))
40
47
  end
41
48
 
42
49
  private
43
50
 
44
51
  def load_data
45
- @data = if @filename.is_a? FastGettext::GetText::MOFile
46
- @filename
47
- else
48
- FastGettext::GetText::MOFile.open(@filename, "UTF-8")
49
- end
52
+ @data =
53
+ if @filename.is_a? FastGettext::GetText::MOFile
54
+ @filename
55
+ else
56
+ FastGettext::GetText::MOFile.open(@filename, "UTF-8")
57
+ end
50
58
  make_singular_and_plural_available
51
59
  end
52
60
 
53
- #(if plural==singular, prefer singular)
61
+ # (if plural==singular, prefer singular)
54
62
  def make_singular_and_plural_available
55
63
  data = {}
56
- @data.each do |key,translation|
57
- next unless key.include? PLURAL_SEPERATOR
64
+ @data.each do |key, translation|
65
+ next unless key.include? PLURAL_SEPARATOR
66
+
58
67
  singular, plural = split_plurals(key)
59
68
  translation = split_plurals(translation)
60
69
  data[singular] ||= translation[0]
61
70
  data[plural] ||= translation[1]
62
71
  end
63
- @data.merge!(data){|key,old,new| old}
72
+ @data.merge!(data) { |_key, old, _new| old }
64
73
  end
65
74
 
66
75
  def split_plurals(singular_plural)
67
- singular_plural.split(PLURAL_SEPERATOR)
76
+ singular_plural.split(PLURAL_SEPARATOR)
68
77
  end
69
78
  end
70
79
  end
@@ -1,29 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fast_gettext/mo_file'
2
4
  module FastGettext
3
5
  # Responsibility:
4
6
  # - abstract po files for Po Repository
5
7
  class PoFile < MoFile
6
- def initialize(file, options={})
8
+ def initialize(file, options = {})
7
9
  @options = options
8
10
  super
9
11
  end
10
12
 
11
- def self.to_mo_file(file, options={})
13
+ def self.to_mo_file(file, options = {})
12
14
  MoFile.new(parse_po_file(file, options))
13
15
  end
14
16
 
15
- protected
16
-
17
- def load_data
18
- @data = if @filename.is_a? FastGettext::GetText::MOFile
19
- @filename
20
- else
21
- FastGettext::PoFile.parse_po_file(@filename, @options)
22
- end
23
- make_singular_and_plural_available
24
- end
25
-
26
- def self.parse_po_file(file, options={})
17
+ def self.parse_po_file(file, options = {})
27
18
  require 'fast_gettext/vendor/poparser'
28
19
  parser = FastGettext::GetText::PoParser.new
29
20
 
@@ -35,5 +26,17 @@ module FastGettext
35
26
  parser.parse_file(file, mo_file)
36
27
  mo_file
37
28
  end
29
+
30
+ protected
31
+
32
+ def load_data
33
+ @data =
34
+ if @filename.is_a? FastGettext::GetText::MOFile
35
+ @filename
36
+ else
37
+ FastGettext::PoFile.parse_po_file(@filename, @options)
38
+ end
39
+ make_singular_and_plural_available
40
+ end
38
41
  end
39
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fast_gettext/cache'
2
4
 
3
5
  module FastGettext
@@ -12,11 +14,15 @@ module FastGettext
12
14
  end
13
15
  end
14
16
 
17
+ DEFAULT_PLURALIZATION_RULE = ->(i) { i != 1 }
18
+
15
19
  [:available_locales, :_locale, :text_domain, :pluralisation_rule].each do |method_name|
16
- key = "fast_gettext_#{method_name}".to_sym
17
- define_method "#{method_name}=" do |value|
18
- switch_cache if Thread.current[key] != Thread.current[key]=value
19
- end
20
+ key = "fast_gettext_#{method_name}"
21
+ eval <<-RUBY, nil, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
22
+ def #{method_name}=(value)
23
+ switch_cache if Thread.current[:#{key}] != Thread.current[:#{key}] = value
24
+ end
25
+ RUBY
20
26
  end
21
27
 
22
28
  def _locale
@@ -24,11 +30,11 @@ module FastGettext
24
30
  end
25
31
  private :_locale, :_locale=
26
32
 
27
-
28
33
  def available_locales
29
34
  locales = Thread.current[:fast_gettext_available_locales] || default_available_locales
30
35
  return unless locales
31
- locales.map{|s|s.to_s}
36
+
37
+ locales.map(&:to_s)
32
38
  end
33
39
 
34
40
  # cattr_accessor with defaults
@@ -37,7 +43,7 @@ module FastGettext
37
43
  [:default_text_domain, "nil"],
38
44
  [:cache_class, "FastGettext::Cache"]
39
45
  ].each do |name, default|
40
- eval <<-Ruby
46
+ eval <<-RUBY, nil, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
41
47
  @@#{name} = #{default}
42
48
  def #{name}=(value)
43
49
  @@#{name} = value
@@ -47,7 +53,7 @@ module FastGettext
47
53
  def #{name}
48
54
  @@#{name}
49
55
  end
50
- Ruby
56
+ RUBY
51
57
  end
52
58
 
53
59
  def text_domain
@@ -57,7 +63,7 @@ module FastGettext
57
63
  # if overwritten by user( FastGettext.pluralisation_rule = xxx) use it,
58
64
  # otherwise fall back to repo or to default lambda
59
65
  def pluralisation_rule
60
- Thread.current[:fast_gettext_pluralisation_rule] || current_repository.pluralisation_rule || lambda{|i| i!=1}
66
+ Thread.current[:fast_gettext_pluralisation_rule] || current_repository.pluralisation_rule || DEFAULT_PLURALIZATION_RULE
61
67
  end
62
68
 
63
69
  def cache
@@ -69,8 +75,8 @@ module FastGettext
69
75
  translation_repositories.values.each(&:reload)
70
76
  end
71
77
 
72
- #global, since re-parsing whole folders takes too much time...
73
- @@translation_repositories={}
78
+ # global, since re-parsing whole folders takes too much time...
79
+ @@translation_repositories = {} # rubocop:disable Style/ClassVars
74
80
  def translation_repositories
75
81
  @@translation_repositories
76
82
  end
@@ -97,7 +103,7 @@ module FastGettext
97
103
  end
98
104
 
99
105
  def locale
100
- _locale || ( default_locale || (available_locales||[]).first || 'en' )
106
+ _locale || (default_locale || (available_locales || []).first || 'en')
101
107
  end
102
108
 
103
109
  def locale=(new_locale)
@@ -107,15 +113,15 @@ module FastGettext
107
113
  # for chaining: puts set_locale('xx') == 'xx' ? 'applied' : 'rejected'
108
114
  # returns the current locale, not the one that was supplied
109
115
  # like locale=(), whoes behavior cannot be changed
110
- def set_locale(new_locale)
116
+ def set_locale(new_locale) # rubocop:disable Naming/AccessorMethodName
111
117
  new_locale = best_locale_in(new_locale)
112
118
  self._locale = new_locale
113
119
  locale
114
120
  end
115
121
 
116
- @@default_locale = nil
122
+ @@default_locale = nil # rubocop:disable Style/ClassVars
117
123
  def default_locale=(new_locale)
118
- @@default_locale = best_locale_in(new_locale)
124
+ @@default_locale = best_locale_in(new_locale) # rubocop:disable Style/ClassVars
119
125
  switch_cache
120
126
  end
121
127
 
@@ -123,17 +129,17 @@ module FastGettext
123
129
  @@default_locale
124
130
  end
125
131
 
126
- #Opera: de-DE,de;q=0.9,en;q=0.8
127
- #Firefox de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
128
- #IE6/7 de
129
- #nil if nothing matches
132
+ # Opera: de-DE,de;q=0.9,en;q=0.8
133
+ # Firefox de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
134
+ # IE6/7 de
135
+ # nil if nothing matches
130
136
  def best_locale_in(locales)
131
137
  formatted_sorted_locales(locales).each do |candidate|
132
- return candidate if not available_locales
138
+ return candidate unless available_locales
133
139
  return candidate if available_locales.include?(candidate)
134
- return candidate[0..1] if available_locales.include?(candidate[0..1])#available locales include a langauge
140
+ return candidate[0..1] if available_locales.include?(candidate[0..1]) # available locales include a langauge
135
141
  end
136
- return nil#nothing found im sorry :P
142
+ nil # nothing found im sorry :P
137
143
  end
138
144
 
139
145
  # temporarily switch locale for a block
@@ -146,27 +152,27 @@ module FastGettext
146
152
  set_locale current_locale
147
153
  end
148
154
 
149
- #turn off translation if none was defined to disable all resulting errors
155
+ # turn off translation if none was defined to disable all resulting errors
150
156
  def silence_errors
151
157
  require 'fast_gettext/translation_repository/base'
152
- translation_repositories[text_domain] ||= TranslationRepository::Base.new('x', :path => 'locale')
158
+ translation_repositories[text_domain] ||= TranslationRepository::Base.new('x', path: 'locale')
153
159
  end
154
160
 
155
161
  private
156
162
 
157
163
  # de-de,DE-CH;q=0.9 -> ['de_DE','de_CH']
158
164
  def formatted_sorted_locales(locales)
159
- found = weighted_locales(locales).reject{|x|x.empty?}.sort_by{|l|l.last}.reverse #sort them by weight which is the last entry
160
- found.flatten.map{|l| format_locale(l)}
165
+ found = weighted_locales(locales).reject(&:empty?).sort_by(&:last).reverse # sort them by weight which is the last entry
166
+ found.flatten.map { |l| format_locale(l) }
161
167
  end
162
168
 
163
- #split the locale and seperate it into different languages
164
- #de-de,de;q=0.9,en;q=0.8 => [['de-de','de','0.5'], ['en','0.8']]
169
+ # split the locale and seperate it into different languages
170
+ # de-de,de;q=0.9,en;q=0.8 => [['de-de','de','0.5'], ['en','0.8']]
165
171
  def weighted_locales(locales)
166
- locales = locales.to_s.gsub(/\s/,'')
172
+ locales = locales.to_s.gsub(/\s/, '')
167
173
  found = [[]]
168
174
  locales.split(',').each do |part|
169
- if part =~ /;q=/ #contains language and weight ?
175
+ if part =~ /;q=/ # contains language and weight ?
170
176
  found.last << part.split(/;q=/)
171
177
  found.last.flatten!
172
178
  found << []
@@ -177,9 +183,9 @@ module FastGettext
177
183
  found
178
184
  end
179
185
 
180
- #de-de -> de_DE
186
+ # de-de -> de_DE
181
187
  def format_locale(locale)
182
- locale.sub(/^([a-zA-Z]{2,3})[-_]([a-zA-Z]{2,3})$/){$1.downcase+'_'+$2.upcase}
188
+ locale.sub(/^([a-zA-Z]{2,3})[-_]([a-zA-Z]{2,3})$/) { $1.downcase + '_' + $2.upcase }
183
189
  end
184
190
 
185
191
  def switch_cache
@@ -1,4 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastGettext
4
+ TRANSLATION_METHODS = [:_, :n_, :s_, :p_, :ns_, :np_].freeze
5
+ NIL_BLOCK = -> { nil }
6
+
2
7
  # this module should be included
3
8
  # Responsibility:
4
9
  # - direct translation queries to the current repository
@@ -6,153 +11,114 @@ module FastGettext
6
11
  # - understand / enforce namespaces
7
12
  # - decide which plural form is used
8
13
  module Translation
9
- extend self
10
-
11
- #make it usable in class definition, e.g.
12
- # class Y
13
- # include FastGettext::Translation
14
- # @@x = _('y')
15
- # end
16
- def self.included(klas) #:nodoc:
17
- klas.extend self
18
- end
19
-
20
- def _(key, &block)
21
- FastGettext.cached_find(key) or (block ? block.call : key)
14
+ def _(key)
15
+ FastGettext.cached_find(key) || (block_given? ? yield : key)
22
16
  end
23
17
 
24
- #translate pluralized
18
+ # translate pluralized
25
19
  # some languages have up to 4 plural forms...
26
20
  # n_(singular, plural, plural form 2, ..., count)
27
21
  # n_('apple','apples',3)
28
- def n_(*keys, &block)
29
- count = keys.pop
22
+ def n_(*keys, count)
30
23
  translations = FastGettext.cached_plural_find(*keys)
31
-
32
24
  selected = FastGettext.pluralisation_rule.call(count)
33
- selected = (selected ? 1 : 0) unless selected.is_a? Numeric #convert booleans to numbers
25
+ selected = (selected ? 1 : 0) unless selected.is_a? Numeric # convert booleans to numbers
34
26
 
27
+ # If we have a translation return it
35
28
  result = translations[selected]
36
- if result
37
- result
38
- elsif keys[selected]
39
- _(keys[selected])
40
- else
41
- block ? block.call : keys.last
42
- end
29
+ return result if result
30
+
31
+ # If we have a block always use it in place of a translation
32
+ return yield if block_given?
33
+
34
+ # Fall back to the best fit translated key if it's there
35
+ _(keys[selected] || keys.last)
43
36
  end
44
37
 
45
- #translate with namespace, use namespect to find key
46
- # 'Car','Tire' -> Tire if no translation could be found
47
- # p_('Car','Tire') <=> s_('Car|Tire')
48
- def p_(namespace, key, separator=nil, &block)
49
- msgid = "#{namespace}#{separator||NAMESPACE_SEPARATOR}#{key}"
50
- FastGettext.cached_find(msgid) or (block ? block.call : key)
38
+ # translate with namespace
39
+ # 'Car', 'Tire' -> Tire if no translation could be found
40
+ # p_('Car', 'Tire') == s_('Car|Tire')
41
+ def p_(namespace, key, separator = nil)
42
+ msgid = "#{namespace}#{separator || CONTEXT_SEPARATOR}#{key}"
43
+
44
+ translation = FastGettext.cached_find(msgid)
45
+ return translation if translation
46
+
47
+ block_given? ? yield : key
51
48
  end
52
49
 
53
- #translate, but discard namespace if nothing was found
50
+ # translate, but discard namespace if nothing was found
54
51
  # Car|Tire -> Tire if no translation could be found
55
- def s_(key, separator=nil, &block)
56
- translation = FastGettext.cached_find(key) and return translation
57
- block ? block.call : key.split(separator||NAMESPACE_SEPARATOR).last
52
+ def s_(key, separator = nil)
53
+ translation = FastGettext.cached_find(key)
54
+ return translation if translation
55
+
56
+ block_given? ? yield : key.split(separator || NAMESPACE_SEPARATOR).last
58
57
  end
59
58
 
60
- #tell gettext: this string need translation (will be found during parsing)
59
+ # tell gettext: this string need translation (will be found during parsing)
61
60
  def N_(translate)
62
61
  translate
63
62
  end
64
63
 
65
- #tell gettext: this string need translation (will be found during parsing)
64
+ # tell gettext: this string need translation (will be found during parsing)
66
65
  def Nn_(*keys)
67
66
  keys
68
67
  end
69
68
 
70
- def ns_(*args, &block)
71
- translation = n_(*args, &block)
72
- # block is called once again to compare result
73
- block && translation == block.call ? translation : translation.split(NAMESPACE_SEPARATOR).last
74
- end
75
- end
69
+ # translate pluralized with separator
70
+ def ns_(*args)
71
+ translation = n_(*args, &NIL_BLOCK)
72
+ return translation if translation
76
73
 
77
- # this module should be included for multi-domain support
78
- module TranslationMultidomain
79
- extend self
80
-
81
- #make it usable in class definition, e.g.
82
- # class Y
83
- # include FastGettext::TranslationMultidomain
84
- # @@x = d_('domain', 'y')
85
- # end
86
- def self.included(klas) #:nodoc:
87
- klas.extend self
88
- end
74
+ return yield if block_given?
89
75
 
90
- # helper block for changing domains
91
- def _in_domain domain
92
- old_domain = FastGettext.text_domain
93
- FastGettext.text_domain = domain
94
- yield if block_given?
95
- ensure
96
- FastGettext.text_domain = old_domain
76
+ n_(*args).split(NAMESPACE_SEPARATOR).last
97
77
  end
98
78
 
99
- # gettext functions to translate in the context of given domain
100
- def d_(domain, key, &block)
101
- _in_domain domain do
102
- FastGettext::Translation._(key, &block)
103
- end
104
- end
105
-
106
- def dn_(domain, *keys, &block)
107
- _in_domain domain do
108
- FastGettext::Translation.n_(*keys, &block)
109
- end
110
- end
79
+ # translate pluralized with context
80
+ def np_(context, plural_one, *args, separator: nil)
81
+ nargs = ["#{context}#{separator || CONTEXT_SEPARATOR}#{plural_one}"] + args
82
+ translation = n_(*nargs, &NIL_BLOCK)
83
+ return translation if translation
111
84
 
112
- def ds_(domain, key, separator=nil, &block)
113
- _in_domain domain do
114
- FastGettext::Translation.s_(key, separator, &block)
115
- end
116
- end
117
-
118
- def dns_(domain, *keys, &block)
119
- _in_domain domain do
120
- FastGettext::Translation.ns_(*keys, &block)
121
- end
122
- end
85
+ return yield if block_given?
123
86
 
124
- # gettext functions to translate in the context of any domain
125
- # (note: if mutiple domains contains key, random translation is returned)
126
- def D_(key)
127
- FastGettext.translation_repositories.each_key do |domain|
128
- result = FastGettext::TranslationMultidomain.d_(domain, key) {nil}
129
- return result unless result.nil?
130
- end
131
- key
87
+ n_(plural_one, *args)
132
88
  end
89
+ end
133
90
 
134
- def Dn_(*keys)
135
- FastGettext.translation_repositories.each_key do |domain|
136
- result = FastGettext::TranslationMultidomain.dn_(domain, *keys) {nil}
137
- return result unless result.nil?
138
- end
139
- keys[-3].split(keys[-2]||NAMESPACE_SEPARATOR).last
140
- end
91
+ module TranslationAliased
92
+ include Translation
93
+ TRANSLATION_METHODS.each { |m| alias_method "#{m.to_s.delete('_')}gettext", m }
94
+ end
141
95
 
142
- def Ds_(key, separator=nil)
143
- FastGettext.translation_repositories.each_key do |domain|
144
- result = FastGettext::TranslationMultidomain.ds_(domain, key, separator) {nil}
145
- return result unless result.nil?
146
- end
147
- key.split(separator||NAMESPACE_SEPARATOR).last
148
- end
96
+ # this module should be included for multi-domain support
97
+ module TranslationMultidomain
98
+ include Translation
149
99
 
150
- def Dns_(*keys)
151
- FastGettext.translation_repositories.each_key do |domain|
152
- result = FastGettext::TranslationMultidomain.dns_(domain, *keys) {nil}
153
- return result unless result.nil?
154
- end
155
- keys[-2].split(NAMESPACE_SEPARATOR).last
100
+ # gettext functions to translate in the context of given domain
101
+ TRANSLATION_METHODS.each do |method|
102
+ eval <<-RUBY, nil, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
103
+ # translate in given domain
104
+ def d#{method}(domain, *args, &block)
105
+ FastGettext.with_domain(domain) { #{method}(*args, &block) }
106
+ end
107
+
108
+ # translate with whatever domain finds a translation
109
+ def D#{method}(*args, &block)
110
+ repos = FastGettext.translation_repositories
111
+ last = repos.size - 1
112
+ repos.each_key.each_with_index do |domain, i|
113
+ if i == last
114
+ return d#{method}(domain, *args, &block)
115
+ else
116
+ result = d#{method}(domain, *args, &NIL_BLOCK)
117
+ return result if result
118
+ end
119
+ end
120
+ end
121
+ RUBY
156
122
  end
157
123
  end
158
124
  end