gettext 3.2.9 → 3.4.9

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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +276 -198
  3. data/Rakefile +7 -4
  4. data/doc/text/news.md +451 -0
  5. data/gettext.gemspec +10 -7
  6. data/lib/gettext/locale_path.rb +17 -9
  7. data/lib/gettext/mo.rb +12 -19
  8. data/lib/gettext/po.rb +4 -4
  9. data/lib/gettext/po_entry.rb +47 -27
  10. data/lib/gettext/po_parser.rb +65 -59
  11. data/lib/gettext/text_domain.rb +1 -1
  12. data/lib/gettext/text_domain_manager.rb +6 -5
  13. data/lib/gettext/tools/msgcat.rb +9 -2
  14. data/lib/gettext/tools/msginit.rb +226 -54
  15. data/lib/gettext/tools/msgmerge.rb +24 -6
  16. data/lib/gettext/tools/parser/erb.rb +9 -1
  17. data/lib/gettext/tools/parser/erubi.rb +88 -0
  18. data/lib/gettext/tools/parser/glade.rb +38 -40
  19. data/lib/gettext/tools/parser/gtk_builder_ui_definitions.rb +129 -0
  20. data/lib/gettext/tools/parser/ruby.rb +288 -221
  21. data/lib/gettext/tools/task.rb +4 -5
  22. data/lib/gettext/tools/xgettext.rb +21 -10
  23. data/lib/gettext/version.rb +3 -3
  24. data/locale/bg/LC_MESSAGES/gettext.mo +0 -0
  25. data/locale/bs/LC_MESSAGES/gettext.mo +0 -0
  26. data/locale/ca/LC_MESSAGES/gettext.mo +0 -0
  27. data/locale/cs/LC_MESSAGES/gettext.mo +0 -0
  28. data/locale/de/LC_MESSAGES/gettext.mo +0 -0
  29. data/locale/el/LC_MESSAGES/gettext.mo +0 -0
  30. data/locale/eo/LC_MESSAGES/gettext.mo +0 -0
  31. data/locale/es/LC_MESSAGES/gettext.mo +0 -0
  32. data/locale/et/LC_MESSAGES/gettext.mo +0 -0
  33. data/locale/fr/LC_MESSAGES/gettext.mo +0 -0
  34. data/locale/hr/LC_MESSAGES/gettext.mo +0 -0
  35. data/locale/hu/LC_MESSAGES/gettext.mo +0 -0
  36. data/locale/it/LC_MESSAGES/gettext.mo +0 -0
  37. data/locale/ja/LC_MESSAGES/gettext.mo +0 -0
  38. data/locale/ko/LC_MESSAGES/gettext.mo +0 -0
  39. data/locale/lv/LC_MESSAGES/gettext.mo +0 -0
  40. data/locale/nb/LC_MESSAGES/gettext.mo +0 -0
  41. data/locale/nl/LC_MESSAGES/gettext.mo +0 -0
  42. data/locale/pt_BR/LC_MESSAGES/gettext.mo +0 -0
  43. data/locale/ru/LC_MESSAGES/gettext.mo +0 -0
  44. data/locale/sr/LC_MESSAGES/gettext.mo +0 -0
  45. data/locale/uk/LC_MESSAGES/gettext.mo +0 -0
  46. data/locale/vi/LC_MESSAGES/gettext.mo +0 -0
  47. data/locale/zh/LC_MESSAGES/gettext.mo +0 -0
  48. data/locale/zh_TW/LC_MESSAGES/gettext.mo +0 -0
  49. data/po/bg/gettext.edit.po +81 -572
  50. data/po/bg/gettext.po +7 -409
  51. data/po/bs/gettext.edit.po +81 -572
  52. data/po/bs/gettext.po +7 -409
  53. data/po/ca/gettext.edit.po +81 -572
  54. data/po/ca/gettext.po +7 -409
  55. data/po/cs/gettext.edit.po +81 -572
  56. data/po/cs/gettext.po +7 -409
  57. data/po/de/gettext.edit.po +81 -572
  58. data/po/de/gettext.po +7 -409
  59. data/po/el/gettext.edit.po +81 -572
  60. data/po/el/gettext.po +7 -409
  61. data/po/eo/gettext.edit.po +81 -572
  62. data/po/eo/gettext.po +7 -409
  63. data/po/es/gettext.edit.po +81 -572
  64. data/po/es/gettext.po +7 -409
  65. data/po/et/gettext.edit.po +81 -572
  66. data/po/et/gettext.po +7 -409
  67. data/po/fr/gettext.edit.po +81 -572
  68. data/po/fr/gettext.po +7 -409
  69. data/po/gettext.pot +93 -645
  70. data/po/hr/gettext.edit.po +81 -572
  71. data/po/hr/gettext.po +7 -409
  72. data/po/hu/gettext.edit.po +81 -572
  73. data/po/hu/gettext.po +7 -409
  74. data/po/it/gettext.edit.po +81 -572
  75. data/po/it/gettext.po +7 -409
  76. data/po/ja/gettext.edit.po +83 -574
  77. data/po/ja/gettext.po +9 -411
  78. data/po/ko/gettext.edit.po +81 -572
  79. data/po/ko/gettext.po +7 -409
  80. data/po/lv/gettext.edit.po +81 -572
  81. data/po/lv/gettext.po +7 -409
  82. data/po/nb/gettext.edit.po +81 -572
  83. data/po/nb/gettext.po +7 -409
  84. data/po/nl/gettext.edit.po +81 -572
  85. data/po/nl/gettext.po +7 -409
  86. data/po/pt_BR/gettext.edit.po +81 -572
  87. data/po/pt_BR/gettext.po +7 -409
  88. data/po/ru/gettext.edit.po +81 -572
  89. data/po/ru/gettext.po +7 -409
  90. data/po/sr/gettext.edit.po +81 -572
  91. data/po/sr/gettext.po +7 -409
  92. data/po/sv/gettext.edit.po +81 -572
  93. data/po/sv/gettext.po +7 -409
  94. data/po/uk/gettext.edit.po +81 -572
  95. data/po/uk/gettext.po +7 -409
  96. data/po/vi/gettext.edit.po +81 -572
  97. data/po/vi/gettext.po +7 -409
  98. data/po/zh/gettext.edit.po +81 -572
  99. data/po/zh/gettext.po +7 -409
  100. data/po/zh_TW/gettext.edit.po +81 -572
  101. data/po/zh_TW/gettext.po +7 -409
  102. data/samples/cgi/po/helloerb1.pot +3 -3
  103. data/samples/cgi/po/helloerb2.pot +3 -3
  104. data/samples/cgi/po/hellolib.pot +3 -3
  105. data/samples/cgi/po/main.pot +3 -3
  106. data/samples/po/bg/hello_gtk_builder.edit.po +15 -0
  107. data/samples/po/bg/hello_gtk_builder.po +12 -0
  108. data/samples/po/bs/hello_gtk_builder.edit.po +15 -0
  109. data/samples/po/bs/hello_gtk_builder.po +12 -0
  110. data/samples/po/ca/hello_gtk_builder.edit.po +15 -0
  111. data/samples/po/ca/hello_gtk_builder.po +12 -0
  112. data/samples/po/cs/hello_gtk_builder.edit.po +15 -0
  113. data/samples/po/cs/hello_gtk_builder.po +12 -0
  114. data/samples/po/de/hello_gtk_builder.edit.po +15 -0
  115. data/samples/po/de/hello_gtk_builder.po +12 -0
  116. data/samples/po/el/hello_gtk_builder.edit.po +15 -0
  117. data/samples/po/el/hello_gtk_builder.po +12 -0
  118. data/samples/po/eo/hello_gtk_builder.edit.po +15 -0
  119. data/samples/po/eo/hello_gtk_builder.po +12 -0
  120. data/samples/po/es/hello_gtk_builder.edit.po +15 -0
  121. data/samples/po/es/hello_gtk_builder.po +12 -0
  122. data/samples/po/fr/hello_gtk_builder.edit.po +15 -0
  123. data/samples/po/fr/hello_gtk_builder.po +12 -0
  124. data/samples/po/hello.pot +3 -3
  125. data/samples/po/hello2.pot +4 -4
  126. data/samples/po/hello_glade2.pot +8 -5
  127. data/samples/po/hello_gtk2.pot +4 -4
  128. data/samples/po/hello_gtk_builder.pot +18 -4
  129. data/samples/po/hello_noop.pot +4 -4
  130. data/samples/po/hello_plural.pot +4 -4
  131. data/samples/po/hello_tk.pot +4 -4
  132. data/samples/po/hr/hello_gtk_builder.edit.po +15 -0
  133. data/samples/po/hr/hello_gtk_builder.po +12 -0
  134. data/samples/po/hu/hello_gtk_builder.edit.po +15 -0
  135. data/samples/po/hu/hello_gtk_builder.po +12 -0
  136. data/samples/po/it/hello_gtk_builder.edit.po +15 -0
  137. data/samples/po/it/hello_gtk_builder.po +12 -0
  138. data/samples/po/ja/hello_gtk_builder.edit.po +15 -0
  139. data/samples/po/ja/hello_gtk_builder.po +12 -0
  140. data/samples/po/ko/hello_gtk_builder.edit.po +15 -0
  141. data/samples/po/ko/hello_gtk_builder.po +12 -0
  142. data/samples/po/lv/hello_gtk_builder.edit.po +15 -0
  143. data/samples/po/lv/hello_gtk_builder.po +12 -0
  144. data/samples/po/nb/hello_gtk_builder.edit.po +15 -0
  145. data/samples/po/nb/hello_gtk_builder.po +12 -0
  146. data/samples/po/nl/hello_gtk_builder.edit.po +15 -0
  147. data/samples/po/nl/hello_gtk_builder.po +12 -0
  148. data/samples/po/pt_BR/hello_gtk_builder.edit.po +15 -0
  149. data/samples/po/pt_BR/hello_gtk_builder.po +12 -0
  150. data/samples/po/ru/hello_gtk_builder.edit.po +15 -0
  151. data/samples/po/ru/hello_gtk_builder.po +12 -0
  152. data/samples/po/sr/hello_gtk_builder.edit.po +15 -0
  153. data/samples/po/sr/hello_gtk_builder.po +12 -0
  154. data/samples/po/sv/hello_gtk_builder.edit.po +15 -0
  155. data/samples/po/sv/hello_gtk_builder.po +12 -0
  156. data/samples/po/uk/hello_gtk_builder.edit.po +15 -0
  157. data/samples/po/uk/hello_gtk_builder.po +12 -0
  158. data/samples/po/vi/hello_gtk_builder.edit.po +15 -0
  159. data/samples/po/vi/hello_gtk_builder.po +12 -0
  160. data/samples/po/zh/hello_gtk_builder.edit.po +15 -0
  161. data/samples/po/zh/hello_gtk_builder.po +12 -0
  162. data/samples/po/zh_TW/hello_gtk_builder.edit.po +15 -0
  163. data/samples/po/zh_TW/hello_gtk_builder.po +12 -0
  164. data/src/po_parser.ry +48 -42
  165. data/test/fixtures/_/backtick.rb +30 -0
  166. data/test/fixtures/_/block_parameter.rb +2 -2
  167. data/test/fixtures/_/double_quote_in_double_quote.rb +2 -2
  168. data/test/fixtures/_/double_quote_in_single_quote.rb +2 -2
  169. data/test/fixtures/_/literal_concatenation_with_continuation_line.rb +2 -2
  170. data/test/fixtures/_/middle_new_line.rb +2 -2
  171. data/test/fixtures/_/multiple_lines_literal.rb +2 -2
  172. data/test/fixtures/_/multiple_messages_in_same_line.rb +2 -2
  173. data/test/fixtures/_/multiple_same_messages.rb +2 -2
  174. data/test/fixtures/_/one_line.rb +2 -2
  175. data/test/fixtures/_/one_new_line.rb +2 -2
  176. data/test/fixtures/_/percent_strings.rb +55 -0
  177. data/test/fixtures/_/pipe.rb +32 -0
  178. data/test/{gettext-test-utils.rb → fixtures/_/quoted_symbol.rb} +13 -23
  179. data/test/fixtures/_.rb +10 -1
  180. data/test/fixtures/erb/case.rhtml +16 -0
  181. data/test/fixtures/erb/minus.rhtml +12 -0
  182. data/test/fixtures/erb/non_ascii.rhtml +1 -0
  183. data/test/fixtures/glade/3.glade +63 -0
  184. data/test/fixtures/gtk_builder_ui_definitions.ui +68 -0
  185. data/test/fixtures/multi_text_domain.rb +12 -12
  186. data/test/fixtures/np_.rb +1 -1
  187. data/test/fixtures/ns_/custom.rb +2 -2
  188. data/test/fixtures/ns_.rb +1 -1
  189. data/test/fixtures/p_.rb +1 -1
  190. data/test/fixtures/ruby/percent_lower_i.rb +1 -0
  191. data/test/fixtures/ruby/percent_lower_w.rb +1 -0
  192. data/test/fixtures/ruby/percent_upper_i.rb +1 -0
  193. data/test/fixtures/ruby/percent_upper_w.rb +1 -0
  194. data/test/fixtures/s_/custom.rb +2 -2
  195. data/test/fixtures/s_.rb +1 -1
  196. data/test/fixtures/simple.rb +5 -1
  197. data/test/fixtures/upper_nn_.rb +77 -0
  198. data/test/helper.rb +99 -0
  199. data/test/locale/fr/LC_MESSAGES/plural_error.mo +0 -0
  200. data/test/locale/fr/LC_MESSAGES/test1.mo +0 -0
  201. data/test/locale/fr_BE/LC_MESSAGES/test1.mo +0 -0
  202. data/test/locale/fr_BE_Foo/LC_MESSAGES/test1.mo +0 -0
  203. data/test/locale/ja/LC_MESSAGES/_.mo +0 -0
  204. data/test/po/_.pot +28 -4
  205. data/test/po/backslash.pot +6 -4
  206. data/test/po/fr/plural_error.po +7 -0
  207. data/test/po/fr/test1.po +7 -0
  208. data/test/po/fr_BE/test1.po +24 -0
  209. data/test/po/fr_BE_Foo/test1.po +20 -0
  210. data/test/po/hello.pot +3 -3
  211. data/test/po/ja/_.edit.po +27 -3
  212. data/test/po/ja/_.po +20 -2
  213. data/test/po/ja/hello.edit.po +0 -1
  214. data/test/po/non_ascii.pot +4 -4
  215. data/test/po/np_.pot +8 -5
  216. data/test/po/ns_.pot +6 -4
  217. data/test/po/p_.pot +3 -3
  218. data/test/po/s_.pot +6 -4
  219. data/test/po/untranslated.pot +4 -4
  220. data/test/run-test.rb +2 -3
  221. data/test/test_class_info.rb +8 -9
  222. data/test/test_gettext.rb +30 -1
  223. data/test/test_locale_path.rb +12 -4
  224. data/test/test_parser.rb +113 -25
  225. data/test/test_po_entry.rb +21 -17
  226. data/test/test_po_parser.rb +49 -4
  227. data/test/test_string.rb +9 -5
  228. data/test/tools/parser/test_glade.rb +91 -0
  229. data/test/tools/parser/test_gtk_builder_ui_definitions.rb +63 -0
  230. data/test/tools/parser/test_ruby.rb +76 -39
  231. data/test/tools/test_msgcat.rb +21 -1
  232. data/test/tools/test_msginit.rb +82 -3
  233. data/test/tools/test_msgmerge.rb +81 -6
  234. data/test/tools/test_xgettext.rb +106 -5
  235. metadata +82 -34
  236. data/lib/gettext/tools/parser/haml.rb +0 -61
  237. /data/test/fixtures/{gladeparser.glade → glade/2.glade} +0 -0
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
3
  # Copyright (C) 2012 Haruka Yoshihara <yoshihara@clear-code.com>
