gettext 2.3.9 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.yardopts +1 -0
  2. data/README.rdoc +28 -23
  3. data/Rakefile +29 -21
  4. data/doc/text/news.md +57 -0
  5. data/gettext.gemspec +1 -1
  6. data/lib/gettext.rb +13 -29
  7. data/lib/gettext/cgi.rb +1 -1
  8. data/lib/gettext/{runtime/class_info.rb → class_info.rb} +1 -1
  9. data/lib/gettext/{runtime/locale_path.rb → locale_path.rb} +1 -2
  10. data/lib/gettext/{runtime/mo.rb → mo.rb} +18 -33
  11. data/lib/gettext/{tools/po.rb → po.rb} +1 -1
  12. data/lib/gettext/{tools/po_entry.rb → po_entry.rb} +8 -4
  13. data/lib/gettext/{tools/poparser.rb → po_parser.rb} +41 -44
  14. data/lib/gettext/{runtime/textdomain.rb → text_domain.rb} +6 -16
  15. data/lib/gettext/text_domain_group.rb +26 -0
  16. data/lib/gettext/{runtime/textdomain_manager.rb → text_domain_manager.rb} +40 -40
  17. data/lib/gettext/tools.rb +1 -182
  18. data/lib/gettext/tools/msgfmt.rb +1 -1
  19. data/lib/gettext/tools/msginit.rb +1 -1
  20. data/lib/gettext/tools/msgmerge.rb +2 -221
  21. data/lib/gettext/tools/parser/erb.rb +49 -30
  22. data/lib/gettext/tools/parser/glade.rb +44 -36
  23. data/lib/gettext/tools/parser/ruby.rb +126 -37
  24. data/lib/gettext/tools/task.rb +225 -0
  25. data/lib/gettext/tools/xgettext.rb +25 -28
  26. data/lib/gettext/version.rb +1 -1
  27. data/locale/bg/LC_MESSAGES/gettext.mo +0 -0
  28. data/locale/bs/LC_MESSAGES/gettext.mo +0 -0
  29. data/locale/ca/LC_MESSAGES/gettext.mo +0 -0
  30. data/locale/cs/LC_MESSAGES/gettext.mo +0 -0
  31. data/locale/de/LC_MESSAGES/gettext.mo +0 -0
  32. data/locale/el/LC_MESSAGES/gettext.mo +0 -0
  33. data/locale/eo/LC_MESSAGES/gettext.mo +0 -0
  34. data/locale/es/LC_MESSAGES/gettext.mo +0 -0
  35. data/locale/et/LC_MESSAGES/gettext.mo +0 -0
  36. data/locale/fr/LC_MESSAGES/gettext.mo +0 -0
  37. data/locale/hr/LC_MESSAGES/gettext.mo +0 -0
  38. data/locale/hu/LC_MESSAGES/gettext.mo +0 -0
  39. data/locale/it/LC_MESSAGES/gettext.mo +0 -0
  40. data/locale/ja/LC_MESSAGES/gettext.mo +0 -0
  41. data/locale/ko/LC_MESSAGES/gettext.mo +0 -0
  42. data/locale/lv/LC_MESSAGES/gettext.mo +0 -0
  43. data/locale/nb/LC_MESSAGES/gettext.mo +0 -0
  44. data/locale/nl/LC_MESSAGES/gettext.mo +0 -0
  45. data/locale/pt_BR/LC_MESSAGES/gettext.mo +0 -0
  46. data/locale/ru/LC_MESSAGES/gettext.mo +0 -0
  47. data/locale/sr/LC_MESSAGES/gettext.mo +0 -0
  48. data/locale/sv/LC_MESSAGES/gettext.mo +0 -0
  49. data/locale/uk/LC_MESSAGES/gettext.mo +0 -0
  50. data/locale/vi/LC_MESSAGES/gettext.mo +0 -0
  51. data/locale/zh/LC_MESSAGES/gettext.mo +0 -0
  52. data/locale/zh_TW/LC_MESSAGES/gettext.mo +0 -0
  53. data/po/gettext.pot +171 -97
  54. data/samples/hello.rb +3 -2
  55. data/samples/hello2.rb +2 -1
  56. data/samples/hello_gtk2.rb +4 -1
  57. data/samples/hello_gtk_builder.rb +32 -0
  58. data/samples/hello_gtk_builder.ui +46 -0
  59. data/samples/hello_noop.rb +2 -1
  60. data/samples/hello_plural.rb +2 -1
  61. data/samples/hello_tk.rb +2 -1
  62. data/src/{poparser.ry → po_parser.ry} +31 -35
  63. data/test/fixtures/N_.rb +1 -1
  64. data/test/fixtures/_/double_quote_in_double_quote.rb +32 -0
  65. data/test/fixtures/_/double_quote_in_single_quote.rb +32 -0
  66. data/test/fixtures/_/literal_concatenation_with_continuation_line.rb +36 -0
  67. data/test/fixtures/_/middle_new_line.rb +32 -0
  68. data/test/fixtures/_/multiple_lines_literal.rb +35 -0
  69. data/test/fixtures/_/multiple_messages_in_same_line.rb +32 -0
  70. data/test/fixtures/_/multiple_same_messages.rb +36 -0
  71. data/{lib/gettext/runtime/mofile.rb → test/fixtures/_/one_new_line.rb} +12 -6
  72. data/test/fixtures/{multi_textdomain.rb → multi_text_domain.rb} +1 -1
  73. data/test/fixtures/non_ascii.rb +1 -1
  74. data/test/fixtures/simple.rb +1 -1
  75. data/test/fixtures/untranslated.rb +1 -1
  76. data/test/gettext-test-utils.rb +1 -24
  77. data/test/po/ja/_.po +63 -13
  78. data/test/po/ja/non_ascii.po +2 -3
  79. data/test/po/ja/untranslated.po +1 -1
  80. data/test/test_class_info.rb +2 -2
  81. data/test/test_gettext.rb +11 -11
  82. data/test/test_mo.rb +2 -2
  83. data/test/test_parser.rb +93 -96
  84. data/test/test_po_entry.rb +237 -146
  85. data/test/test_po_parser.rb +107 -98
  86. data/test/test_string.rb +1 -1
  87. data/test/{test_textdomain_bind.rb → test_text_domain_bind.rb} +6 -6
  88. data/test/{test_textdomain_multi.rb → test_text_domain_multi.rb} +5 -5
  89. data/test/{test_textdomain_toplevel.rb → test_text_domain_toplevel.rb} +1 -1
  90. data/test/test_thread.rb +1 -1
  91. data/test/tools/files/simple_translation.rb +1 -1
  92. data/test/{parser → tools/parser}/test_ruby.rb +110 -14
  93. data/test/tools/test_msgmerge.rb +17 -276
  94. data/test/tools/test_po.rb +1 -1
  95. data/test/tools/test_xgettext.rb +175 -144
  96. metadata +59 -33
  97. data/lib/gettext/core_ext/iconv.rb +0 -110
  98. data/lib/gettext/core_ext/string.rb +0 -91
  99. data/lib/gettext/parser/erb.rb +0 -5
  100. data/lib/gettext/parser/glade.rb +0 -5
  101. data/lib/gettext/parser/ruby.rb +0 -172
  102. data/lib/gettext/runtime/textdomain_group.rb +0 -26
  103. data/lib/gettext/task.rb +0 -203
  104. data/lib/gettext/utils.rb +0 -39
  105. data/test/test_po_generation.rb +0 -45
  106. data/test/tools/test_tools.rb +0 -61
