gettext 2.3.0 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
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