4
- # Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
4
+ # Copyright (C) 2012-2021 Sutou Kouhei <kou@clear-code.com>
5
5
  #
6
6
  # License: Ruby's or LGPL
7
7
  #
@@ -19,11 +19,17 @@
19
19
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
20
 
21
21
  require "etc"
22
+ require "optparse"
23
+
24
+ begin
25
+ require "datasets"
26
+ rescue LoadError
27
+ end
28
+ require "locale/info"
29
+
22
30
  require "gettext"
23
31
  require "gettext/po_parser"
24
32
  require "gettext/tools/msgmerge"
25
- require "locale/info"
26
- require "optparse"
27
33
 
28
34
  module GetText
29
35
  module Tools
@@ -156,7 +162,7 @@ module GetText
156
162
  else
157
163
  unless File.exist?(@input_file)
158
164
  raise(ValidationError,
159
- _("file '%s' does not exist." % @input_file))
165
+ _("file '%s' does not exist.") % @input_file)
160
166
  end
161
167
  end
162
168
 
@@ -168,8 +174,9 @@ module GetText
168
174
 
169
175
  unless valid_locale?(language_tag)
170
176
  raise(ValidationError,
171
- _("Locale '#{language_tag}' is invalid. " +
172
- "Please check if your specified locale is usable."))
177
+ _("Locale '%s' is invalid. " +
178
+ "Please check if your specified locale is usable.") %
179
+ language_tag)
173
180
  end
