gettext 2.3.0 → 2.3.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 (74) hide show
  1. data/.yardopts +6 -0
  2. data/Rakefile +7 -6
  3. data/doc/text/news.md +51 -2
  4. data/gettext.gemspec +1 -0
  5. data/lib/gettext/runtime/locale_path.rb +0 -1
  6. data/lib/gettext/runtime/mofile.rb +8 -2
  7. data/lib/gettext/runtime/textdomain.rb +19 -21
  8. data/lib/gettext/runtime/textdomain_manager.rb +0 -1
  9. data/lib/gettext/tools/msginit.rb +19 -9
  10. data/lib/gettext/tools/msgmerge.rb +28 -15
  11. data/lib/gettext/tools/parser/erb.rb +25 -5
  12. data/lib/gettext/tools/parser/glade.rb +4 -3
  13. data/lib/gettext/tools/parser/ruby.rb +23 -5
  14. data/lib/gettext/tools/poparser.rb +2 -1
  15. data/lib/gettext/tools/xgettext.rb +187 -136
  16. data/lib/gettext/version.rb +1 -1
  17. data/po/ja/gettext.po +82 -41
  18. data/samples/cgi/locale/bg/LC_MESSAGES/main.mo +0 -0
  19. data/samples/cgi/locale/bs/LC_MESSAGES/main.mo +0 -0
  20. data/samples/cgi/locale/ca/LC_MESSAGES/main.mo +0 -0
  21. data/samples/cgi/locale/cs/LC_MESSAGES/main.mo +0 -0
  22. data/samples/cgi/locale/de/LC_MESSAGES/main.mo +0 -0
  23. data/samples/cgi/locale/el/LC_MESSAGES/main.mo +0 -0
  24. data/samples/cgi/locale/eo/LC_MESSAGES/main.mo +0 -0
  25. data/samples/cgi/locale/es/LC_MESSAGES/main.mo +0 -0
  26. data/samples/po/hello.pot +3 -3
  27. data/samples/po/hello2.pot +3 -3
  28. data/samples/po/hello_glade2.pot +3 -3
  29. data/samples/po/hello_gtk2.pot +3 -3
  30. data/samples/po/hello_noop.pot +3 -3
  31. data/samples/po/hello_plural.pot +3 -3
  32. data/samples/po/hello_tk.pot +3 -3
  33. data/src/poparser.ry +1 -0
  34. data/test/fixtures/_.rb +4 -4
  35. data/test/fixtures/{erb.rhtml → erb/ascii.rhtml} +0 -0
  36. data/test/fixtures/{erb.rxml → erb/ascii.rxml} +0 -0
  37. data/test/fixtures/erb/non_ascii.rhtml +13 -0
  38. data/test/fixtures/untranslated.rb +12 -0
  39. data/test/gettext-test-utils.rb +23 -0
  40. data/test/locale/ja/LC_MESSAGES/untranslated.mo +0 -0
  41. data/test/parser/test_ruby.rb +48 -1
  42. data/test/po/_.pot +4 -4
  43. data/test/po/ascii.pot +23 -0
  44. data/test/po/backslash.pot +3 -3
  45. data/test/po/ja/untranslated.po +22 -0
  46. data/test/po/no_exist_msgid.pot +20 -0
  47. data/test/po/non_ascii.pot +3 -3
  48. data/test/po/not_existed_msgid.pot +20 -0
  49. data/test/po/np_.pot +3 -3
  50. data/test/po/ns_.pot +3 -3
  51. data/test/po/p_.pot +3 -3
  52. data/test/po/s_.pot +3 -3
  53. data/test/po/untranslated.pot +23 -0
  54. data/test/run-test.rb +1 -1
  55. data/test/test_gettext.rb +2 -2
  56. data/test/test_mofile.rb +10 -0
  57. data/test/test_parser.rb +45 -19
  58. data/test/test_po_parser.rb +14 -1
  59. data/test/tools/test_msginit.rb +52 -36
  60. data/test/tools/test_msgmerge.rb +44 -6
  61. data/test/tools/test_xgettext.rb +203 -5
  62. metadata +143 -146
  63. data/po/de/gettext.po +0 -668
  64. data/po/de/gettext.po.bak +0 -589
  65. data/po/el/gettext.po +0 -571
  66. data/po/fr/gettext.po +0 -589
  67. data/po/gettext.pot +0 -638
  68. data/po/gettext.pot~ +0 -638
  69. data/po/it/gettext.po +0 -589
  70. data/po/uk/gettext.po +0 -571
  71. data/test/locale/ja/LC_MESSAGES/npgettext.mo +0 -0
  72. data/test/locale/ja/LC_MESSAGES/nsgettext.mo +0 -0
  73. data/test/locale/ja/LC_MESSAGES/pgettext.mo +0 -0
  74. data/test/locale/ja/LC_MESSAGES/sgettext.mo +0 -0
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  =begin
2
3
  parser/ruby.rb - parser for ruby script