@@ -1,8 +1,9 @@
1
- # encoding: utf-8
1
+ # -*- coding: utf-8 -*-
2
2
 
3
3
  =begin
4
4
  parser/glade.rb - parser for Glade-2
5
5
 
6
+ Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
6
7
  Copyright (C) 2004,2005 Masao Mutoh
7
8
 
8
9
  You may redistribute it and/or modify it under the same
@@ -13,47 +14,69 @@ require 'cgi'
13
14
  require 'gettext'
14
15
 
15
16
  module GetText
16
- module GladeParser
17
+ class GladeParser
17
18
  extend GetText
18
- extend self
19
19
 
20
20
  bindtextdomain("gettext")
21
21
 
22
+ class << self
23
+ XML_RE = /<\?xml/
24
+ GLADE_RE = /glade-2.0.dtd/
25
+
26
+ 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
36
+ end
37
+
38
+ def parse(path, options={})
39
+ parser = new(path, options)
40
+ parser.parse
41
+ end
42
+ end
43
+
22
44
  TARGET1 = /<property.*translatable="yes">(.*)/
23
45
  TARGET2 = /(.*)<\/property>/
24
46
 
25
- def parse(file) # :nodoc:
26
- lines = IO.readlines(file)
27
- parse_lines(file, lines)
47
+ def initialize(path, options={})
48
+ @path = path
49
+ @options = options
50
+ end
51
+
52
+ def parse # :nodoc:
53
+ File.open(@path) do |file|
54
+ parse_source(file)
55
+ end
28
56
  end