174
181
  @locale = language_tag.to_simple.to_s
175
182
  @language = language_tag.language
@@ -177,7 +184,7 @@ module GetText
177
184
  @output_file ||= "#{@locale}.po"
178
185
  if File.exist?(@output_file)
179
186
  raise(ValidationError,
180
- _("file '%s' has already existed." % @output_file))
187
+ _("file '%s' has already existed.") % @output_file)
181
188
  end
182
189
  end
183
190
 
@@ -319,53 +326,8 @@ module GetText
319
326
  end
320
327
 
321
328
  def plural_forms(language)
322
- case language
323
- when "ja", "vi", "ko", /\Azh.*\z/
324
- nplural = "1"
325
- plural_expression = "0"
326
- when "en", "de", "nl", "sv", "da", "no", "fo", "es", "pt",
327
- "it", "bg", "el", "fi", "et", "he", "eo", "hu", "tr",
328
- "ca", "nb"
329
- nplural = "2"
330
- plural_expression = "n != 1"
331
- when "pt_BR", "fr"
332
- nplural = "2"
333
- plural_expression = "n>1"
334
- when "lv"
335
- nplural = "3"
336
- plural_expression = "n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2"
337
- when "ga"
338
- nplural = "3"
339
- plural_expression = "n==1 ? 0 : n==2 ? 1 : 2"
340
- when "ro"
341
- nplural = "3"
342
- plural_expression = "n==1 ? 0 : " +
343
- "(n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2"
344
- when "lt", "bs"
345
- nplural = "3"
346
- plural_expression = "n%10==1 && n%100!=11 ? 0 : " +
347
- "n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2"
348
- when "ru", "uk", "sr", "hr"
349
- nplural = "3"
350
- plural_expression = "n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +
351
- "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2"
352
- when "cs", "sk"
353
- nplural = "3"
354
- plural_expression = "(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2"
355
- when "pl"
356
- nplural = "3"
357
- plural_expression = "n==1 ? 0 : n%10>=2 && " +
358
- "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2"
359
- when "sl"
360
- nplural = "4"
361
- plural_expression = "n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 " +
362
- "|| n%100==4 ? 2 : 3"
363
- else
364
- nplural = nil
365
- plural_expression = nil
366
- end
367
-
368
- "nplurals=#{nplural}; plural=#{plural_expression};"
329
+ converter = CLDRPluralsConverter.new(language)
330
+ converter.convert
369
331
  end