3
4
 
@@ -118,13 +119,30 @@ module GetText
118
119
  # (and ignored here).
119
120
  # And You don't need to keep the pomessages as unique.
120
121
 
121
- def parse(path, deprecated = []) # :nodoc:
122
- lines = IO.readlines(path)
123
- parse_lines(path, lines, deprecated)
122
+ def parse(path) # :nodoc:
123
+ source = IO.read(path)
124
+
125
+ if source.respond_to?(:encode)
126
+ encoding = detect_encoding(source) || source.encoding
127
+
128
+ source.force_encoding(encoding)
129
+ end
130
+
131
+ parse_lines(path, source.each_line.to_a)
132
+ end
133
+
134
+ def detect_encoding(source)
135
+ return nil unless source.respond_to?(:force_encoding)
136
+ binary_source = source.dup.force_encoding("ASCII-8BIT")
137
+ if /\A.*coding\s*[=:]\s*([[:alnum:]\-_]+)/ =~ binary_source
138
+ $1.gsub(/-(?:unix|mac|dos)\z/, "")
139
+ else
140
+ nil
141
+ end
124
142
  end
125
143
 
126
- def parse_lines(path, lines, deprecated = []) # :nodoc:
127
- pomessages = deprecated
144
+ def parse_lines(path, lines) # :nodoc:
145
+ pomessages = []
128
146
  file = StringIO.new(lines.join + "\n")
129
147
  rl = RubyLexX.new
130
148
  rl.set_input(file)
@@ -123,6 +123,7 @@ module_eval(<<'...end poparser.ry/module_eval...', 'poparser.ry', 119)
123
123
  end
124
124
 
125
125
  def on_message(msgid, msgstr)
126
+ msgstr = nil if msgstr.empty?
126
127
  @data[msgid] = msgstr
127
128
  @data.set_comment(msgid, @comments.join("\n"))
128
129
 
@@ -387,6 +388,6 @@ def _reduce_none(val, _values, result)
387
388
  end
388
389
 
389
390
  end # class PoParser
390
- end # module GetText
391
+ end # module GetText
391
392
 
392
393
 
@@ -22,38 +22,52 @@
22
22
 
23
23
  require "pathname"
24
24
  require "optparse"
25
+ require "locale"
25
26
  require "gettext"
26
27
 
27
28
  module GetText
28
29
  module Tools
29
- class XGetText #:nodoc:
30
+ class XGetText
30
31
  class << self
31
32
  def run(*arguments)
32
33
  new.run(*arguments)
33
34
  end
35
+
36
+ # Adds a parser to the default parser list.
37
+ #
38
+ # @param (see #add_parser)
39
+ # @return [void]
40
+ #
41
+ # @see #add_parser
42
+ def add_parser(parser)
43
+ @@default_parsers.unshift(parser)
44
+ end
34
45
  end
35
46
 
36
47
  include GetText
37
48
 
38
49
  bindtextdomain("rgettext")
39
50
 
40
- def initialize #:nodoc:
41
- @ex_parsers = []
42
- parsers = [
43
- ["glade.rb", "GladeParser"],
44
- ["erb.rb", "ErbParser"],
45
- # ["ripper.rb", "RipperParser"],
46
- ["ruby.rb", "RubyParser"] # Default parser.
47
- ]
48
- parsers.each do |f, klass|
49
- begin
50
- require "gettext/tools/parser/#{f}"
51
- @ex_parsers << GetText.const_get(klass)
52
- rescue
53
- $stderr.puts(_("'%{klass}' is ignored.") % {:klass => klass})
54
- $stderr.puts($!) if $DEBUG
55
- end
51
+ # @api private
52
+ @@default_parsers = []
53
+ builtin_parser_info_list = [
54
+ ["glade", "GladeParser"],
55
+ ["erb", "ErbParser"],
56
+ # ["ripper", "RipperParser"],
57
+ ["ruby", "RubyParser"] # Default parser.
58
+ ]
59
+ builtin_parser_info_list.each do |f, klass|
60
+ begin
61
+ require "gettext/tools/parser/#{f}"
62
+ @@default_parsers << GetText.const_get(klass)
63
+ rescue
64
+ $stderr.puts(_("'%{klass}' is ignored.") % {:klass => klass})
65
+ $stderr.puts($!) if $DEBUG
56
66
  end