29
57
 
30
- #from ary of lines.
31
- def parse_lines(file, lines) # :nodoc:
58
+ private
59
+ def parse_source(input) # :nodoc:
32
60
  targets = []
33
- cnt = 0
34
61
  target = false
35
- line_no = 0
62
+ start_line_no = nil
36
63
  val = nil
37
64
 
38
- loop do
39
- line = lines.shift
40
- break unless line
41
-
42
- cnt += 1
65
+ input.each_line.with_index do |line, i|
43
66
  if TARGET1 =~ line
44
- line_no = cnt
67
+ start_line_no = i + 1
45
68
  val = $1 + "\n"
46
69
  target = true
47
70
  if TARGET2 =~ $1
48
71
  val = $1
49
- add_target(val, file, line_no, targets)
72
+ add_target(val, start_line_no, targets)
50
73
  val = nil
51
74
  target = false
52
75
  end
53
76
  elsif target
54
77
  if TARGET2 =~ line
55
78
  val << $1
56
- add_target(val, file, line_no, targets)
79
+ add_target(val, start_line_no, targets)
57
80
  val = nil
58
81
  target = false
59
82
  else
@@ -64,29 +87,14 @@ module GetText
64
87
  targets
65
88
  end
66
89
 
67
- XML_RE = /<\?xml/
68
- GLADE_RE = /glade-2.0.dtd/
69
-
70
- def target?(file) # :nodoc:
71
- data = IO.readlines(file)
72
- if XML_RE =~ data[0] and GLADE_RE =~ data[1]
73
- true
74
- else
75
- if File.extname(file) == '.glade'
76
- raise _("`%{file}' is not glade-2.0 format.") % {:file => file}
77
- end
78
- false
79
- end
80
- end
81
-
82
- def add_target(val, file, line_no, targets) # :nodoc:
90
+ def add_target(val, line_no, targets) # :nodoc:
83
91
  return unless val.size > 0
84
92
  assoc_data = targets.assoc(val)
85
93
  val = CGI.unescapeHTML(val)
86
94
  if assoc_data
87
- targets[targets.index(assoc_data)] = assoc_data << "#{file}:#{line_no}"
95
+ targets[targets.index(assoc_data)] = assoc_data << "#{@path}:#{line_no}"
88
96
  else
89
- targets << [val.gsub(/\n/, '\n'), "#{file}:#{line_no}"]
97
+ targets << [val.gsub(/\n/, '\n'), "#{@path}:#{line_no}"]
90
98
  end
91
99
  targets
92
100
  end
@@ -2,6 +2,7 @@
2
2
  =begin
3
3
  parser/ruby.rb - parser for ruby script
4
4
 
5
+ Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
5
6
  Copyright (C) 2003-2009 Masao Mutoh
6
7
  Copyright (C) 2005 speakillof
7
8
  Copyright (C) 2001,2002 Yasushi Shoji, Masao Mutoh
@@ -11,9 +12,9 @@
11
12
 
12
13
  =end
13
14
 
14
- require 'irb/ruby-lex.rb'
15
- require 'stringio'
16
- require 'gettext/tools/po_entry'
15
+ require "irb/ruby-lex"
16
+ require "stringio"
17
+ require "gettext/po_entry"
17
18
 
18
19
  module GetText
19
20
  class RubyLexX < RubyLex # :nodoc: all
