gettext 2.3.9 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -0
- data/README.rdoc +28 -23
- data/Rakefile +29 -21
- data/doc/text/news.md +57 -0
- data/gettext.gemspec +1 -1
- data/lib/gettext.rb +13 -29
- data/lib/gettext/cgi.rb +1 -1
- data/lib/gettext/{runtime/class_info.rb → class_info.rb} +1 -1
- data/lib/gettext/{runtime/locale_path.rb → locale_path.rb} +1 -2
- data/lib/gettext/{runtime/mo.rb → mo.rb} +18 -33
- data/lib/gettext/{tools/po.rb → po.rb} +1 -1
- data/lib/gettext/{tools/po_entry.rb → po_entry.rb} +8 -4
- data/lib/gettext/{tools/poparser.rb → po_parser.rb} +41 -44
- data/lib/gettext/{runtime/textdomain.rb → text_domain.rb} +6 -16
- data/lib/gettext/text_domain_group.rb +26 -0
- data/lib/gettext/{runtime/textdomain_manager.rb → text_domain_manager.rb} +40 -40
- data/lib/gettext/tools.rb +1 -182
- data/lib/gettext/tools/msgfmt.rb +1 -1
- data/lib/gettext/tools/msginit.rb +1 -1
- data/lib/gettext/tools/msgmerge.rb +2 -221
- data/lib/gettext/tools/parser/erb.rb +49 -30
- data/lib/gettext/tools/parser/glade.rb +44 -36
- data/lib/gettext/tools/parser/ruby.rb +126 -37
- data/lib/gettext/tools/task.rb +225 -0
- data/lib/gettext/tools/xgettext.rb +25 -28
- data/lib/gettext/version.rb +1 -1
- data/locale/bg/LC_MESSAGES/gettext.mo +0 -0
- data/locale/bs/LC_MESSAGES/gettext.mo +0 -0
- data/locale/ca/LC_MESSAGES/gettext.mo +0 -0
- data/locale/cs/LC_MESSAGES/gettext.mo +0 -0
- data/locale/de/LC_MESSAGES/gettext.mo +0 -0
- data/locale/el/LC_MESSAGES/gettext.mo +0 -0
- data/locale/eo/LC_MESSAGES/gettext.mo +0 -0
- data/locale/es/LC_MESSAGES/gettext.mo +0 -0
- data/locale/et/LC_MESSAGES/gettext.mo +0 -0
- data/locale/fr/LC_MESSAGES/gettext.mo +0 -0
- data/locale/hr/LC_MESSAGES/gettext.mo +0 -0
- data/locale/hu/LC_MESSAGES/gettext.mo +0 -0
- data/locale/it/LC_MESSAGES/gettext.mo +0 -0
- data/locale/ja/LC_MESSAGES/gettext.mo +0 -0
- data/locale/ko/LC_MESSAGES/gettext.mo +0 -0
- data/locale/lv/LC_MESSAGES/gettext.mo +0 -0
- data/locale/nb/LC_MESSAGES/gettext.mo +0 -0
- data/locale/nl/LC_MESSAGES/gettext.mo +0 -0
- data/locale/pt_BR/LC_MESSAGES/gettext.mo +0 -0
- data/locale/ru/LC_MESSAGES/gettext.mo +0 -0
- data/locale/sr/LC_MESSAGES/gettext.mo +0 -0
- data/locale/sv/LC_MESSAGES/gettext.mo +0 -0
- data/locale/uk/LC_MESSAGES/gettext.mo +0 -0
- data/locale/vi/LC_MESSAGES/gettext.mo +0 -0
- data/locale/zh/LC_MESSAGES/gettext.mo +0 -0
- data/locale/zh_TW/LC_MESSAGES/gettext.mo +0 -0
- data/po/gettext.pot +171 -97
- data/samples/hello.rb +3 -2
- data/samples/hello2.rb +2 -1
- data/samples/hello_gtk2.rb +4 -1
- data/samples/hello_gtk_builder.rb +32 -0
- data/samples/hello_gtk_builder.ui +46 -0
- data/samples/hello_noop.rb +2 -1
- data/samples/hello_plural.rb +2 -1
- data/samples/hello_tk.rb +2 -1
- data/src/{poparser.ry → po_parser.ry} +31 -35
- data/test/fixtures/N_.rb +1 -1
- data/test/fixtures/_/double_quote_in_double_quote.rb +32 -0
- data/test/fixtures/_/double_quote_in_single_quote.rb +32 -0
- data/test/fixtures/_/literal_concatenation_with_continuation_line.rb +36 -0
- data/test/fixtures/_/middle_new_line.rb +32 -0
- data/test/fixtures/_/multiple_lines_literal.rb +35 -0
- data/test/fixtures/_/multiple_messages_in_same_line.rb +32 -0
- data/test/fixtures/_/multiple_same_messages.rb +36 -0
- data/{lib/gettext/runtime/mofile.rb → test/fixtures/_/one_new_line.rb} +12 -6
- data/test/fixtures/{multi_textdomain.rb → multi_text_domain.rb} +1 -1
- data/test/fixtures/non_ascii.rb +1 -1
- data/test/fixtures/simple.rb +1 -1
- data/test/fixtures/untranslated.rb +1 -1
- data/test/gettext-test-utils.rb +1 -24
- data/test/po/ja/_.po +63 -13
- data/test/po/ja/non_ascii.po +2 -3
- data/test/po/ja/untranslated.po +1 -1
- data/test/test_class_info.rb +2 -2
- data/test/test_gettext.rb +11 -11
- data/test/test_mo.rb +2 -2
- data/test/test_parser.rb +93 -96
- data/test/test_po_entry.rb +237 -146
- data/test/test_po_parser.rb +107 -98
- data/test/test_string.rb +1 -1
- data/test/{test_textdomain_bind.rb → test_text_domain_bind.rb} +6 -6
- data/test/{test_textdomain_multi.rb → test_text_domain_multi.rb} +5 -5
- data/test/{test_textdomain_toplevel.rb → test_text_domain_toplevel.rb} +1 -1
- data/test/test_thread.rb +1 -1
- data/test/tools/files/simple_translation.rb +1 -1
- data/test/{parser → tools/parser}/test_ruby.rb +110 -14
- data/test/tools/test_msgmerge.rb +17 -276
- data/test/tools/test_po.rb +1 -1
- data/test/tools/test_xgettext.rb +175 -144
- metadata +59 -33
- data/lib/gettext/core_ext/iconv.rb +0 -110
- data/lib/gettext/core_ext/string.rb +0 -91
- data/lib/gettext/parser/erb.rb +0 -5
- data/lib/gettext/parser/glade.rb +0 -5
- data/lib/gettext/parser/ruby.rb +0 -172
- data/lib/gettext/runtime/textdomain_group.rb +0 -26
- data/lib/gettext/task.rb +0 -203
- data/lib/gettext/utils.rb +0 -39
- data/test/test_po_generation.rb +0 -45
- data/test/tools/test_tools.rb +0 -61
@@ -1,8 +1,9 @@
|
|
1
|
-
#
|
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
|
-
|
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
|
26
|
-
|
27
|
-
|
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
|
-
|
31
|
-
def
|
58
|
+
private
|
59
|
+
def parse_source(input) # :nodoc:
|
32
60
|
targets = []
|
33
|
-
cnt = 0
|
34
61
|
target = false
|
35
|
-
|
62
|
+
start_line_no = nil
|
36
63
|
val = nil
|
37
64
|
|
38
|
-
|
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
|
-
|
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,
|
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,
|
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
|
-
|
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 << "#{
|
95
|
+
targets[targets.index(assoc_data)] = assoc_data << "#{@path}:#{line_no}"
|
88
96
|
else
|
89
|
-
targets << [val.gsub(/\n/, '\n'), "#{
|
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
|
15
|
-
require
|
16
|
-
require
|
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/,
|
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,
|
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
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
+
class << self
|
118
|
+
def target?(file) # :nodoc:
|
119
|
+
true # always true, as the default parser.
|
120
|
+
end
|
117
121
|
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
123
|
-
|
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
|
-
|
126
|
-
|
208
|
+
# Extracts messages from @path.
|
209
|
+
#
|
210
|
+
# @return [Array<POEntry>] Extracted messages
|
211
|
+
def parse
|
212
|
+
source = IO.read(@path)
|
127
213
|
|
128
|
-
|
129
|
-
|
214
|
+
encoding = detect_encoding(source) || source.encoding
|
215
|
+
source.force_encoding(encoding)
|
130
216
|
|
131
|
-
|
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
|
229
|
+
def parse_source(source)
|
145
230
|
po = []
|
146
|
-
file = StringIO.new(
|
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,
|
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,
|
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
|
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,
|
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 <<
|
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
|