67
+ end
68
+
69
+ def initialize #:nodoc:
70
+ @parsers = @@default_parsers
57
71
 
58
72
  @input_files = nil
59
73
  @output = nil
@@ -62,53 +76,58 @@ module GetText
62
76
  @package_version = nil
63
77
  @msgid_bugs_address = nil
64
78
  @copyright_holder = nil
79
+ @output_encoding = nil
65
80
  end
66
81
 
67
- # How to add your option parser
68
- # The option parser module requires to have target?(file) and
69
- # parser(file, ary) method.
82
+ # The parser object requires to have target?(path) and
83
+ # parse(path) method.
70
84
  #
71
- # require "gettext/tools/xgettext"
72
- # module FooParser
73
- # module_function
74
- # def target?(file)
75
- # File.extname(file) == ".foo" # *.foo file only.
76
- # end
77
- # def parse(file)
78
- # :
79
- # ary = []
80
- # # Simple message
81
- # po = PoMessage.new(:normal)
82
- # po.msgid = "hello"
83
- # po.sources = ["foo.rb:200", "bar.rb:300"]
84
- # po.add_comment("Comment for the message")
85
- # ary << po
86
- # # Plural message
87
- # po = PoMessage.new(:plural)
88
- # po.msgid = "An apple"
89
- # po.msgid_plural = "Apples"
90
- # po.sources = ["foo.rb:200", "bar.rb:300"]
91
- # ary << po
92
- # # Simple message with the message context
93
- # po = PoMessage.new(:msgctxt)
94
- # po.msgctxt = "context"
95
- # po.msgid = "hello"
96
- # po.sources = ["foo.rb:200", "bar.rb:300"]
97
- # ary << po
98
- # # Plural message with the message context.
99
- # po = PoMessage.new(:msgctxt_plural)
100
- # po.msgctxt = "context"
101
- # po.msgid = "An apple"
102
- # po.msgid_plural = "Apples"
103
- # po.sources = ["foo.rb:200", "bar.rb:300"]
104
- # ary << po
105
- # return ary
106
- # end
107
- # end
85
+ # @example How to add your parser
86
+ # require "gettext/tools/xgettext"
87
+ # class FooParser
88
+ # def target?(path)
89
+ # File.extname(path) == ".foo" # *.foo file only.
90
+ # end
91
+ # def parse(path)
92
+ # po_messages = []
93
+ # # Simple message
94
+ # message = PoMessage.new(:normal)
95
+ # message.msgid = "hello"
96
+ # message.sources = ["foo.rb:200", "bar.rb:300"]
97
+ # message.add_comment("Comment for the message")
98
+ # po_messages << message
99
+ # # Plural message
100
+ # message = PoMessage.new(:plural)
101
+ # message.msgid = "An apple"
102
+ # message.msgid_plural = "Apples"
103
+ # message.sources = ["foo.rb:200", "bar.rb:300"]
104
+ # po_messages << message
105
+ # # Simple message with the message context
106
+ # message = PoMessage.new(:msgctxt)
107
+ # message.msgctxt = "context"
108
+ # message.msgid = "hello"
109
+ # message.sources = ["foo.rb:200", "bar.rb:300"]
110
+ # po_messages << message
111
+ # # Plural message with the message context.
112
+ # message = PoMessage.new(:msgctxt_plural)
113
+ # message.msgctxt = "context"
114
+ # message.msgid = "An apple"
115
+ # message.msgid_plural = "Apples"
116
+ # message.sources = ["foo.rb:200", "bar.rb:300"]
117
+ # po_messages << message
118
+ # return po_messages
119
+ # end
120
+ # end
108
121
  #
109
- # GetText::XGetText.add_parser(FooParser)
110
- def add_parser(klass)
111
- @ex_parsers.insert(0, klass)
122
+ # GetText::XGetText.add_parser(FooParser.new)
123
+ #
124
+ # @param [#target?, #parse] parser
125
+ # It parses target file and extracts translate target messages from the
126
+ # target file. If there are multiple target files, parser.parse is
127
+ # called multiple times.
128
+ # @return [void]
129
+ def add_parser(parser)
130
+ @parsers.unshift(parser)
112
131
  end