370
332
 
371
333
  DESCRIPTION_TITLE = /^SOME DESCRIPTIVE TITLE\.$/
@@ -407,6 +369,216 @@ module GetText
407
369
  def year
408
370
  now.year
409
371
  end
372
+
373
+ class CLDRPluralsConverter
374
+ def initialize(language)
375
+ @language = language
376
+
377
+ end
378
+
379
+ def convert
380
+ n_plurals = nil
381
+ expression = nil
382
+ if defined?(Datasets::CLDRPlurals)
383
+ plurals = Datasets::CLDRPlurals.new
384
+ plurals.each do |locale|
385
+ next unless locale.name == @language
386
+ n_plurals, expression = convert_plural_rules(locale.rules)
387
+ break
388
+ end
389
+ end
390
+ "nplurals=#{n_plurals}; plural=#{expression};"
391
+ end
392
+
393
+ private
394
+ def convert_plural_rules(rules)
395
+ n_plurals = 1
396
+ conditions = []
397
+ order = [
398
+ "one",
399
+ "zero",
400
+ "two",
401
+ "few",
402
+ "many",
403
+ "other",
404
+ ]
405
+ rules = rules.reject do |rule|
406
+ rule.integer_samples.nil?
407
+ end
408
+ rules = rules.sort_by do |rule|
409
+ order.index(rule.count)
410
+ end
411
+ rules[0..-2].each do |rule|
412
+ next if rule.condition.nil?
413
+ condition = convert_plural_condition(rule.condition)
414
+ next if condition.nil?
415
+ next if condition == false
416
+ n_plurals += 1
417
+ conditions << condition
418
+ end
419
+ expression = ""
420
+ case conditions.size
421
+ when 0
422
+ expression << "0"
423
+ when 1
424
+ condition = conditions[0]
425
+ case condition
426
+ when "(n == 1)"
427
+ expression << "n != 1"
428
+ when "(n <= 1)"
429
+ expression << "n > 1"
430
+ else
431
+ expression << "#{condition} ? 1 : 0"
432
+ end
433
+ else
434
+ (conditions.size + 1).times do |i|
435
+ if i == conditions.size
436
+ expression << i.to_s
437
+ else
438
+ condition = conditions[i]
439
+ expression << "#{condition} ? #{i} : "
440
+ end
441
+ end
442
+ end
443
+ [n_plurals, expression]
444
+ end
445
+
446
+ def convert_plural_condition(condition)
447
+ case condition[0]
448
+ when :and
449
+ gettext_condition = nil
450
+ condition[1..-1].each do |sub_condition|
451
+ sub_gettext_condition = convert_plural_condition(sub_condition)
452
+ case sub_gettext_condition
453
+ when String
454
+ if gettext_condition.is_a?(String)
455
+ gettext_condition << " && #{sub_gettext_condition}"
456
+ else
457
+ gettext_condition = sub_gettext_condition
458
+ end
459
+ when TrueClass
460
+ unless gettext_condition.is_a?(String)
461
+ gettext_condition = true
462
+ end
463
+ when FalseClass
464
+ return false
465
+ else
466
+ raise "unknown value #{sub_gettext_condition.inspect}"
467
+ end
468
+ end
469
+ gettext_condition
470
+ when :or
471
+ gettext_condition = false
472
+ condition[1..-1].each do |sub_condition|
473
+ sub_gettext_condition = convert_plural_condition(sub_condition)
474
+ case sub_gettext_condition
475
+ when String
476
+ if gettext_condition.is_a?(String)
477
+ gettext_condition << " || #{sub_gettext_condition}"
478
+ else
479
+ gettext_condition = sub_gettext_condition
480
+ end
481
+ when TrueClass
482
+ return true
483
+ when FalseClass
484
+ else
485
+ raise "unknown value #{sub_gettext_condition.inspect}"
486
+ end
487
+ end
488
+ gettext_condition
489
+ when :equal
490
+ left = convert_plural_condition(condition[1])
491
+ right = condition[2]
492
+ case left
493
+ when String
494
+ right = compact_equal_values(right)
495
+ gettext_conditions = right.collect do |right_value|
496
+ case right_value
497
+ when Range
498
+ if right_value.begin.zero?
499
+ "(#{left} <= #{right_value.end})"
500
+ else
501
+ "(#{left} >= #{right_value.begin} && " +
502
+ "#{left} <= #{right_value.end})"
503
+ end
504
+ else
505
+ "(#{left} == #{right_value})"
506
+ end
507
+ end
508
+ if gettext_conditions.size == 1
509
+ gettext_conditions[0]
510
+ else
511
+ gettext_conditions.join(" || ")
512
+ end
513
+ when 0
514
+ if right.include?(0)
515
+ true
516
+ else
517
+ false
518
+ end
519
+ else
520
+ false
521
+ end
522
+ when :not_equal
523
+ left = convert_plural_condition(condition[1])
524
+ right = condition[2]
525
+ case left
526
+ when String
527
+ right = compact_equal_values(right)
528
+ gettext_conditions = right.collect do |right_value|
529
+ case right_value
530
+ when Range
531
+ "(#{left} < #{right_value.begin} || " +
532
+ "#{left} > #{right_value.end})"
533
+ else
534
+ "(#{left} != #{right_value})"
535
+ end
536
+ end
537
+ if gettext_conditions.size == 1
538
+ gettext_conditions[0]
539
+ else
540
+ gettext_conditions = gettext_conditions.collect do |gettext_condition|
541
+ "(#{gettext_condition})"
542
+ end
543
+ gettext_conditions.join(" && ")
544
+ end
545
+ when 0
546
+ if right.include?(0)
547
+ false
548
+ else
549
+ true
550
+ end
551
+ else
552
+ false
553
+ end
554
+ when :mod
555
+ left = convert_plural_condition(condition[1])
556
+ right = condition[2]
557
+ case left
558
+ when "n"
559
+ "(n % #{right})"
560
+ else
561
+ false
562
+ end
563
+ when "n", "i"
564
+ "n"
565
+ when "v", "w"
566
+ 0
567
+ when "f", "t", "c", "e"
568
+ false
569
+ else
570
+ raise "unknown operator: #{condition[0].inspect}"
571
+ end
572
+ end
573
+
574
+ def compact_equal_values(values)
575
+ if values == [0, 1]
576
+ [0..1]
577
+ else
578
+ values
579
+ end
580
+ end
581
+ end
410
582
  end
