gettext 2.3.3 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/text/news.md +37 -0
- data/gettext.gemspec +1 -0
- data/lib/gettext/runtime/mo.rb +382 -0
- data/lib/gettext/runtime/mofile.rb +24 -366
- data/lib/gettext/runtime/textdomain.rb +17 -17
- data/lib/gettext/tools.rb +1 -1
- data/lib/gettext/tools/msgfmt.rb +2 -2
- data/lib/gettext/tools/msginit.rb +16 -15
- data/lib/gettext/tools/msgmerge.rb +258 -255
- data/lib/gettext/tools/parser/ruby.rb +24 -24
- data/lib/gettext/tools/po.rb +256 -0
- data/lib/gettext/tools/po_entry.rb +355 -0
- data/lib/gettext/tools/poparser.rb +118 -16
- data/lib/gettext/tools/xgettext.rb +56 -58
- data/lib/gettext/version.rb +1 -1
- data/samples/po/hello.pot +3 -3
- data/samples/po/hello2.pot +3 -3
- data/samples/po/hello_glade2.pot +3 -3
- data/samples/po/hello_gtk2.pot +3 -3
- data/samples/po/hello_noop.pot +3 -3
- data/samples/po/hello_plural.pot +3 -3
- data/samples/po/hello_tk.pot +3 -3
- data/src/poparser.ry +111 -9
- data/test/parser/test_ruby.rb +17 -13
- data/test/po/_.pot +3 -3
- data/test/po/backslash.pot +3 -3
- data/test/po/non_ascii.pot +3 -3
- data/test/po/np_.pot +5 -4
- data/test/po/ns_.pot +3 -3
- data/test/po/p_.pot +3 -3
- data/test/po/s_.pot +3 -3
- data/test/po/untranslated.pot +3 -3
- data/test/{test_mofile.rb → test_mo.rb} +3 -3
- data/test/test_parser.rb +13 -12
- data/test/test_po_entry.rb +329 -0
- data/test/test_po_parser.rb +209 -8
- data/test/tools/test_msginit.rb +0 -2
- data/test/tools/test_msgmerge.rb +427 -50
- data/test/tools/test_po.rb +487 -0
- data/test/tools/test_xgettext.rb +1 -1
- metadata +28 -45
- data/data/locale/de/LC_MESSAGES/gettext.mo +0 -0
- data/data/locale/de/LC_MESSAGES/rgettext.mo +0 -0
- data/data/locale/el/LC_MESSAGES/gettext.mo +0 -0
- data/data/locale/el/LC_MESSAGES/rgettext.mo +0 -0
- data/data/locale/sr/LC_MESSAGES/gettext.mo +0 -0
- data/data/locale/sr/LC_MESSAGES/rgettext.mo +0 -0
- data/data/locale/uk/LC_MESSAGES/gettext.mo +0 -0
- data/data/locale/uk/LC_MESSAGES/rgettext.mo +0 -0
- data/lib/gettext/tools/pomessage.rb +0 -232
- data/samples/locale/bg/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/bs/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/ca/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/cs/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/de/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/el/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/eo/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/es/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/fr/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/hr/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/hu/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/it/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/ja/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/ko/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/lv/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/nb/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/nl/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/pt_BR/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/ru/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/sr/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/sv/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/uk/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/vi/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/zh/LC_MESSAGES/hello_gtk.mo +0 -0
- data/samples/locale/zh_TW/LC_MESSAGES/hello_gtk.mo +0 -0
- data/test/po/ascii.pot +0 -23
- data/test/po/no_exist_msgid.pot +0 -20
- data/test/po/not_existed_msgid.pot +0 -20
- data/test/test_po_message.rb +0 -118
@@ -14,7 +14,7 @@
|
|
14
14
|
=end
|
15
15
|
|
16
16
|
require 'gettext/core_ext/string'
|
17
|
-
require 'gettext/runtime/
|
17
|
+
require 'gettext/runtime/mo'
|
18
18
|
require 'gettext/runtime/locale_path'
|
19
19
|
|
20
20
|
module GetText
|
@@ -72,26 +72,26 @@ module GetText
|
|
72
72
|
|
73
73
|
lang_key = lang.to_s
|
74
74
|
|
75
|
-
|
75
|
+
mo = nil
|
76
76
|
if self.class.cached?
|
77
|
-
|
77
|
+
mo = @mofiles[lang_key]
|
78
78
|
end
|
79
|
-
unless
|
80
|
-
|
79
|
+
unless mo
|
80
|
+
mo = load_mo(lang)
|
81
81
|
end
|
82
82
|
|
83
|
-
if (!
|
83
|
+
if (! mo) or (mo ==:empty)
|
84
84
|
return nil
|
85
85
|
end
|
86
86
|
|
87
|
-
return
|
87
|
+
return mo[msgid] if mo.has_key?(msgid)
|
88
88
|
|
89
89
|
ret = nil
|
90
90
|
if msgid.include?("\000")
|
91
91
|
# Check "aaa\000bbb" and show warning but return the singular part.
|
92
92
|
msgid_single = msgid.split("\000")[0]
|
93
93
|
msgid_single_prefix_re = /^#{Regexp.quote(msgid_single)}\000/
|
94
|
-
|
94
|
+
mo.each do |key, val|
|
95
95
|
if msgid_single_prefix_re =~ key
|
96
96
|
# Usually, this is not caused to make po-files from rgettext.
|
97
97
|
separated_msgid = msgid.gsub(/\000/, '", "')
|
@@ -106,7 +106,7 @@ module GetText
|
|
106
106
|
end
|
107
107
|
else
|
108
108
|
msgid_prefix_re = /^#{Regexp.quote(msgid)}\000/
|
109
|
-
|
109
|
+
mo.each do |key, val|
|
110
110
|
if msgid_prefix_re =~ key
|
111
111
|
ret = val.split("\000")[0]
|
112
112
|
break
|
@@ -132,8 +132,8 @@ module GetText
|
|
132
132
|
ret = nil
|
133
133
|
elsif msg.include?("\000")
|
134
134
|
# [[msgstr[0], msgstr[1], msgstr[2],...], cond]
|
135
|
-
|
136
|
-
cond = (
|
135
|
+
mo = @mofiles[lang.to_posix.to_s]
|
136
|
+
cond = (mo and mo != :empty) ? mo.plural_as_proc : DEFAULT_PLURAL_CALC
|
137
137
|
ret = [msg.split("\000"), cond]
|
138
138
|
else
|
139
139
|
ret = [[msg], DEFAULT_SINGLE_CALC]
|
@@ -160,21 +160,21 @@ module GetText
|
|
160
160
|
lang = lang.to_posix unless lang.kind_of? Locale::Tag::Posix
|
161
161
|
lang_key = lang.to_s
|
162
162
|
|
163
|
-
|
164
|
-
if
|
165
|
-
if
|
163
|
+
mo = @mofiles[lang_key]
|
164
|
+
if mo
|
165
|
+
if mo == :empty
|
166
166
|
return :empty
|
167
167
|
elsif ! self.class.cached?
|
168
|
-
|
168
|
+
mo.update!
|
169
169
|
end
|
170
|
-
return
|
170
|
+
return mo
|
171
171
|
end
|
172
172
|
|
173
173
|
path = @locale_path.current_path(lang)
|
174
174
|
|
175
175
|
if path
|
176
176
|
charset = @output_charset || lang.charset || Locale.charset || "UTF-8"
|
177
|
-
@mofiles[lang_key] =
|
177
|
+
@mofiles[lang_key] = MO.open(path, charset)
|
178
178
|
else
|
179
179
|
@mofiles[lang_key] = :empty
|
180
180
|
end
|
data/lib/gettext/tools.rb
CHANGED
data/lib/gettext/tools/msgfmt.rb
CHANGED
@@ -66,14 +66,14 @@ module GetText
|
|
66
66
|
parse_arguments(*arguments)
|
67
67
|
validate
|
68
68
|
|
69
|
-
parser =
|
69
|
+
parser = POParser.new
|
70
70
|
parser.ignore_fuzzy = false
|
71
71
|
pot = parser.parse_file(@input_file,
|
72
|
-
GetText::
|
72
|
+
GetText::PO.new)
|
73
73
|
po = replace_pot_header(pot)
|
74
74
|
|
75
75
|
File.open(@output_file, "w") do |f|
|
76
|
-
f.puts(po.
|
76
|
+
f.puts(po.to_s)
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -169,15 +169,16 @@ module GetText
|
|
169
169
|
end
|
170
170
|
|
171
171
|
def replace_pot_header(pot) #:nodoc:
|
172
|
-
@entry = pot[""]
|
173
|
-
@comment = pot
|
172
|
+
@entry = pot[""].msgstr
|
173
|
+
@comment = pot[""].translator_comment
|
174
174
|
@translator = translator_info
|
175
175
|
|
176
176
|
replace_entry
|
177
177
|
replace_comment
|
178
178
|
|
179
|
-
pot[""] = @entry
|
180
|
-
pot
|
179
|
+
pot[""] = @entry
|
180
|
+
pot[""].translator_comment = @comment
|
181
|
+
pot[""].flag = pot[""].flag.gsub(/\Afuzzy\z/, "")
|
181
182
|
pot
|
182
183
|
end
|
183
184
|
|
@@ -239,11 +240,11 @@ module GetText
|
|
239
240
|
replace_description
|
240
241
|
replace_first_author
|
241
242
|
replace_copyright_year
|
242
|
-
@comment = @comment.gsub(
|
243
|
+
@comment = @comment.gsub(/^fuzzy$/, "")
|
243
244
|
end
|
244
245
|
|
245
246
|
EMAIL = "EMAIL@ADDRESS"
|
246
|
-
FIRST_AUTHOR_KEY = /^
|
247
|
+
FIRST_AUTHOR_KEY = /^FIRST AUTHOR <#{EMAIL}>, (\d+\.)$/
|
247
248
|
|
248
249
|
def replace_last_translator #:nodoc:
|
249
250
|
unless @translator.nil?
|
@@ -273,9 +274,9 @@ module GetText
|
|
273
274
|
def replace_plural_forms #:nodoc:
|
274
275
|
plural_entry = plural_forms(@language)
|
275
276
|
if PLURAL_FORMS =~ @entry
|
276
|
-
@entry = @entry.gsub(PLURAL_FORMS, "\\1 #{plural_entry}")
|
277
|
+
@entry = @entry.gsub(PLURAL_FORMS, "\\1 #{plural_entry}\n")
|
277
278
|
else
|
278
|
-
@entry << "Plural-Forms: #{plural_entry}"
|
279
|
+
@entry << "Plural-Forms: #{plural_entry}\n"
|
279
280
|
end
|
280
281
|
end
|
281
282
|
|
@@ -329,7 +330,7 @@ module GetText
|
|
329
330
|
"nplurals=#{nplural}; plural=#{plural_expression};"
|
330
331
|
end
|
331
332
|
|
332
|
-
DESCRIPTION_TITLE = /^
|
333
|
+
DESCRIPTION_TITLE = /^SOME DESCRIPTIVE TITLE\.$/
|
333
334
|
|
334
335
|
def replace_description #:nodoc:
|
335
336
|
language_name = Locale::Info.get_language(@language).name
|
@@ -342,18 +343,18 @@ module GetText
|
|
342
343
|
@comment = @comment.gsub(DESCRIPTION_TITLE, "\\1 #{description}")
|
343
344
|
end
|
344
345
|
|
345
|
-
YEAR_KEY = /^(
|
346
|
+
YEAR_KEY = /^(FIRST AUTHOR <#{EMAIL}>,) YEAR\.$/
|
346
347
|
LAST_TRANSLATOR_KEY = /^(Last-Translator:) FULL NAME <#{EMAIL}>$/
|
347
348
|
|
348
349
|
def replace_first_author #:nodoc:
|
349
350
|
@comment = @comment.gsub(YEAR_KEY, "\\1 #{year}.")
|
350
351
|
unless @translator.nil?
|
351
352
|
@comment = @comment.gsub(FIRST_AUTHOR_KEY,
|
352
|
-
"
|
353
|
+
"#{@translator}, \\1")
|
353
354
|
end
|
354
355
|
end
|
355
356
|
|
356
|
-
COPYRIGHT_KEY =
|
357
|
+
COPYRIGHT_KEY = /^(Copyright \(C\)) YEAR (THE PACKAGE'S COPYRIGHT HOLDER)$/
|
357
358
|
def replace_copyright_year #:nodoc:
|
358
359
|
@comment = @comment.gsub(COPYRIGHT_KEY, "\\1 #{year} \\2")
|
359
360
|
end
|
@@ -20,64 +20,110 @@
|
|
20
20
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
21
21
|
|
22
22
|
require "optparse"
|
23
|
+
require "levenshtein"
|
23
24
|
require "gettext"
|
24
25
|
require "gettext/tools/poparser"
|
25
|
-
require "gettext/tools/
|
26
|
-
|
27
|
-
# TODO: MsgMerge should use PoMessage to generate PO content.
|
26
|
+
require "gettext/tools/po"
|
28
27
|
|
29
28
|
module GetText
|
30
29
|
module Tools
|
31
30
|
class MsgMerge
|
32
31
|
class PoData #:nodoc:
|
33
32
|
|
34
|
-
attr_reader :
|
33
|
+
attr_reader :po
|
35
34
|
|
36
35
|
def initialize
|
37
|
-
@
|
38
|
-
@msgid2comment = {}
|
39
|
-
@msgids = []
|
36
|
+
@po = PO.new
|
40
37
|
end
|
41
38
|
|
42
|
-
def set_comment(
|
43
|
-
|
39
|
+
def set_comment(msgid, comments, msgctxt=nil)
|
40
|
+
entry = generate_entry(msgid)
|
41
|
+
|
42
|
+
if msgid == :last
|
43
|
+
entry.comment = comments
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
comments.each_line do |_line|
|
48
|
+
line = _line.chomp
|
49
|
+
entry = parse_comment(line, entry)
|
50
|
+
end
|
44
51
|
end
|
45
52
|
|
46
53
|
def msgstr(msgid)
|
47
|
-
|
54
|
+
self[msgid]
|
48
55
|
end
|
49
56
|
|
50
57
|
def comment(msgid)
|
51
|
-
|
58
|
+
msgctxt, msgid, _ = split_msgid(msgid)
|
59
|
+
id = [msgctxt, msgid]
|
60
|
+
entry = @po[*id]
|
61
|
+
return nil if entry.nil?
|
62
|
+
|
63
|
+
formatted_comments = entry.format_translator_comment
|
64
|
+
formatted_comments << entry.format_extracted_comment
|
65
|
+
formatted_comments << entry.format_reference_comment
|
66
|
+
formatted_comments << entry.format_flag_comment
|
67
|
+
formatted_comments << entry.format_previous_comment
|
68
|
+
|
69
|
+
unless entry.comment.nil?
|
70
|
+
formatted_comments = entry.format_comment("#", entry.comment)
|
71
|
+
end
|
72
|
+
|
73
|
+
formatted_comments.chomp
|
52
74
|
end
|
53
75
|
|
54
76
|
def [](msgid)
|
55
|
-
|
77
|
+
msgctxt, msgid, _ = split_msgid(msgid)
|
78
|
+
@po[msgctxt, msgid].msgstr
|
56
79
|
end
|
57
80
|
|
58
|
-
def []=(msgid,
|
59
|
-
|
60
|
-
|
61
|
-
|
81
|
+
def []=(msgid, value)
|
82
|
+
msgctxt, msgid, msgid_plural = split_msgid(msgid)
|
83
|
+
id = [msgctxt, msgid]
|
84
|
+
|
85
|
+
if value.instance_of?(POEntry)
|
86
|
+
@po[*id] = value
|
87
|
+
return value
|
62
88
|
end
|
63
89
|
|
64
|
-
|
90
|
+
msgstr = value
|
91
|
+
if @po.has_key?(*id)
|
92
|
+
@po[*id] = msgstr
|
93
|
+
@po[*id].msgctxt = msgctxt
|
94
|
+
@po[*id].msgid_plural = msgid_plural
|
95
|
+
else
|
96
|
+
type = detect_entry_type(msgctxt, msgid_plural)
|
97
|
+
entry = POEntry.new(type)
|
98
|
+
entry.msgctxt = msgctxt
|
99
|
+
entry.msgid = msgid
|
100
|
+
entry.msgid_plural = msgid_plural
|
101
|
+
entry.msgstr = msgstr
|
102
|
+
@po[*id] = entry
|
103
|
+
entry
|
104
|
+
end
|
65
105
|
end
|
66
106
|
|
67
107
|
def each_msgid
|
68
|
-
msgids
|
69
|
-
|
108
|
+
msgids.each do |id|
|
109
|
+
next if id.kind_of?(Symbol) or id.empty?
|
110
|
+
yield(id)
|
70
111
|
end
|
112
|
+
end
|
71
113
|
|
72
|
-
|
73
|
-
|
114
|
+
def msgids
|
115
|
+
@po.collect do |entry|
|
116
|
+
msgctxt = entry.msgctxt
|
117
|
+
msgid = entry.msgid
|
118
|
+
generate_original_string(msgctxt, msgid)
|
74
119
|
end
|
75
120
|
end
|
76
121
|
|
77
122
|
def msgid?(msgid)
|
78
123
|
return false if msgid.kind_of?(Symbol)
|
79
124
|
return true if msgid.empty?
|
80
|
-
|
125
|
+
msgctxt, msgid, _ = split_msgid(msgid)
|
126
|
+
@po.has_key?(msgctxt, msgid)
|
81
127
|
end
|
82
128
|
|
83
129
|
# Is it necessary to implement this method?
|
@@ -86,9 +132,9 @@ module GetText
|
|
86
132
|
end
|
87
133
|
|
88
134
|
def nplurals
|
89
|
-
return 0 if @
|
135
|
+
return 0 if @po[""].msgstr.nil?
|
90
136
|
|
91
|
-
if /\s*nplurals\s*=\s*(\d+)/ =~ @
|
137
|
+
if /\s*nplurals\s*=\s*(\d+)/ =~ @po[""].msgstr
|
92
138
|
return $1.to_i
|
93
139
|
else
|
94
140
|
return 0
|
@@ -96,81 +142,12 @@ module GetText
|
|
96
142
|
end
|
97
143
|
|
98
144
|
def generate_po
|
99
|
-
|
100
|
-
str << generate_po_header
|
101
|
-
str << "\n"
|
102
|
-
|
103
|
-
po_entries = []
|
104
|
-
self.each_msgid do |id|
|
105
|
-
po_entries << self.generate_po_entry(id)
|
106
|
-
end
|
107
|
-
|
108
|
-
unless @msgid2comment[:last].nil?
|
109
|
-
po_entries << "#{@msgid2comment[:last]}\n"
|
110
|
-
end
|
111
|
-
|
112
|
-
str << po_entries.join("\n")
|
113
|
-
str
|
114
|
-
end
|
115
|
-
|
116
|
-
def generate_po_header
|
117
|
-
str = ""
|
118
|
-
|
119
|
-
str << @msgid2comment[""].strip << "\n"
|
120
|
-
str << 'msgid ""' << "\n"
|
121
|
-
str << 'msgstr ""' << "\n"
|
122
|
-
msgstr = @msgid2msgstr[""].gsub(/"/, '\"').gsub(/\r/, "")
|
123
|
-
msgstr = msgstr.gsub(/^(.*)$/, '"\1\n"')
|
124
|
-
str << msgstr.chomp
|
125
|
-
str << "\n"
|
126
|
-
|
127
|
-
str
|
145
|
+
@po.to_s
|
128
146
|
end
|
129
147
|
|
130
148
|
def generate_po_entry(msgid)
|
131
|
-
|
132
|
-
|
133
|
-
if str[-1] != "\n"[0]
|
134
|
-
str << "\n"
|
135
|
-
end
|
136
|
-
|
137
|
-
id = msgid.gsub(/\r/, "")
|
138
|
-
msgstr = @msgid2msgstr[msgid]
|
139
|
-
if msgstr.nil?
|
140
|
-
msgstr = ""
|
141
|
-
else
|
142
|
-
msgstr = msgstr.gsub(/\r/, "")
|
143
|
-
end
|
144
|
-
|
145
|
-
if id.include?("\004")
|
146
|
-
ids = id.split(/\004/)
|
147
|
-
context = ids[0]
|
148
|
-
id = ids[1]
|
149
|
-
str << "msgctxt " << __conv(context) << "\n"
|
150
|
-
end
|
151
|
-
|
152
|
-
if id.include?("\000")
|
153
|
-
ids = id.split(/\000/)
|
154
|
-
str << "msgid " << __conv(ids[0]) << "\n"
|
155
|
-
ids[1..-1].each do |single_id|
|
156
|
-
str << "msgid_plural " << __conv(single_id) << "\n"
|
157
|
-
end
|
158
|
-
|
159
|
-
if msgstr.empty?
|
160
|
-
nplurals.times do |id_count|
|
161
|
-
str << "msgstr[#{id_count}] " << '""' << "\n"
|
162
|
-
end
|
163
|
-
else
|
164
|
-
msgstr.split("\000", -1).each_with_index do |m, n|
|
165
|
-
str << "msgstr[#{n}] " << __conv(m) << "\n"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
else
|
169
|
-
str << "msgid " << __conv(id) << "\n"
|
170
|
-
str << "msgstr " << __conv(msgstr) << "\n"
|
171
|
-
end
|
172
|
-
|
173
|
-
str
|
149
|
+
msgctxt, msgid, _ = split_msgid(msgid)
|
150
|
+
@po[msgctxt, msgid].to_s
|
174
151
|
end
|
175
152
|
|
176
153
|
def __conv(str)
|
@@ -189,209 +166,235 @@ module GetText
|
|
189
166
|
end
|
190
167
|
|
191
168
|
def escape(string)
|
192
|
-
|
169
|
+
POEntry.escape(string)
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
def split_msgid(msgid)
|
174
|
+
return [nil, msgid, nil] if msgid == :last
|
175
|
+
return [nil, "", nil] if msgid.empty?
|
176
|
+
msgctxt, msgid = msgid.split("\004", 2)
|
177
|
+
if msgid.nil?
|
178
|
+
msgid = msgctxt
|
179
|
+
msgctxt = nil
|
180
|
+
end
|
181
|
+
msgid, msgid_plural = msgid.split("\000", 2)
|
182
|
+
[msgctxt, msgid, msgid_plural]
|
183
|
+
end
|
184
|
+
|
185
|
+
def generate_original_string(msgctxt, msgid)
|
186
|
+
return msgid if msgid == :last
|
187
|
+
original_string = ""
|
188
|
+
msgid_plural = @po[msgctxt, msgid].msgid_plural
|
189
|
+
original_string << "#{msgctxt}\004" unless msgctxt.nil?
|
190
|
+
original_string << msgid
|
191
|
+
original_string << "\000#{msgid_plural}" unless msgid_plural.nil?
|
192
|
+
original_string
|
193
|
+
end
|
194
|
+
|
195
|
+
def detect_entry_type(msgctxt, msgid_plural)
|
196
|
+
if msgctxt.nil?
|
197
|
+
if msgid_plural.nil?
|
198
|
+
:normal
|
199
|
+
else
|
200
|
+
:plural
|
201
|
+
end
|
202
|
+
else
|
203
|
+
if msgid_plural.nil?
|
204
|
+
:msgctxt
|
205
|
+
else
|
206
|
+
:msgctxt_plural
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def generate_entry(msgid)
|
212
|
+
msgctxt, msgid, _ = split_msgid(msgid)
|
213
|
+
id = [msgctxt, msgid]
|
214
|
+
@po[*id] = nil unless @po.has_key?(*id)
|
215
|
+
entry = @po[*id]
|
216
|
+
|
217
|
+
entry.translator_comment = ""
|
218
|
+
entry.extracted_comment = ""
|
219
|
+
entry.references = []
|
220
|
+
entry.flag = ""
|
221
|
+
entry.previous = ""
|
222
|
+
entry
|
223
|
+
end
|
224
|
+
|
225
|
+
def parse_comment(line, entry)
|
226
|
+
if line == "#"
|
227
|
+
entry.translator_comment << ""
|
228
|
+
elsif /\A(#.)\s*(.*)\z/ =~ line
|
229
|
+
mark = $1
|
230
|
+
content = $2
|
231
|
+
case mark
|
232
|
+
when POParser::TRANSLATOR_COMMENT_MARK
|
233
|
+
entry.translator_comment << "#{content}\n"
|
234
|
+
when POParser::EXTRACTED_COMMENT_MARK
|
235
|
+
entry.extracted_comment << "#{content}\n"
|
236
|
+
when POParser::REFERENCE_COMMENT_MARK
|
237
|
+
entry.references << content
|
238
|
+
when POParser::FLAG_MARK
|
239
|
+
entry.flag << "#{content}\n"
|
240
|
+
when POParser::PREVIOUS_MSGID_COMMENT_MARK
|
241
|
+
entry.previous << "#{content}\n"
|
242
|
+
else
|
243
|
+
entry.comment << line
|
244
|
+
end
|
245
|
+
end
|
246
|
+
entry
|
193
247
|
end
|
194
248
|
end
|
195
249
|
|
196
250
|
class Merger #:nodoc:
|
197
|
-
# From GNU gettext source.
|
198
|
-
#
|
199
251
|
# Merge the reference with the definition: take the #. and
|
200
252
|
# #: comments from the reference, take the # comments from
|
201
253
|
# the definition, take the msgstr from the definition. Add
|
202
254
|
# this merged entry to the output message list.
|
203
255
|
|
204
|
-
DOT_COMMENT_RE = /\A#\./
|
205
|
-
SEMICOLON_COMMENT_RE = /\A#\:/
|
206
|
-
FUZZY_RE = /\A#\,/
|
207
|
-
NOT_SPECIAL_COMMENT_RE = /\A#([^:.,]|\z)/
|
208
|
-
|
209
|
-
CRLF_RE = /\r?\n/
|
210
256
|
POT_DATE_EXTRACT_RE = /POT-Creation-Date:\s*(.*)?\s*$/
|
211
257
|
POT_DATE_RE = /POT-Creation-Date:.*?$/
|
212
258
|
|
213
259
|
def merge(definition, reference)
|
214
|
-
|
215
|
-
msgstr = definition[msgid] || ""
|
216
|
-
definition[msgid] = msgstr
|
217
|
-
end
|
260
|
+
result = GetText::PO.new
|
218
261
|
|
219
|
-
reference.
|
220
|
-
|
221
|
-
|
222
|
-
|
262
|
+
reference.each do |entry|
|
263
|
+
msgid = entry.msgid
|
264
|
+
msgctxt = entry.msgctxt
|
265
|
+
id = [msgctxt, msgid]
|
223
266
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
used = []
|
228
|
-
merge_header(result, definition)
|
229
|
-
|
230
|
-
result.each_msgid do |msgid|
|
231
|
-
if definition.msgid?(msgid)
|
232
|
-
used << msgid
|
233
|
-
merge_message(msgid, result, msgid, definition)
|
234
|
-
elsif other_msgid = definition.search_msgid_fuzzy(msgid, used)
|
235
|
-
used << other_msgid
|
236
|
-
merge_fuzzy_message(msgid, result, other_msgid, definition)
|
237
|
-
elsif msgid.index("\000") and (reference.msgstr(msgid).gsub("\000", "").empty?)
|
238
|
-
# plural
|
239
|
-
result[msgid] = ([""] * definition.nplurals).join("\000")
|
240
|
-
else
|
241
|
-
change_reference_comment(msgid, result)
|
267
|
+
if definition.has_key?(*id)
|
268
|
+
result[*id] = merge_entry(definition[*id], entry)
|
269
|
+
next
|
242
270
|
end
|
243
|
-
end
|
244
271
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
last_comment << "\n"
|
252
|
-
last_comment << definition.generate_po_entry(msgid).strip.gsub(/^/, "#. ")
|
253
|
-
last_comment << "\n"
|
272
|
+
if msgctxt.nil?
|
273
|
+
same_msgid_entry = find_by_msgid(definition, msgid)
|
274
|
+
if not same_msgid_entry.nil? and not same_msgid_entry.msgctxt.nil?
|
275
|
+
result[nil, msgid] = merge_fuzzy_entry(same_msgid_entry, entry)
|
276
|
+
next
|
277
|
+
end
|
254
278
|
end
|
279
|
+
|
280
|
+
fuzzy_entry = find_fuzzy_entry(definition, msgid, msgctxt)
|
281
|
+
unless fuzzy_entry.nil?
|
282
|
+
result[*id] = merge_fuzzy_entry(fuzzy_entry, entry)
|
283
|
+
next
|
284
|
+
end
|
285
|
+
|
286
|
+
result[*id] = entry
|
255
287
|
end
|
256
|
-
result.set_comment(:last, last_comment) unless last_comment.empty?
|
257
288
|
|
289
|
+
add_obsolete_entry(result, definition)
|
258
290
|
result
|
259
291
|
end
|
260
292
|
|
261
|
-
def
|
262
|
-
|
293
|
+
def merge_entry(definition_entry, reference_entry)
|
294
|
+
if definition_entry.msgid.empty? and definition_entry.msgctxt.nil?
|
295
|
+
new_header = merge_header(definition_entry, reference_entry)
|
296
|
+
return new_header
|
297
|
+
end
|
263
298
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
if msgid.index("\000")
|
269
|
-
if def_msgstr.index("\000")
|
270
|
-
# OK
|
271
|
-
target[msgid] = def_msgstr
|
272
|
-
else
|
273
|
-
# NG
|
274
|
-
strings = []
|
275
|
-
definition.nplurals.times do
|
276
|
-
strings << def_msgstr
|
277
|
-
end
|
278
|
-
target[msgid] = strings.join("\000")
|
279
|
-
end
|
280
|
-
else
|
281
|
-
if def_msgstr.index("\000")
|
282
|
-
# NG
|
283
|
-
target[msgid] = def_msgstr.split("\000")[0]
|
284
|
-
else
|
285
|
-
# OK
|
286
|
-
target[msgid] = def_msgstr
|
287
|
-
end
|
299
|
+
if definition_entry.flag == "fuzzy"
|
300
|
+
entry = definition_entry
|
301
|
+
entry.flag = "fuzzy"
|
302
|
+
return entry
|
288
303
|
end
|
289
|
-
end
|
290
304
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
end
|
305
|
+
entry = reference_entry
|
306
|
+
entry.translator_comment = definition_entry.translator_comment
|
307
|
+
entry.previous = nil
|
295
308
|
|
296
|
-
|
297
|
-
|
298
|
-
|
309
|
+
unless definition_entry.msgid_plural == reference_entry.msgid_plural
|
310
|
+
entry.flag = "fuzzy"
|
311
|
+
end
|
299
312
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
is_fuzzy = false
|
313
|
+
entry.msgstr = definition_entry.msgstr
|
314
|
+
entry
|
315
|
+
end
|
304
316
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
317
|
+
def merge_header(old_header, new_header)
|
318
|
+
header = old_header
|
319
|
+
if POT_DATE_EXTRACT_RE =~ new_header.msgstr
|
320
|
+
create_date = $1
|
321
|
+
pot_creation_date = "POT-Creation-Date: #{create_date}"
|
322
|
+
header.msgstr = header.msgstr.gsub(POT_DATE_RE, pot_creation_date)
|
309
323
|
end
|
324
|
+
header.flag = nil
|
325
|
+
header
|
326
|
+
end
|
310
327
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
elsif SEMICOLON_COMMENT_RE =~ l
|
315
|
-
semi_comment << l
|
316
|
-
elsif FUZZY_RE =~ l
|
317
|
-
is_fuzzy = true if msgid != ""
|
318
|
-
end
|
328
|
+
def find_by_msgid(entries, msgid)
|
329
|
+
same_msgid_entries = entries.find_all do |entry|
|
330
|
+
entry.msgid == msgid
|
319
331
|
end
|
332
|
+
same_msgid_entries = same_msgid_entries.sort_by do |entry|
|
333
|
+
entry.msgctxt
|
334
|
+
end
|
335
|
+
same_msgid_entries.first
|
336
|
+
end
|
320
337
|
|
321
|
-
|
322
|
-
|
338
|
+
def merge_fuzzy_entry(fuzzy_entry, entry)
|
339
|
+
merged_entry = merge_entry(fuzzy_entry, entry)
|
340
|
+
merged_entry.flag = "fuzzy"
|
341
|
+
merged_entry
|
323
342
|
end
|
324
343
|
|
325
|
-
|
326
|
-
normal_comment = []
|
327
|
-
dot_comment = []
|
328
|
-
semi_comment = []
|
329
|
-
is_fuzzy = false
|
344
|
+
MAX_FUZZY_DISTANCE = 0.5 # XXX: make sure that its value is proper.
|
330
345
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
346
|
+
def find_fuzzy_entry(definition, msgid, msgctxt)
|
347
|
+
min_distance_entry = nil
|
348
|
+
min_distance = MAX_FUZZY_DISTANCE
|
349
|
+
|
350
|
+
same_msgctxt_entries = definition.find_all do |entry|
|
351
|
+
entry.msgctxt == msgctxt
|
352
|
+
end
|
353
|
+
same_msgctxt_entries.each do |entry|
|
354
|
+
distance = Levenshtein.normalized_distance(entry.msgid, msgid)
|
355
|
+
if min_distance > distance
|
356
|
+
min_distance = distance
|
357
|
+
min_distance_entry = entry
|
340
358
|
end
|
341
359
|
end
|
342
360
|
|
343
|
-
|
344
|
-
podata.set_comment(msgid, str)
|
361
|
+
min_distance_entry
|
345
362
|
end
|
346
363
|
|
347
|
-
def
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
if $1 == ""
|
352
|
-
"# "
|
353
|
-
else
|
354
|
-
sss
|
355
|
-
end
|
356
|
-
end
|
357
|
-
if normal_comment.size > 0
|
358
|
-
str << "\n"
|
364
|
+
def add_obsolete_entry(result, definition)
|
365
|
+
obsolete_entry = generate_obsolete_entry(result, definition)
|
366
|
+
unless obsolete_entry.nil?
|
367
|
+
result[:last] = obsolete_entry
|
359
368
|
end
|
369
|
+
result
|
370
|
+
end
|
360
371
|
|
361
|
-
|
362
|
-
|
363
|
-
"#. "
|
364
|
-
else
|
365
|
-
sss
|
366
|
-
end
|
367
|
-
end
|
368
|
-
if dot_comment.size > 0
|
369
|
-
str << "\n"
|
370
|
-
end
|
372
|
+
def generate_obsolete_entry(result, definition)
|
373
|
+
obsolete_entry = nil
|
371
374
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
end
|
375
|
+
obsolete_entries = extract_obsolete_entries(result, definition)
|
376
|
+
unless obsolete_entries.empty?
|
377
|
+
obsolete_comment = ""
|
376
378
|
|
377
|
-
|
378
|
-
|
379
|
+
obsolete_entries.each do |entry|
|
380
|
+
obsolete_comment << entry.to_s
|
381
|
+
end
|
382
|
+
obsolete_entry = POEntry.new(:normal)
|
383
|
+
obsolete_entry.msgid = :last
|
384
|
+
obsolete_entry.comment = obsolete_comment
|
379
385
|
end
|
380
|
-
|
381
|
-
str
|
386
|
+
obsolete_entry
|
382
387
|
end
|
383
388
|
|
384
|
-
def
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
def_msg = def_msg.sub(POT_DATE_RE, "POT-Creation-Date: #{time}")
|
389
|
+
def extract_obsolete_entries(result, definition)
|
390
|
+
obsolete_entries = []
|
391
|
+
definition.each do |entry|
|
392
|
+
id = [entry.msgctxt, entry.msgid]
|
393
|
+
unless result.has_key?(*id)
|
394
|
+
obsolete_entries << entry
|
395
|
+
end
|
392
396
|
end
|
393
|
-
|
394
|
-
target[""] = def_msg
|
397
|
+
obsolete_entries
|
395
398
|
end
|
396
399
|
end
|
397
400
|
|
@@ -500,10 +503,10 @@ module GetText
|
|
500
503
|
def run(*options) #:nodoc:
|
501
504
|
config = check_command_line_options(*options)
|
502
505
|
|
503
|
-
parser =
|
506
|
+
parser = POParser.new
|
504
507
|
parser.ignore_fuzzy = false
|
505
|
-
defpo = parser.parse_file(config.defpo,
|
506
|
-
refpot = parser.parse_file(config.refpot,
|
508
|
+
defpo = parser.parse_file(config.defpo, PO.new)
|
509
|
+
refpot = parser.parse_file(config.refpot, PO.new)
|
507
510
|
|
508
511
|
merger = Merger.new
|
509
512
|
result = merger.merge(defpo, refpot)
|
@@ -512,10 +515,10 @@ module GetText
|
|
512
515
|
|
513
516
|
if config.output.is_a?(String)
|
514
517
|
File.open(File.expand_path(config.output), "w+") do |file|
|
515
|
-
file.write(result.
|
518
|
+
file.write(result.to_s)
|
516
519
|
end
|
517
520
|
else
|
518
|
-
puts(result.
|
521
|
+
puts(result.to_s)
|
519
522
|
end
|
520
523
|
end
|
521
524
|
end
|