@@ -31,7 +32,7 @@ module GetText
31
32
  end
32
33
 
33
34
  if @here_header
34
- s = s.sub(/\A.*?\n/, '').sub(/^.*\n\Z/, '')
35
+ s = s.sub(/\A.*?\n/, "").sub(/^.*\n\Z/, "")
35
36
  else
36
37
  begin
37
38
  s = eval(s)
@@ -85,7 +86,7 @@ module GetText
85
86
  # Supports parsing by setting attributes by and by.
86
87
  def set_current_attribute(str)
87
88
  param = @param_type[@param_number]
88
- raise ParseError, 'no more string parameters expected' unless param
89
+ raise ParseError, "no more string parameters expected" unless param
89
90
  set_value(param, str)
90
91
  end
91
92
 
@@ -107,32 +108,116 @@ module GetText
107
108
  end
108
109
  end
109
110
 
110
- module RubyParser
111
- extend self
111
+ class RubyParser
112
+ ID = ["gettext", "_", "N_", "sgettext", "s_"]
113
+ PLURAL_ID = ["ngettext", "n_", "Nn_", "ns_", "nsgettext"]
114
+ MSGCTXT_ID = ["pgettext", "p_"]
115
+ MSGCTXT_PLURAL_ID = ["npgettext", "np_"]
112
116
 
113
- ID = ['gettext', '_', 'N_', 'sgettext', 's_']
114
- PLURAL_ID = ['ngettext', 'n_', 'Nn_', 'ns_', 'nsgettext']
115
- MSGCTXT_ID = ['pgettext', 'p_']
116
- MSGCTXT_PLURAL_ID = ['npgettext', 'np_']
117
+ class << self
118
+ def target?(file) # :nodoc:
119
+ true # always true, as the default parser.
120
+ end
117
121
 
118
- # (Since 2.1.0) the 2nd parameter is deprecated
119
- # (and ignored here).
120
- # And You don't need to keep the poentries as unique.
122
+ # Parses Ruby script located at `path`.
123
+ #
124
+ # This is a short cut method. It equals to `new(path,
125
+ # options).parse`.
126
+ #
127
+ # @param (see #initialize)
128
+ # @option (see #initialize)
129
+ # @return (see #parse)
130
+ # @see #initialize
131
+ # @see #parse
132
+ def parse(path, options={})
133
+ parser = new(path, options)
134
+ parser.parse
135
+ end
136
+ end
121
137
 
122
- def parse(path) # :nodoc:
123
- source = IO.read(path)
138
+ #
139
+ # @example `:comment_tag` option: String tag
140
+ # path = "hello.rb"
141
+ # # content:
142
+ # # # TRANSLATORS: This is a comment to translators.
143
+ # # _("Hello")
144
+ # #
145
+ # # # This is a comment for programmers.
146
+ # # # TRANSLATORS: This is a comment to translators.
147
+ # # # This is also a comment to translators.
148
+ # # _("World")
149
+ # #
150
+ # # # This is a comment for programmers.
151
+ # # # This is also a comment for programmers
152
+ # # # because all lines don't start with "TRANSRATORS:".
153
+ # # _("Bye")
154
+ # options = {:comment_tag => "TRANSLATORS:"}
155
+ # parser = GetText::RubyParser.new(path, options)
156
+ # parser.parse
157
+ # # => [
158
+ # # POEntry<
159
+ # # :msgid => "Hello",
160
+ # # :extracted_comment =>
161
+ # # "TRANSLATORS: This is a comment to translators.",
162
+ # # >,
163
+ # # POEntry<
164
+ # # :msgid => "World",
165
+ # # :extracted_comment =>
166
+ # # "TRANSLATORS: This is a comment to translators.\n" +
167
+ # # "This is also a comment to translators.",
168
+ # # >,
169
+ # # POEntry<
170
+ # # :msgid => "Bye",
171
+ # # :extracted_comment => nil,
172
+ # # >,
173
+ # # ]
174
+ #
175
+ # @example `:comment_tag` option: nil tag
176
+ # path = "hello.rb"
177
+ # # content:
178
+ # # # This is a comment to translators.
179
+ # # # This is also a comment for translators.
180
+ # # _("Hello")
181
+ # options = {:comment_tag => nil}
182
+ # parser = GetText::RubyParser.new(path, options)
183
+ # parser.parse
184
+ # # => [
185
+ # # POEntry<
186
+ # # :msgid => "Hello",
187
+ # # :extracted_comment =>
188
+ # # "This is a comment to translators.\n" +
189
+ # # " This is also a comment for translators.",
190
+ # # >,
191
+ # # ]
192
+ #
193
+ # @param path [String] Ruby script path to be parsed
194
+ # @param options [Hash] Options
195
+ # @option options [String, nil] :comment_tag The tag to
196
+ # detect comments to be extracted. The extracted comments are
197
+ # used to deliver messages to translators from programmers.
198
+ #
199
+ # If the tag is String and a line in a comment start with the
200
+ # tag, the line and the following lines are extracted.
201
+ #
202
+ # If the tag is nil, all comments are extracted.
203
+ def initialize(path, options={})
204
+ @path = path
205
+ @options = options
206
+ end
124
207
 