411
583
  end
412
584
  end
@@ -1,9 +1,7 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
1
  # Copyright (C) 2012-2013 Haruka Yoshihara <yoshihara@clear-code.com>
4
- # Copyright (C) 2012-2015 Kouhei Sutou <kou@clear-code.com>
5
- # Copyright (C) 2005-2009 Masao Mutoh
6
- # Copyright (C) 2005,2006 speakillof
2
+ # Copyright (C) 2012-2023 Sutou Kouhei <kou@clear-code.com>
3
+ # Copyright (C) 2005-2009 Masao Mutoh
4
+ # Copyright (C) 2005,2006 speakillof
7
5
  #
8
6
  # License: Ruby's or LGPL
9
7
  #
@@ -49,6 +47,7 @@ module GetText
49
47
  config.parse(command_line)
50
48
 
51
49
  parser = POParser.new
50
+ parser.report_warning = config.report_warning?
52
51
  parser.ignore_fuzzy = false
53
52
  definition_po = PO.new
54
53
  reference_pot = PO.new
@@ -309,10 +308,12 @@ module GetText
309
308
  @output = nil
310
309
  @order = :reference
311
310
  @po_format_options = {
312
- :max_line_width => POEntry::Formatter::DEFAULT_MAX_LINE_WIDTH,
311
+ max_line_width: POEntry::Formatter::DEFAULT_MAX_LINE_WIDTH,
312
+ use_one_line_per_reference: false,
313
313
  }
