fast_gettext 1.8.0 → 2.0.0

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