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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af7af45d00211da2791e03bf1f4f7c611d8c557a10f339189ebdf2a72008ff05
4
- data.tar.gz: 23040ab7524d7eb95b0e11fcc22a05a1ddf54eeeb88babade01c9876620447c5
3
+ metadata.gz: 3b7df1ae6f9c9acae6fe82a22b0ce3efd1d545c9d814447070ebb59652a7fd71
4
+ data.tar.gz: 05af6efd5f93f5ea8bf2f8be7314566d0238781f7d809799823223ec01866ce4
5
5
  SHA512:
6
- metadata.gz: c9152caffa285a723a4681e23db5d0710465a5b47f2c2251f863026cf91da316cdd36155097038896d77cb1c5562af619e73cf29f69be70bebec28ac8bf6d272
7
- data.tar.gz: c59623e1c15a293922cfcdd457634a2cb5f082900ee0948d14d7d37c4ddac5533f1fa6437f8e9bc200cc391e9be4a8c6d416e72568017905f0be930fbd65bb00
6
+ metadata.gz: b0df4b37e9837b4661cf7c0cbef1c75144c3c2804b2da4110a4850c08eb3084a91cd419ffb5c796eff30ee96860d87cb555a3cb2bd3e99e25db64731868a08ff
7
+ data.tar.gz: dc6f882c3c3fa4ea1c3fc2399077d149c3e981124de8f9da99e0ee7bdf2fd248ac74131cc92f9167d05ef15a04eb2ba4e25780ea1f8bc9ecfe04795776704310
data/CHANGELOG CHANGED
@@ -1,11 +1,12 @@
1
+ 2.0.0 -- Changed p_ separator to \0004 https://github.com/grosser/fast_gettext/pull/107 add np and add gettext aliases
1
2
  1.6.0 -- Remove restrictions around yaml file names
2
3
  1.1.0 -- translations are no longer eager loaded for improved startup performance, pass `eager_load: true` to preload for example in preforked web server
3
4
  1.0.0 -- do not enforce attr_accessible unless ProtectedAttributes are loaded
4
- 0.9.0 -- reworked internals of caching to be plugable
5
+ 0.9.0 -- reworked internals of caching to be pluggable
5
6
  0.7.0 -- set_locale resets to default locale if none of the available locales was tried to set
6
- 0.6.0 -- plurals use singular translations as fallack e.g. you translated 'Axis' then n_('Axis','Axis',1) would return the translation for 'Axis' if no plural translation was found
7
+ 0.6.0 -- plurals use singular translations as fallback e.g. you translated 'Axis' then n_('Axis','Axis',1) would return the translation for 'Axis' if no plural translation was found
7
8
  0.4.14 -- "" is translated as "", not as gettext meta information
8
- 0.4.0 -- pluralisation_rules is no longer stored in each repository, only retrived. Added Chain and Logger repository.
9
+ 0.4.0 -- pluralisation_rules is no longer stored in each repository, only retrieved. Added Chain and Logger repository.
9
10
  0.3.6 -- FastGettext.default_locale=
10
11
  0.3.5 -- FastGettext.default_text_domain=
11
12
  0.3.4 -- Exceptions are thrown, not returned when translating without text domain
data/Readme.md CHANGED
@@ -1,11 +1,12 @@
1
1
  FastGettext
2
2
  ===========
3
- GetText but 3.5 x faster, 560 x less memory, simple, clean namespace (7 vs 34) and threadsafe!
3
+ GetText but 12 x faster, 530 x less garbage, clean namespace (8 vs 26), simple and threadsafe!
4
4
 
5
5
  It supports multiple backends (.mo, .po, .yml files, Database(ActiveRecord + any other), Chain, Loggers) and can easily be extended.
6
6
 