125
- if source.respond_to?(:encode)
126
- encoding = detect_encoding(source) || source.encoding
208
+ # Extracts messages from @path.
209
+ #
210
+ # @return [Array<POEntry>] Extracted messages
211
+ def parse
212
+ source = IO.read(@path)
127
213
 
128
- source.force_encoding(encoding)
129
- end
214
+ encoding = detect_encoding(source) || source.encoding
215
+ source.force_encoding(encoding)
130
216
 
131
- parse_lines(path, source.each_line.to_a)
217
+ parse_source(source)
132
218
  end
133
219
 
134
220
  def detect_encoding(source)
135
- return nil unless source.respond_to?(:force_encoding)
136
221
  binary_source = source.dup.force_encoding("ASCII-8BIT")
137
222
  if /\A.*coding\s*[=:]\s*([[:alnum:]\-_]+)/ =~ binary_source
138
223
  $1.gsub(/-(?:unix|mac|dos)\z/, "")
@@ -141,9 +226,9 @@ module GetText
141
226
  end
142
227
  end
143
228
 
144
- def parse_lines(path, lines) # :nodoc:
229
+ def parse_source(source)
145
230
  po = []
146
- file = StringIO.new(lines.join + "\n")
231
+ file = StringIO.new(source)
147
232
  rl = RubyLexX.new
148
233
  rl.set_input(file)
149
234
  rl.skip_space = true
@@ -151,7 +236,7 @@ module GetText
151
236
 
152
237
  po_entry = nil
153
238
  line_no = nil
154
- last_comment = ''
239
+ last_comment = ""
155
240
  reset_comment = false
156
241
  ignore_next_comma = false
157
242
  rl.parse do |tk|
@@ -160,7 +245,7 @@ module GetText
160
245
  ignore_next_comma = false
161
246
  case tk
162
247
  when RubyToken::TkIDENTIFIER, RubyToken::TkCONSTANT
163
- if store_po_entry(po, po_entry, path, line_no, last_comment)
248
+ if store_po_entry(po, po_entry, line_no, last_comment)
164
249
  last_comment = ""
165
250
  end
166
251
  if ID.include?(tk.name)
@@ -186,14 +271,14 @@ module GetText
186
271
  po_entry.advance_to_next_attribute if po_entry
187
272
  end
188
273
  else
189
- if store_po_entry(po, po_entry, path, line_no, last_comment)
274
+ if store_po_entry(po, po_entry, line_no, last_comment)
190
275
  po_entry = nil
191
276
  last_comment = ""
192
277
  end
193
278
  end
194
279
  rescue
195
280
  $stderr.print "\n\nError"
196
- $stderr.print " parsing #{path}:#{tk.line_no}\n\t #{lines[tk.line_no - 1]}" if tk
281
+ $stderr.print " parsing #{@path}:#{tk.line_no}\n\t #{source.lines.to_a[tk.line_no - 1]}" if tk
197
282
  $stderr.print "\n #{$!.inspect} in\n"
198
283
  $stderr.print $!.backtrace.join("\n")
199
284
  $stderr.print "\n"