314
314
  @enable_fuzzy_matching = true
315
315
  @update = nil
316
+ @report_warning = true
316
317
  @output_obsolete_entries = true
317
318
  @backup = ENV["VERSION_CONTROL"]
318
319
  @suffix = ENV["SIMPLE_BACKUP_SUFFIX"] || "~"
@@ -337,6 +338,12 @@ module GetText
337
338
  @enable_fuzzy_matching
338
339
  end
339
340
 
341
+ # @return [Bool] true if reporting warning is enabled,
342
+ # false otherwise.
343
+ def report_warning?
344
+ @report_warning
345
+ end
346
+
340
347
  # @return [Bool] true if outputting obsolete entries is
341
348
  # enabled, false otherwise.
342
349
  def output_obsolete_entries?
@@ -416,12 +423,23 @@ module GetText
416
423
  @po_format_options[:max_line_width] = max_line_width
417
424
  end
418
425
 
426
+ parser.on("--[no-]use-one-line-per-reference",
427
+ _("Use one line for each reference comment"),
428
+ "(#{@po_format_options[:use_one_line_per_reference]})") do |use|
429
+ @po_format_options[:use_one_line_per_reference] = use
430
+ end
431
+
419
432
  parser.on("--[no-]fuzzy-matching",
420
433
  _("Disable fuzzy matching"),
421
434
  _("(enable)")) do |boolean|