7
7
  [Example Rails application](https://github.com/grosser/gettext_i18n_rails_example)
8
8
 
9
+
9
10
  Comparison
10
11
  ==========
11
12
  <table>
@@ -18,17 +19,17 @@ Comparison
18
19
  </tr>
19
20
  <tr>
20
21
  <td>Speed*</td>
21
- <td>0.82s</td>
22
- <td>1.36s</td>
23
- <td>4.88s</td>
24
- <td>21.77s</td>
22
+ <td>0.08s</td>
23
+ <td>0.14s</td>
24
+ <td>1.75s</td>
25
+ <td>3.75s</td>
25
26
  </tr>
26
27
  <tr>
27
- <td>RAM*</td>
28
- <td>4K</td>
29
- <td>8K</td>
30
- <td>4480K</td>
31
- <td>10100K</td>
28
+ <td>Objects*</td>
29
+ <td>11K</td>
30
+ <td>15K</td>
31
+ <td>8017K</td>
32
+ <td>7107K</td>
32
33
  </tr>
33
34
  <tr>
34
35
  <td>Included backends</td>
@@ -38,10 +39,12 @@ Comparison
38
39
  <td>yml (db/key-value/po/chain in other I18n backends)</td>
39
40
  </tr>
40
41
  </table>
41
- <small>*50.000 translations with ruby enterprise 1.8.6 through `rake benchmark`</small>
42
+ <small>*500.000 translations with ruby 2.5.3 through `bundle exec rake benchmark`</small>
43
+
42
44
 
43
45
  Setup
44
46
  =====
47
+
45
48
  ### 1. Install
46
49
 
47
50
  ```Bash
@@ -60,8 +63,8 @@ Or po files (less maintenance than mo)
60
63
 
61
64
  ```Ruby
62
65
  FastGettext.add_text_domain('my_app', path: 'locale', type: :po)
63
- # :ignore_fuzzy => true to not use fuzzy translations
64
- # :report_warning => false to hide warnings about obsolete/fuzzy translations
66
+ # ignore_fuzzy: true to not use fuzzy translations
67
+ # report_warning: false to hide warnings about obsolete/fuzzy translations
65
68
  ```
66
69
 
67
70
  Or yaml files (use I18n syntax/indentation)
@@ -77,7 +80,7 @@ Or database (scaleable, good for many locales/translators)
77
80
  ```Ruby
78
81
  # db access is cached <-> only first lookup hits the db
79
82
  require "fast_gettext/translation_repository/db"
80
- FastGettext::TranslationRepository::Db.require_models #load and include default models
83
+ FastGettext::TranslationRepository::Db.require_models # load and include default models
81
84
  FastGettext.add_text_domain('my_app', type: :db, model: TranslationKey)
82
85
  ```
83
86
 
@@ -86,24 +89,83 @@ Do this once in every Thread. (e.g. Rails -> ApplicationController)
86
89
 
87
90
  ```Ruby
88
91
  FastGettext.text_domain = 'my_app'
89
- FastGettext.available_locales = ['de','en','fr','en_US','en_UK'] # only allow these locales to be set (optional)
92
+ FastGettext.available_locales = ['de', 'en', 'fr', 'en_US', 'en_UK'] # only allow these locales to be set (optional)
90
93
  FastGettext.locale = 'de'
91
94
  ```
92
95
 
93
96
  ### 4. Start translating
94
97
 
95
- ```Ruby
96
- include FastGettext::Translation
97
- _('Car') == 'Auto'
98
- _('not-found') == 'not-found'
99
- s_('Namespace|not-found') == 'not-found'
100
- n_('Axis','Axis',3) == 'Achsen' #German plural of Axis
101
- _('Hello %{name}!') % {name: "Pete"} == 'Hello Pete!'
98
+ FastGetText supports all the translation methods of [ruby-gettext](http://github.com/ruby-gettext/gettext) with added support for block defaults.
99
+ (to get `*gettext` methods, use `FastGetText::TranslationAliased`)
100
+
101
+ #### `_()` or `gettext()`: basic translation
102
+
103
+ ```ruby
104
+ extend FastGettext::Translation
105
+ _('Car') == 'Auto' # found translation for 'Car'
106
+ _('not-found') == 'not-found' # The msgid is returned by default
107
+ ```
108
+
109
+ #### `n_()` or `ngettext()`: pluralization
110
+
111
+ ```ruby
112
+ n_('Car', 'Cars', 1) == 'Auto'
113
+ n_('Car', 'Cars', 2) == 'Autos' # German plural of Cars
114
+ ```
115
+
116
+ You'll often want to interpolate the results of `n_()` using ruby builtin `%` operator.
117
+
118
+ ```ruby
119
+ n_('Car', '#{n} Cars', 2) % { n: count } == '2 Autos'
120
+ ```
121
+
122
+ #### `p_()` or `pgettext()`: translation with context
123
+
124
+ ```ruby
125
+ p_('File', 'Open') == _("File\004Open") == "öffnen"
126
+ p_('Context', 'not-found') == 'not-found'
127
+ ```
128
+
129
+ #### `s_()` or `sgetext()`: translation with namespace
130
+
131
+ ```ruby
132
+ s_('File|Open') == _('File|Open') == "öffnen"
133
+ s_('Context|not-found') == 'not-found'
134
+ ```
135
+
136
+ The difference between `s_()` and `p_()` is largely based on how the translations
137
+ are stored. Your preference will be based on your workflow and translation editing
138
+ tools.
139
+
140
+ #### `pn_()` or `pngettext()`: context-aware pluralized
141
+
142
+ ```ruby
143
+ pn_('Fruit', 'Apple', 'Apples', 3) == 'Äpfel'
144
+ pn_('Fruit', 'Apple', 'Apples', 1) == 'Apfel'
145
+ ```
146
+
147
+ #### `sn_()` or `sngettext()`: without context pluralized
148
+
149
+ ```ruby
150
+ sn_('Fruit|Apple', 'Apples', 3) == 'Äpfel'
151
+ sn_('Fruit|Apple', 'Apples', 1) == 'Apfel'
152
+ ```
153
+
154
+ #### `N_()` and `Nn_()`: make dynamic translations available to the parser.
155
+
156
+ In many instances, your strings will not be found the by the ruby-parse. These methods
157
+ allow for those strings to be discovered.
158
+
159
+ ```
160
+ N_("active"); N_("inactive"); N_("paused") # possible value of status for parser to find.
161
+ Nn_("active", "inactive", "paused") # alternative method
162
+ _("Your account is #{account_state}.") % { account_state: status }
102
163
  ```
103
164
 
104
165
 
105
166
  Managing translations
106
- ============
167
+ =====================
168
+
107
169
  ### mo/po-files
108
170
  Generate .po or .mo files using GetText parser (example tasks at [gettext_i18n_rails](http://github.com/grosser/gettext_i18n_rails))
109
171
 
@@ -113,12 +175,12 @@ Tell Gettext where your .mo or .po files lie, e.g. for locale/de/my_app.po and l
113
175
  FastGettext.add_text_domain('my_app', path: 'locale')
114
176
  ```
115
177
 
116
- Use the [original GetText](http://github.com/mutoh/gettext) to create and manage po/mo-files.
178
+ Use the [original GetText](http://github.com/ruby-gettext/gettext) to create and manage po/mo-files.
117
179
  (Work on a po/mo parser & reader that is easier to use has started, contributions welcome @ [get_pomo](http://github.com/grosser/get_pomo) )
118
180
 
119
181
  ### Database
120
182
  [Example migration for ActiveRecord](http://github.com/grosser/fast_gettext/blob/master/examples/db/migration.rb)<br/>
121
- The default plural seperator is `||||` but you may overwrite it (or suggest a better one..).
183
+ The default plural separator is `||||` but you may overwrite it (or suggest a better one..).
122
184
 
123
185
  This is usable with any model DataMapper/Sequel or any other(non-database) backend, the only thing you need to do is respond to the self.translation(key, locale) call.
124
186
  If you want to use your own models, have a look at the [default models](http://github.com/grosser/fast_gettext/tree/master/lib/fast_gettext/translation_repository/db_models) to see what you want/need to implement.
@@ -130,7 +192,7 @@ Rails
130
192
  Try the [gettext_i18n_rails plugin](http://github.com/grosser/gettext_i18n_rails), it simplifies the setup.<br/>
131
193
  Try the [translation_db_engine](http://github.com/grosser/translation_db_engine), to manage your translations in a db.
132
194
 
133
- Setting `available_locales`,`text_domain` or `locale` will not work inside the `evironment.rb`,
195
+ Setting `available_locales`,`text_domain` or `locale` will not work inside the `environment.rb`,
134
196
  since it runs in a different thread then e.g. controllers, so set them inside your application_controller.
135
197
 
136
198
  ```Ruby
@@ -145,7 +207,7 @@ class ApplicationController ...
145
207
  include FastGettext::Translation
146
208
  before_filter :set_locale
147
209
  def set_locale
148
- FastGettext.available_locales = ['de','en',...]
210
+ FastGettext.available_locales = ['de', 'en', ...]
149
211
  FastGettext.text_domain = 'frontend'
150
212
  FastGettext.set_locale(params[:locale] || session[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'])
151
213
  session[:locale] = I18n.locale = FastGettext.locale
@@ -155,6 +217,7 @@ class ApplicationController ...
155
217
 
156
218
  Advanced features
157
219
  =================
220
+
158
221
  ### Abnormal pluralisation
159
222
  Plurals are selected by index, think of it as `['car', 'cars'][index]`<br/>
160
223
  A pluralisation rule decides which form to use e.g. in english its `count == 1 ? 0 : 1`.<br/>
@@ -163,7 +226,7 @@ If you have any languages that do not fit this rule, you have to add a custom pl
163
226
  Via Ruby:
164
227
 
165
228
  ```Ruby
166
- FastGettext.pluralisation_rule = lambda{|count| count > 5 ? 1 : (count > 2 ? 0 : 2)}
229
+ FastGettext.pluralisation_rule = ->(count){ count > 5 ? 1 : (count > 2 ? 0 : 2)}
167
230
  ```
168
231
 
169
232
  Via mo/pofile:
@@ -179,7 +242,7 @@ If you only use one text domain, setting `FastGettext.default_text_domain = 'app
179
242
  is sufficient and no more `text_domain=` is needed
180
243
 
181
244
  ### default_locale
182
- If the simple rule of "first `availble_locale` or 'en'" is not suficcient for you, set `FastGettext.default_locale = 'de'`.
245
+ If the simple rule of "first `available_locale` or 'en'" is not sufficient for you, set `FastGettext.default_locale = 'de'`.
183
246
 
184
247
  ### default_available_locales
185
248
  Fallback when no available_locales are set
@@ -203,7 +266,7 @@ repos = [
203
266
  FastGettext::TranslationRepository.build('new', path: '....'),
204
267
  FastGettext::TranslationRepository.build('old', path: '....')
205
268
  ]
206
- FastGettext.add_text_domain 'combined', type: :chain, :chain: repos
269
+ FastGettext.add_text_domain 'combined', type: :chain, chain: repos
207
270
  ```
208
271
 
209
272
  ### Merge
@@ -213,8 +276,8 @@ a subordinate repository is added. This puts the burden on the load phase and sp
213
276
 
214
277
  ```Ruby
215
278
  repos = [
216
- FastGettext::TranslationRepository.build('new', :path: '....'),
217
- FastGettext::TranslationRepository.build('old', :path: '....')
279
+ FastGettext::TranslationRepository.build('new', path: '....'),
280
+ FastGettext::TranslationRepository.build('old', path: '....')
218
281
  ]
219
282
  domain = FastGettext.add_text_domain 'combined', type: :merge, chain: repos
220
283
  ```
@@ -232,7 +295,7 @@ When you want to know which keys could not be translated or were used, add a Log
232
295
  ```Ruby
233
296
  repos = [
234
297
  FastGettext::TranslationRepository.build('app', path: '....')
235
- FastGettext::TranslationRepository.build('logger', type: :logger, callback: lambda{|key_or_array_of_ids| ... }),
298
+ FastGettext::TranslationRepository.build('logger', type: :logger, callback: ->(key_or_array_of_ids) { ... }),
236
299
  }
237
300
  FastGettext.add_text_domain 'combined', type: :chain, chain: repos
238
301
  ```
@@ -246,7 +309,7 @@ Want a xml version ?
246
309
  Write your own TranslationRepository!
247
310
 
248
311
  ```Ruby
249
- # fast_gettext/translation_repository/xxx.rb
312
+ # fast_gettext/translation_repository/wtf.rb
250
313
  module FastGettext
251
314
  module TranslationRepository
252
315
  class Wtf
@@ -263,33 +326,65 @@ If you have more than one gettext domain, there are two sets of functions
263
326
  available:
264
327
 
265
328
  ```Ruby
266
- include FastGettext::TranslationMultidomain
329
+ extend FastGettext::TranslationMultidomain
267
330
 
268
331
  d_("domainname", "string") # finds 'string' in domain domainname
269
332
  dn_("domainname", "string", "strings", 1) # ditto
270
- # etc.
333
+ dp_("domainname", "context", "key")
334
+ ds_("domainname", "context|key")
335
+ dnp_("domainname", "context", "string", "strings")
336
+ dns_("domainname", "context|string", "strings")
271
337
  ```
272
338
 
273
339
  These are helper methods so you don't need to write:
274
340
 
275
341
  ```Ruby
276
- FastGettext.text_domain = "domainname"
277
- _("string")
342
+ FastGettext.with_domain("domainname") { _("string") }
278
343
  ```
279
344
 
280
345
  It is useful in Rails plugins in the views for example. The second set of
281
346
  functions are D functions which search for string in _all_ domains. If there
282
347
  are multiple translations in different domains, it returns them in random
283
- order (depends on the Ruby hash implementation):
348
+ order (depends on the Ruby hash implementation).
284
349
 
285
350
  ```Ruby
286
- include FastGettext::TranslationMultidomain
351
+ extend FastGettext::TranslationMultidomain
287
352
 
288
353
  D_("string") # finds 'string' in any domain
289
- # etc.
354
+ Dn_("string", "strings", 1) # ditto
355
+ Dp_("context", "key")
356
+ Ds_("context|key")
357
+ Dnp_("context", "string", "strings")
358
+ Dns_("context|string", "strings")
359
+ ```
360
+
361
+ Alternatively you can use [merge repository](https://github.com/grosser/fast_gettext#merge) to achieve the same behavior.
362
+
363
+ #### Block defaults
364
+
365
+ All the translation methods (includ MultiDomain) support a block default, a feature not provided by ruby-gettext. When a translation is
366
+ not found, if a block is provided the block is always returned. Otherwise, a key is returned. Methods doing pluralization will attempt a simple translation of alternate keys.
367
+
368
+ ```ruby
369
+ _('not-found'){ "alternative default" } == alternate default
370
+ ```
371
+
372
+ This block default is useful when the default is a very long passage of text that wouldn't make a useful key. You can also instrument logging not found keys.
373
+
374
+ ```ruby
375
+ _('terms-and-conditions'){
376
+ load_terms_and_conditions
377
+ request_terms_and_conditions_translation_from_legal
378
+ }
379
+
380
+ # Override _ with logging
381
+ def _(key, &block)
382
+ result = gettext(key){ nil } # nil returned when not found
383
+ log_missing_translation_key(key) if result.nil?
384
+ result || (block ? block.call : key)
385
+ end
290
386
  ```
291
387
 
292
- Alternatively you can use [merge repository](https://github.com/grosser/fast_gettext#merge) to achieve the same behaviour.
293
388
 
294
389
  FAQ
295
390
  ===
@@ -297,13 +392,8 @@ FAQ
297
392
  - [Iconv require error in 1.9.2](http://exceptionz.wordpress.com/2010/02/03/how-to-fix-the-iconv-require-error-in-ruby-1-9)
298
393
 
299
394
 
300
- TODO
301
- ====
302
- - Add a fallback for Iconv.conv in ruby 1.9.4 -> lib/fast_gettext/vendor/iconv
303
- - YML backend that reads ActiveSupport::I18n files
304
-
305
- Author
306
- ======
395
+ Authors
396
+ =======
307
397
  Mo/Po-file parsing from Masao Mutoh, see vendor/README
308
398
 
309
399
  ### [Contributors](http://github.com/grosser/fast_gettext/contributors)
@@ -327,6 +417,7 @@ Mo/Po-file parsing from Masao Mutoh, see vendor/README
327
417
  - [Martin Meier](https://github.com/mameier)
328
418
  - [morcoteg](https://github.com/morcoteg)
329
419
  - [Daniel Schepers](https://github.com/tall-dan)
420
+ - [Robert Graff](https://github.com/rgraff)
330
421
 
331
422
  [Michael Grosser](http://grosser.it)<br/>
332
423
  michael@grosser.it<br/>
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fast_gettext/mo_file'
2
4
  require 'fast_gettext/storage'
3
5
  require 'fast_gettext/translation'
@@ -6,30 +8,28 @@ require 'fast_gettext/vendor/string'
6
8
  require 'fast_gettext/version'
7
9
 
8
10
  module FastGettext
9
- include FastGettext::Storage
10
- extend self
11
+ extend FastGettext::Storage
12
+ extend FastGettext::Translation
11
13
 
12
- LOCALE_REX = /^[a-z]{2,3}$|^[a-z]{2,3}_[A-Z]{2,3}$/
14
+ LOCALE_REX = /^[a-z]{2,3}$|^[a-z]{2,3}_[A-Z]{2,3}$/.freeze
13
15
  NAMESPACE_SEPARATOR = '|'
16
+ CONTEXT_SEPARATOR = "\004"
14
17
 
15
- # users should not include FastGettext, since this would contaminate their namespace
16
- # rather use
17
- # FastGettext.locale = ..
18
- # FastGettext.text_domain = ..
19
- # and
20
- # include FastGettext::Translation
21
- FastGettext::Translation.public_instance_methods.each do |method|
22
- define_method method do |*args|
23
- Translation.send(method,*args)
24
- end
18
+ # helper block for changing domains
19
+ def self.with_domain(domain)
20
+ old_domain = FastGettext.text_domain
21
+ FastGettext.text_domain = domain
22
+ yield
23
+ ensure
24
+ FastGettext.text_domain = old_domain
25
25
  end
26
26
 
27
- def add_text_domain(name,options)
28
- translation_repositories[name] = TranslationRepository.build(name,options)
27
+ def self.add_text_domain(name, options)
28
+ translation_repositories[name] = TranslationRepository.build(name, options)
29
29
  end
30
30
 
31
31
  # some repositories know where to store their locales
32
- def locale_path
32
+ def self.locale_path
33
33
  translation_repositories[text_domain].instance_variable_get(:@options)[:path]
34
34
  end
35
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastGettext
2
4
  class Cache
3
5
  def initialize
@@ -8,13 +10,13 @@ module FastGettext
8
10
  def fetch(key)
9
11
  translation = @current[key]
10
12
  if translation.nil? # uncached
11
- @current[key] = yield || false # TODO get rid of this false hack and cache :missing
13
+ @current[key] = yield || false # TODO: get rid of this false hack and cache :missing
12
14
  else
13
15
  translation
14
16
  end
15
17
  end
16
18
 
17
- # TODO only used for tests, maybe if-else around it ...
19
+ # TODO: only used for tests, maybe if-else around it ...
18
20
  def []=(key, value)
19
21
  @current[key] = value
20
22
  end