@@ -204,10 +289,9 @@ module GetText
204
289
  when RubyToken::TkCOMMENT_WITH_CONTENT
205
290
  last_comment = "" if reset_comment
206
291
  if last_comment.empty?
207
- # new comment from programmer to translator?
208
292
  comment1 = tk.value.lstrip
209
- if comment1 =~ /^TRANSLATORS\:/
210
- last_comment = $'
293
+ if comment_to_be_extracted?(comment1)
294
+ last_comment << comment1
211
295
  end
212
296
  else
213
297
  last_comment += "\n"
@@ -222,14 +306,10 @@ module GetText
222
306
  po
223
307
  end
224
308
 
225
- def target?(file) # :nodoc:
226
- true # always true, as the default parser.
227
- end
228
-
229
309
  private
230
- def store_po_entry(po, po_entry, file_name, line_no, last_comment) #:nodoc:
310
+ def store_po_entry(po, po_entry, line_no, last_comment) #:nodoc:
231
311
  if po_entry && po_entry.msgid
232
- po_entry.references << file_name + ":" + line_no
312
+ po_entry.references << @path + ":" + line_no
233
313
  po_entry.add_comment(last_comment) unless last_comment.empty?
234
314
  po << po_entry
235
315
  true
@@ -237,5 +317,14 @@ module GetText
237
317
  false
238
318
  end
239
319
  end
320
+
321
+ def comment_to_be_extracted?(comment)
322
+ return false unless @options.has_key?(:comment_tag)
323
+
324
+ tag = @options[:comment_tag]
325
+ return true if tag.nil?
326
+
327
+ /\A#{Regexp.escape(tag)}/ === comment
328
+ end
240
329
  end
241
330
  end
