gettext 2.3.3 → 2.3.4
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.
- 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
|