422
435
  @enable_fuzzy_matching = boolean
423
436
  end
424
437
 
438
+ parser.on("--no-report-warning",
439
+ _("Don't report warning messages")) do |report_warning|
440
+ @report_warning = report_warning
441
+ end
442
+
425
443
  parser.on("--no-obsolete-entries",
426
444
  _("Don't output obsolete entries")) do |boolean|
427
445
  @output_obsolete_entries = boolean
@@ -57,12 +57,20 @@ module GetText
57
57
  @options = options
58
58
  end
59
59
 
60
+ @@erb_accept_keyword_arguments =
61
+ ERB.instance_method(:initialize).parameters.any? {|type, _| type == :key}
62
+
60
63
  # Extracts messages from @path.
61
64
  #
62
65
  # @return [Array<POEntry>] Extracted messages
63
66
  def parse
64
67
  content = IO.read(@path)
65
- src = ERB.new(content).src
68
+ if @@erb_accept_keyword_arguments
69
+ erb = ERB.new(content, trim_mode: "-")
70
+ else
71
+ erb = ERB.new(content, nil, "-")
72
+ end
73
+ src = erb.src
66
74
 
67
75
  # Force the src encoding back to the encoding in magic comment
68
76
  # or original content.
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ =begin
4
+ parser/erubi.rb - parser for ERB using Erubi
5
+
6
+ Copyright (C) 2005-2009 Masao Mutoh
7
+
8
+ You may redistribute it and/or modify it under the same
9
+ license terms as Ruby or LGPL.
10
+ =end
11
+
12
+ require "erubi"
13
+ require 'gettext/tools/parser/ruby'
14
+
15
+ module GetText
16
+ class ErubiParser
17
+ @config = {
18
+ :extnames => ['.rhtml', '.erb']
19
+ }
20
+
21
+ class << self
22
+ # Sets some preferences to parse ERB files.
23
+ # * config: a Hash of the config. It can takes some values below:
24
+ # * :extnames: An Array of target files extension. Default is [".rhtml"].
25
+ def init(config)
26
+ config.each{|k, v|
27
+ @config[k] = v
28
+ }
29
+ end
30
+
31
+ def target?(file) # :nodoc:
32
+ @config[:extnames].each do |v|
33
+ return true if File.extname(file) == v
34
+ end
35
+ false
36
+ end
37
+
38
+ # Parses eRuby script located at `path`.
39
+ #
40
+ # This is a short cut method. It equals to `new(path,
41
+ # options).parse`.
42
+ #
43
+ # @return [Array<POEntry>] Extracted messages
44
+ # @see #initialize and #parse
45
+ def parse(path, options={})
46
+ parser = new(path, options)
47
+ parser.parse
48
+ end
49
+ end
50
+
51
+ # @param path [String] eRuby script path to be parsed
52
+ # @param options [Hash]
53
+ def initialize(path, options={})
54
+ @path = path
55
+ @options = options
56
+ end
57
+
58
+ # Extracts messages from @path.
59
+ #
60
+ # @return [Array<POEntry>] Extracted messages
61
+ def parse
62
+ content = IO.read(@path)
63
+
64
+ encoding = detect_encoding(content)
65
+ content.force_encoding(encoding)
66
+
67
+ erb = Erubi::Engine.new(content)
68
+ src = erb.src
69
+
70
+ RubyParser.new(@path, @options).parse_source(src)
71
+ end
72
+
73
+ def detect_encoding(content)
74
+ if /#.*coding: (\S*)/ =~ content.lines.first
75
+ $1.upcase
76
+ else
77
+ content.encoding
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ if __FILE__ == $0
84
+ # ex) ruby glade.rhtml foo.rhtml bar.rhtml
85
+ ARGV.each do |file|
86
+ p GetText::ErubiParser.parse(file)
87
+ end
88
+ end
@@ -1,14 +1,20 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- =begin
4
- parser/glade.rb - parser for Glade-2
5
-
6
- Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
7
- Copyright (C) 2004,2005 Masao Mutoh
8
-
9
- You may redistribute it and/or modify it under the same
10
- license terms as Ruby or LGPL.
11
- =end
1
+ # Copyright (C) 2004,2005 Masao Mutoh
2
+ # Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
3
+ #
4
+ # License: Ruby's or LGPL
5
+ #
6
+ # This library is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
12
18
 