@@ -0,0 +1,225 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # License: Ruby's or LGPL
6
+ #
7
+ # This library is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This library is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require "rake"
21
+ require "gettext/tools"
22
+
23
+ module GetText
24
+ module Tools
25
+ class Task
26
+ include GetText
27
+ include Rake::DSL
28
+
29
+ attr_accessor :locales, :po_base_directory, :mo_base_directory, :domain
30
+ attr_accessor :namespace_prefix, :files
31
+ # @return [Array<String>] Command line options for extracting messages
32
+ # from sources.
33
+ # @see GetText::Tools::XGetText
34
+ # @see `rxgettext --help`
35
+ attr_reader :xgettext_options
36
+ def initialize(spec)
37
+ @spec = spec
38
+ @locales = []
39
+ @po_base_directory = "po"
40
+ @mo_base_directory = "."
41
+ @files = target_files
42
+ @domain = @spec.name
43
+ @namespace_prefix = nil
44
+ @xgettext_options = []
45
+ yield(self) if block_given?
46
+ @locales = detect_locales if @locales.empty?
47
+ raise("must set locales: #{inspect}") if @locales.empty?
48
+ define
49
+ end
50
+
51
+ private
52
+ def define
53
+ define_file_tasks
54
+ if namespace_prefix
55
+ namespace_recursive namespace_prefix do
56
+ define_named_tasks
57
+ end
58
+ else
59
+ define_named_tasks
60
+ end
61
+ end
62
+
63
+ def define_file_tasks
64
+ unless files.empty?
65
+ pot_dependencies = files.dup
66
+ unless File.exist?(po_base_directory)
67
+ directory po_base_directory
68
+ pot_dependencies << po_base_directory
69
+ end
70
+ file pot_file => pot_dependencies do
71
+ command_line = [
72
+ "--package-name", @spec.name,
73
+ "--package-version", @spec.version.to_s,
74
+ "--output", pot_file,
75
+ ]
76
+ command_line.concat(@xgettext_options)
77
+ command_line.concat(files)
78
+ GetText::Tools::XGetText.run(*command_line)
79
+ end
80
+ end
81
+
82
+ locales.each do |locale|
83
+ _po_file = po_file(locale)
84
+ unless files.empty?
85
+ po_dependencies = [pot_file]
86
+ _po_directory = po_directory(locale)
87
+ unless File.exist?(_po_directory)
88
+ directory _po_directory
89
+ po_dependencies << _po_directory
90
+ end
91
+ file _po_file => po_dependencies do
92
+ if File.exist?(_po_file)
93
+ GetText::Tools::MsgMerge.run(po_file(locale), pot_file,
94
+ "--output", _po_file)
95
+ else
96
+ GetText::Tools::MsgInit.run("--input", pot_file,
97
+ "--output", _po_file,
98
+ "--locale", locale.to_s)
99
+ end
100
+ end
101
+ end
102
+
103
+ mo_dependencies = [_po_file]
104
+ _mo_directory = mo_directory(locale)
105
+ unless File.exist?(_mo_directory)
106
+ directory _mo_directory
107
+ mo_dependencies << _mo_directory
108
+ end
109
+ _mo_file = mo_file(locale)
110
+ file _mo_file => mo_dependencies do
111
+ GetText::Tools::MsgFmt.run(_po_file, "--output", _mo_file)
112
+ end
113
+ end
114
+ end
115
+
116
+ def define_named_tasks
117
+ namespace :gettext do
118
+ namespace :pot do
119
+ desc "Create #{pot_file}"
120
+ task :create => pot_file
121
+ end
122
+
123
+ namespace :po do
124
+ update_tasks = []
125
+ @locales.each do |locale|
126
+ namespace locale do
127
+ desc "Update #{po_file(locale)}"
128
+ task :update => po_file(locale)
129
+ update_tasks << (current_scope + ["update"]).join(":")
130
+ end
131
+ end
132
+
133
+ desc "Update *.po"
134
+ task :update => update_tasks
135
+ end
136
+
137
+ namespace :mo do
138
+ update_tasks = []
139
+ @locales.each do |locale|
140
+ namespace locale do
141
+ desc "Update #{mo_file(locale)}"
142
+ task :update => mo_file(locale)
143
+ update_tasks << (current_scope + ["update"]).join(":")
144
+ end
145
+ end
146
+
147
+ desc "Update *.mo"
148
+ task :update => update_tasks
149
+ end
150
+ end
151
+
152
+ desc "Update *.mo"
153
+ task :gettext => (current_scope + ["gettext", "mo", "update"]).join(":")
154
+ end
155
+
156
+ def pot_file
157
+ File.join(po_base_directory, "#{domain}.pot")
158
+ end
159
+
160
+ def po_directory(locale)
161
+ File.join(po_base_directory, locale.to_s)
162
+ end
163
+
164
+ def po_file(locale)
165
+ File.join(po_directory(locale), "#{domain}.po")
166
+ end
167
+
168
+ def mo_directory(locale)
169
+ File.join(mo_base_directory, "locale", locale.to_s, "LC_MESSAGES")
170
+ end
171
+
172
+ def mo_file(locale)
173
+ File.join(mo_directory(locale), "#{domain}.mo")
174
+ end
175
+
176
+ def target_files
177
+ files = @spec.files.find_all do |file|
178
+ /\A\.(?:rb|erb|glade)\z/i =~ File.extname(file)
179
+ end
180
+ files += @spec.executables.collect do |executable|
181
+ "bin/#{executable}"
182
+ end
183
+ files
184
+ end
185
+
186
+ def detect_locales
187
+ locales = []
188
+ return locales unless File.exist?(po_base_directory)
189
+
190
+ Dir.open(po_base_directory) do |dir|
191
+ dir.each do |entry|
192
+ next unless /\A[a-z]{2}(?:_[A-Z]{2})?\z/ =~ entry
193
+ next unless File.directory?(File.join(dir.path, entry))
194
+ locales << entry
195
+ end
196
+ end
197
+ locales
198
+ end
199
+
200
+ def current_scope
201
+ scope = Rake.application.current_scope
202
+ if scope.is_a?(Array)
203
+ scope
204
+ else
205
+ if scope.empty?
206
+ []
207
+ else
208
+ [scope.path]
209
+ end
210
+ end
211
+ end
212
+
213
+ def namespace_recursive(namespace_spec, &block)
214
+ first, rest = namespace_spec.split(/:/, 2)
215
+ namespace first do
216
+ if rest.nil?
217
+ block.call
218
+ else
219
+ namespace_recursive(rest, &block)
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end