113
132
 
114
133
  def generate_pot_header # :nodoc:
@@ -131,99 +150,35 @@ msgstr ""
131
150
  "Language-Team: LANGUAGE <LL@li.org>\\n"
132
151
  "Language: \\n"
133
152
  "MIME-Version: 1.0\\n"
134
- "Content-Type: text/plain; charset=UTF-8\\n"
153
+ "Content-Type: text/plain; charset=#{@output_encoding}\\n"
135
154
  "Content-Transfer-Encoding: 8bit\\n"
136
155
  "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n"
137
156
  EOH
138
157
  end
139
158
 
140
159
  def generate_pot(paths) # :nodoc:
141
- pomessages = parse(paths)
160
+ po_messages = parse(paths)
142
161
  str = ""
143
- pomessages.each do |target|
144
- str << target.to_po_str
162
+ po_messages.each do |target|
163
+ str << encode(target.to_po_str)
145
164
  end
146
165
  str
147
166
  end
148
167
 
149
168
  def parse(paths) # :nodoc:
150
- pomessages = []
169
+ po_messages = []
151
170
  paths = [paths] if paths.kind_of?(String)
152
171
  paths.each do |path|
153
172
  begin
154
- @ex_parsers.each do |klass|
155
- next unless klass.target?(path)
156
-
157
- if klass.method(:parse).arity == 1
158
- targets = klass.parse(path)
159
- else
160
- # For backward compatibility
161
- targets = klass.parse(path, [])
162
- end
163
-
164
- targets.each do |pomessage|
165
- if pomessage.kind_of?(Array)
166
- pomessage = PoMessage.new_from_ary(pomessage)
167
- end
168
-
169
- if pomessage.msgid.empty?
170
- warn _("Warning: The empty \"\" msgid is reserved by " +
171
- "gettext. So gettext(\"\") doesn't returns " +
172
- "empty string but the header entry in po file.")
173
- # TODO: add pommesage.source to the pot header as below:
174
- # # SOME DESCRIPTIVE TITLE.
175
- # # Copyright (C) YEAR THE COPYRIGHT HOLDER
176
- # # This file is distributed under the same license as the PACKAGE package.
177
- # # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
178
- # #
179
- # #: test/test_gettext.rb:65
180
- # #, fuzzy
181
- # "#: test/test_gettext.rb:65" line is added.
182
- next
183
- end
184
-
185
- if @output.is_a?(String)
186
- base_path = Pathname.new(@output).dirname.expand_path
187
- pomessage.sources = pomessage.sources.collect do |source|
188
- path, line, = source.split(/:(\d+)\z/, 2)
189
- absolute_path = Pathname.new(path).expand_path
190
- begin
191
- path = absolute_path.relative_path_from(base_path).to_s
192
- rescue ArgumentError
193
- raise # Should we ignore it?
194
- end
195
- "#{path}:#{line}"
196
- end
197
- end
198
-
199
- # Save the previous target
200
- if pomessages.empty?
201
- existing = nil
202
- else
203
- message = pomessages.find {|t| t == pomessage}
204
- existing = pomessages.index(message)
205
- end
206
-
207
- if existing
208
- pomessage = pomessages[existing].merge(pomessage)
209
- pomessages[existing] = pomessage
210
- else
211
- pomessages << pomessage
212
- end
213
- end
214
- break
215
- end
173
+ parse_path(path, po_messages)
216
174
  rescue
217
175
  puts(_("Error parsing %{path}") % {:path => path})
218
176
  raise
219
177
  end
220
178
  end
221
- pomessages
179
+ po_messages
222
180
  end
223
181
 
224
- # constant values
225
- VERSION = GetText::VERSION
226
-
227
182
  def check_command_line_options(*options) # :nodoc:
228
183
  input_files, output = parse_arguments(*options)
229
184
 
@@ -279,6 +234,11 @@ EOH
279
234
  @copyright_holder = out
280
235
  end
281
236
 
237
+ parser.on("--output-encoding=ENCODING",
238
+ _("set encoding for output")) do |encoding|
239
+ @output_encoding = encoding
240
+ end
241
+
282
242
  parser.on("-r", "--require=library",
283
243
  _("require the library before executing xgettext")) do |out|