13
19
  require 'cgi'
14
20
  require 'gettext'
@@ -24,15 +30,11 @@ module GetText
24
30
  GLADE_RE = /glade-2.0.dtd/
25
31
 
26
32
  def target?(file) # :nodoc:
27
- data = IO.readlines(file)
28
- if XML_RE =~ data[0] and GLADE_RE =~ data[1]
29
- true
30
- else
31
- if File.extname(file) == '.glade'
32
- raise _("`%{file}' is not glade-2.0 format.") % {:file => file}
33
- end
34
- false
35
- end
33
+ return false unless File.extname(file) == ".glade"
34
+ data = File.read(file)
35
+ return false unless data.include?("<?xml")
36
+ return false unless data.include?("glade-2.0.dtd")
37
+ true
36
38
  end
37
39
 
38
40
  def parse(path, options={})
@@ -57,7 +59,7 @@ module GetText
57
59
 
58
60
  private
59
61
  def parse_source(input) # :nodoc:
60
- targets = []
62
+ po = []
61
63
  target = false
62
64
  start_line_no = nil
63
65
  val = nil
@@ -69,14 +71,14 @@ module GetText
69
71
  target = true
70
72
  if TARGET2 =~ $1
71
73
  val = $1
72
- add_target(val, start_line_no, targets)
74
+ add_po_entry(po, val, start_line_no)
73
75
  val = nil
74
76
  target = false
75
77
  end
76
78
  elsif target
77
79
  if TARGET2 =~ line
78
80
  val << $1
79
- add_target(val, start_line_no, targets)
81
+ add_po_entry(po, val, start_line_no)
80
82
  val = nil
81
83
  target = false
82
84
  else
@@ -84,26 +86,22 @@ module GetText
84
86
  end
85
87
  end
86
88
  end
87
- targets
89
+ po
88
90
  end
89
91
 
90
- def add_target(val, line_no, targets) # :nodoc:
91
- return unless val.size > 0
92
- assoc_data = targets.assoc(val)
93
- val = CGI.unescapeHTML(val)
94
- if assoc_data
95
- targets[targets.index(assoc_data)] = assoc_data << "#{@path}:#{line_no}"
96
- else
97
- targets << [val.gsub(/\n/, '\n'), "#{@path}:#{line_no}"]
92
+ def add_po_entry(po, value, line_no) # :nodoc:
93
+ return if value.empty?
94
+ value = CGI.unescapeHTML(value)
95
+ value = value.gsub(/\n/, "\n")
96
+ po_entry = po.find do |entry|
97
+ entry.msgid == value
98
98
  end
99
- targets
99
+ if po_entry.nil?
100
+ po_entry = POEntry.new(:normal)
101
+ po_entry.msgid = value
102
+ po << po_entry
103
+ end
104
+ po_entry.references << "#{@path}:#{line_no}"
100
105
  end
101
106
  end
102
107
  end
103
-
104
- if __FILE__ == $0
105
- # ex) ruby glade.rb foo.glade bar.glade
106
- ARGV.each do |file|
107
- p GetText::GladeParser.parse(file)
108
- end
109
- end