284
244
  require out
@@ -294,7 +254,7 @@ EOH
294
254
  end
295
255
 
296
256
  parser.on_tail("--version", _("display version information and exit")) do
297
- puts(VERSION)
257
+ puts(GetText::VERSION)
298
258
  exit(true)
299
259
  end
300
260
 
@@ -306,14 +266,19 @@ EOH
306
266
  def run(*options) # :nodoc:
307
267
  check_command_line_options(*options)
308
268
 
269
+ @output_encoding ||= "UTF-8"
270
+
271
+ pot_header = generate_pot_header
272
+ pot_messages = generate_pot(@input_files)
273
+
309
274
  if @output.is_a?(String)
310
275
  File.open(File.expand_path(@output), "w+") do |file|
311
- file.puts(generate_pot_header)
312
- file.puts(generate_pot(@input_files))
276
+ file.puts(pot_header)
277
+ file.puts(pot_messages)
313
278
  end
314
279
  else
315
- @output.puts(generate_pot_header)
316
- @output.puts(generate_pot(@input_files))
280
+ @output.puts(pot_header)
281
+ @output.puts(pot_messages)
317
282
  end
318
283
  self
319
284
  end
@@ -322,6 +287,92 @@ EOH
322
287
  def now
323
288
  Time.now
324
289
  end
290
+
291
+ def parse_path(path, po_messages)
292
+ @parsers.each do |parser|
293
+ next unless parser.target?(path)
294
+
295
+ extracted_po_messages = parser.parse(path)
296
+ extracted_po_messages.each do |po_message|
297
+ if po_message.kind_of?(Array)
298
+ po_message = PoMessage.new_from_ary(po_message)
299
+ end
300
+
301
+ if po_message.msgid.empty?
302
+ warn _("Warning: The empty \"\" msgid is reserved by " +
303
+ "gettext. So gettext(\"\") doesn't returns " +
304
+ "empty string but the header entry in po file.")
305
+ # TODO: add pommesage.source to the pot header as below:
306
+ # # SOME DESCRIPTIVE TITLE.
307
+ # # Copyright (C) YEAR THE COPYRIGHT HOLDER
308
+ # # This file is distributed under the same license as the PACKAGE package.
309
+ # # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
310
+ # #
311
+ # #: test/test_gettext.rb:65
312
+ # #, fuzzy
313
+ # "#: test/test_gettext.rb:65" line is added.
314
+ next
315
+ end
316
+
317
+ if @output.is_a?(String)
318
+ base_path = Pathname.new(@output).dirname.expand_path
319
+ po_message.sources = po_message.sources.collect do |source|
320
+ path, line, = source.split(/:(\d+)\z/, 2)
321
+ absolute_path = Pathname.new(path).expand_path
322
+ begin
323
+ path = absolute_path.relative_path_from(base_path).to_s
324
+ rescue ArgumentError
325
+ raise # Should we ignore it?
326
+ end
327
+ "#{path}:#{line}"
328
+ end
329
+ end
330
+
331
+ # Save the previous target
332
+ if po_messages.empty?
333
+ existing = nil
334
+ else
335
+ message = po_messages.find {|t| t == po_message}
336
+ existing = po_messages.index(message)
337
+ end
338
+
339
+ if existing
340
+ po_message = po_messages[existing].merge(po_message)
341
+ po_messages[existing] = po_message
342
+ else
343
+ po_messages << po_message
344
+ end
345
+ end
346
+ break
347
+ end
348
+ end
349
+
350
+ def encode(string)
351
+ if string.respond_to?(:encode)
352
+ string.encode(@output_encoding)
353
+ else
354
+ string # don't support
355
+ end
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+ # This is the module for backward compatibility,
362
+ # but GetText::Tools::XGetText.run should be used.
363
+ module GetText
364
+ module RGetText
365
+ def run(paths=nil, out=STDOUT)
366
+ warn("Warning: This method is obsolete. " +
367
+ "Please use GetText::Tools::XGetText.run.")
368
+ if paths.nil?
369
+ GetText::Tools::XGetText.run(*ARGV)
370
+ elsif out == STDOUT
371
+ GetText::Tools::XGetText.run(paths)
372
+ else
373
+ GetText::Tools::XGetText.run("-o", out, paths)
374
+ end
325
375
  end
376
+ module_function :run
326
377
  end
327
378
  end