ofm_gettext 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/COPYING +56 -0
  2. data/ChangeLog-1 +2016 -0
  3. data/NEWS-1 +414 -0
  4. data/gettext.gemspec +7 -8
  5. data/lib/gettext/runtime/class_info.rb +69 -0
  6. data/lib/gettext/runtime/locale_path.rb +123 -0
  7. data/lib/gettext/runtime/mofile.rb +332 -0
  8. data/lib/gettext/runtime/textdomain.rb +179 -0
  9. data/lib/gettext/runtime/textdomain_group.rb +26 -0
  10. data/lib/gettext/runtime/textdomain_manager.rb +214 -0
  11. data/lib/gettext/tools/parser/erb.rb +54 -0
  12. data/lib/gettext/tools/parser/glade.rb +100 -0
  13. data/lib/gettext/tools/parser/ruby.rb +226 -0
  14. data/lib/gettext/tools/pomessage.rb +199 -0
  15. data/lib/gettext/tools/poparser.rb +358 -0
  16. data/po/uk/rgettext.po +143 -0
  17. data/samples/cgi/po/uk/helloerb1.po +62 -0
  18. data/samples/cgi/po/uk/helloerb2.po +54 -0
  19. data/samples/cgi/po/uk/hellolib.po +26 -0
  20. data/samples/cgi/po/uk/main.po +84 -0
  21. data/samples/po/uk/hello.po +22 -0
  22. data/samples/po/uk/hello2.po +30 -0
  23. data/samples/po/uk/hello_glade2.po +34 -0
  24. data/samples/po/uk/hello_gtk.po +22 -0
  25. data/samples/po/uk/hello_noop.po +26 -0
  26. data/samples/po/uk/hello_plural.po +29 -0
  27. data/samples/po/uk/hello_tk.po +26 -0
  28. data/test/po/ja/test3.po +19 -0
  29. data/test/po/li/plural_error.po +27 -0
  30. data/test/test_locale_path.rb +76 -0
  31. data/test/test_po_generation.rb +22 -0
  32. data/test/test_pomessage.rb +101 -0
  33. data/test/test_textdomain_bind.rb +39 -0
  34. data/test/test_thread.rb +43 -0
  35. data/test/tools/files/app.pot +0 -0
  36. data/test/tools/files/de/app.po +0 -0
  37. data/test/tools/files/en/app.po +0 -0
  38. data/test/tools/files/en/test.po +21 -0
  39. data/test/tools/files/simple_1.po +2 -0
  40. data/test/tools/files/simple_2.po +2 -0
  41. data/test/tools/files/simple_translation.rb +3 -0
  42. data/test/tools/files/version.po +7 -0
  43. data/test/tools/test.pot +21 -0
  44. data/test/tools/test_tools.rb +63 -0
  45. metadata +380 -335
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ parser/glade.rb - parser for Glade-2
5
+
6
+ Copyright (C) 2004,2005 Masao Mutoh
7
+
8
+ You may redistribute it and/or modify it under the same
9
+ license terms as Ruby or LGPL.
10
+ =end
11
+
12
+ require 'cgi'
13
+ require 'gettext'
14
+
15
+ module GetText
16
+ module GladeParser
17
+ extend GetText
18
+ extend self
19
+
20
+ bindtextdomain("rgettext")
21
+
22
+ TARGET1 = /<property.*translatable="yes">(.*)/
23
+ TARGET2 = /(.*)<\/property>/
24
+
25
+ def parse(file, targets = []) # :nodoc:
26
+ lines = IO.readlines(file)
27
+ parse_lines(file, lines, targets)
28
+ end
29
+
30
+ #from ary of lines.
31
+ def parse_lines(file, lines, targets) # :nodoc:
32
+ cnt = 0
33
+ target = false
34
+ line_no = 0
35
+ val = nil
36
+
37
+ loop do
38
+ line = lines.shift
39
+ break unless line
40
+
41
+ cnt += 1
42
+ if TARGET1 =~ line
43
+ line_no = cnt
44
+ val = $1 + "\n"
45
+ target = true
46
+ if TARGET2 =~ $1
47
+ val = $1
48
+ add_target(val, file, line_no, targets)
49
+ val = nil
50
+ target = false
51
+ end
52
+ elsif target
53
+ if TARGET2 =~ line
54
+ val << $1
55
+ add_target(val, file, line_no, targets)
56
+ val = nil
57
+ target = false
58
+ else
59
+ val << line
60
+ end
61
+ end
62
+ end
63
+ targets
64
+ end
65
+
66
+ XML_RE = /<\?xml/
67
+ GLADE_RE = /glade-2.0.dtd/
68
+
69
+ def target?(file) # :nodoc:
70
+ data = IO.readlines(file)
71
+ if XML_RE =~ data[0] and GLADE_RE =~ data[1]
72
+ true
73
+ else
74
+ if File.extname(file) == '.glade'
75
+ raise _("`%{file}' is not glade-2.0 format.") % {:file => file}
76
+ end
77
+ false
78
+ end
79
+ end
80
+
81
+ def add_target(val, file, line_no, targets) # :nodoc:
82
+ return unless val.size > 0
83
+ assoc_data = targets.assoc(val)
84
+ val = CGI.unescapeHTML(val)
85
+ if assoc_data
86
+ targets[targets.index(assoc_data)] = assoc_data << "#{file}:#{line_no}"
87
+ else
88
+ targets << [val.gsub(/\n/, '\n'), "#{file}:#{line_no}"]
89
+ end
90
+ targets
91
+ end
92
+ end
93
+ end
94
+
95
+ if __FILE__ == $0
96
+ # ex) ruby glade.rb foo.glade bar.glade
97
+ ARGV.each do |file|
98
+ p GetText::GladeParser.parse(file)
99
+ end
100
+ end
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/ruby
2
+ =begin
3
+ parser/ruby.rb - parser for ruby script
4
+
5
+ Copyright (C) 2003-2009 Masao Mutoh
6
+ Copyright (C) 2005 speakillof
7
+ Copyright (C) 2001,2002 Yasushi Shoji, Masao Mutoh
8
+
9
+ You may redistribute it and/or modify it under the same
10
+ license terms as Ruby or LGPL.
11
+
12
+ =end
13
+
14
+ require 'irb/ruby-lex.rb'
15
+ require 'stringio'
16
+ require 'gettext/tools/pomessage'
17
+
18
+ module GetText
19
+ class RubyLexX < RubyLex # :nodoc: all
20
+ # Parser#parse resemlbes RubyLex#lex
21
+ def parse
22
+ until ( (tk = token).kind_of?(RubyToken::TkEND_OF_SCRIPT) && !@continue or tk.nil? )
23
+ s = get_readed
24
+ if RubyToken::TkSTRING === tk
25
+ def tk.value
26
+ @value
27
+ end
28
+
29
+ def tk.value=(s)
30
+ @value = s
31
+ end
32
+
33
+ if @here_header
34
+ s = s.sub(/\A.*?\n/, '').sub(/^.*\n\Z/, '')
35
+ else
36
+ begin
37
+ s = eval(s)
38
+ rescue Exception
39
+ # Do nothing.
40
+ end
41
+ end
42
+
43
+ tk.value = s
44
+ end
45
+
46
+ if $DEBUG
47
+ if tk.is_a? TkSTRING
48
+ $stderr.puts("#{tk}: #{tk.value}")
49
+ elsif tk.is_a? TkIDENTIFIER
50
+ $stderr.puts("#{tk}: #{tk.name}")
51
+ else
52
+ $stderr.puts(tk)
53
+ end
54
+ end
55
+
56
+ yield tk
57
+ end
58
+ return nil
59
+ end
60
+
61
+ # Original parser does not keep the content of the comments,
62
+ # so monkey patching this with new token type and extended
63
+ # identify_comment implementation
64
+ RubyToken.def_token :TkCOMMENT_WITH_CONTENT, TkVal
65
+
66
+ def identify_comment
67
+ @ltype = "#"
68
+ get_readed # skip the hash sign itself
69
+
70
+ while ch = getc
71
+ if ch == "\n"
72
+ @ltype = nil
73
+ ungetc
74
+ break
75
+ end
76
+ end
77
+ return Token(TkCOMMENT_WITH_CONTENT, get_readed)
78
+ end
79
+
80
+ end
81
+
82
+ # Extends PoMessage for RubyParser.
83
+ # Implements a sort of state machine to assist the parser.
84
+ module PoMessageForRubyParser
85
+ # Supports parsing by setting attributes by and by.
86
+ def set_current_attribute(str)
87
+ param = @param_type[@param_number]
88
+ raise ParseError, 'no more string parameters expected' unless param
89
+ set_value(param, str)
90
+ end
91
+
92
+ def init_param
93
+ @param_number = 0
94
+ self
95
+ end
96
+
97
+ def advance_to_next_attribute
98
+ @param_number += 1
99
+ end
100
+ end
101
+ class PoMessage
102
+ include PoMessageForRubyParser
103
+ alias :initialize_old :initialize
104
+ def initialize(type)
105
+ initialize_old(type)
106
+ init_param
107
+ end
108
+ end
109
+
110
+ module RubyParser
111
+ extend self
112
+
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
+
118
+ # (Since 2.1.0) the 2nd parameter is deprecated
119
+ # (and ignored here).
120
+ # And You don't need to keep the pomessages as unique.
121
+
122
+ def parse(path, deprecated = []) # :nodoc:
123
+ lines = IO.readlines(path)
124
+ parse_lines(path, lines, deprecated)
125
+ end
126
+
127
+ def parse_lines(path, lines, deprecated = []) # :nodoc:
128
+ pomessages = deprecated
129
+ file = StringIO.new(lines.join + "\n")
130
+ rl = RubyLexX.new
131
+ rl.set_input(file)
132
+ rl.skip_space = true
133
+ #rl.readed_auto_clean_up = true
134
+
135
+ pomessage = nil
136
+ line_no = nil
137
+ last_comment = ''
138
+ reset_comment = false
139
+ rl.parse do |tk|
140
+ begin
141
+ case tk
142
+ when RubyToken::TkIDENTIFIER, RubyToken::TkCONSTANT
143
+ store_pomessage(pomessages, pomessage, path, line_no, last_comment)
144
+ if ID.include?(tk.name)
145
+ pomessage = PoMessage.new(:normal)
146
+ elsif PLURAL_ID.include?(tk.name)
147
+ pomessage = PoMessage.new(:plural)
148
+ elsif MSGCTXT_ID.include?(tk.name)
149
+ pomessage = PoMessage.new(:msgctxt)
150
+ elsif MSGCTXT_PLURAL_ID.include?(tk.name)
151
+ pomessage = PoMessage.new(:msgctxt_plural)
152
+ else
153
+ pomessage = nil
154
+ end
155
+ line_no = tk.line_no.to_s
156
+ when RubyToken::TkSTRING
157
+ pomessage.set_current_attribute tk.value if pomessage
158
+ when RubyToken::TkPLUS, RubyToken::TkNL
159
+ #do nothing
160
+ when RubyToken::TkCOMMA
161
+ pomessage.advance_to_next_attribute if pomessage
162
+ else
163
+ if store_pomessage(pomessages, pomessage, path, line_no, last_comment)
164
+ pomessage = nil
165
+ end
166
+ end
167
+ rescue
168
+ $stderr.print "\n\nError"
169
+ $stderr.print " parsing #{path}:#{tk.line_no}\n\t #{lines[tk.line_no - 1]}" if tk
170
+ $stderr.print "\n #{$!.inspect} in\n"
171
+ $stderr.print $!.backtrace.join("\n")
172
+ $stderr.print "\n"
173
+ exit 1
174
+ end
175
+
176
+ case tk
177
+ when RubyToken::TkCOMMENT_WITH_CONTENT
178
+ last_comment = "" if reset_comment
179
+ if last_comment.empty?
180
+ # new comment from programmer to translator?
181
+ comment1 = tk.value.lstrip
182
+ if comment1 =~ /^TRANSLATORS\:/
183
+ last_comment = $'
184
+ end
185
+ else
186
+ last_comment += "\n"
187
+ last_comment += tk.value
188
+ end
189
+ reset_comment = false
190
+ when RubyToken::TkNL
191
+ else
192
+ reset_comment = true
193
+ end
194
+ end
195
+ pomessages
196
+ end
197
+
198
+ def target?(file) # :nodoc:
199
+ true # always true, as the default parser.
200
+ end
201
+
202
+ private
203
+ def store_pomessage(pomessages, pomessage, file_name, line_no, last_comment) #:nodoc:
204
+ if pomessage && pomessage.msgid
205
+ pomessage.sources << file_name + ":" + line_no
206
+ pomessage.add_comment(last_comment) unless last_comment.empty?
207
+ pomessages << pomessage
208
+ true
209
+ else
210
+ false
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ if __FILE__ == $0
217
+ require 'pp'
218
+ ARGV.each do |path|
219
+ pp GetText::RubyParser.parse(path)
220
+ end
221
+
222
+ #rl = GetText::RubyLexX.new; rl.set_input(ARGF)
223
+ #rl.parse do |tk|
224
+ #p tk
225
+ #end
226
+ end
@@ -0,0 +1,199 @@
1
+ # encoding: utf-8
2
+
3
+ module GetText
4
+ class ParseError < StandardError
5
+ end
6
+
7
+ # Contains data related to the expression or sentence that
8
+ # is to be translated.
9
+ class PoMessage
10
+ PARAMS = {
11
+ :normal => [:msgid],
12
+ :plural => [:msgid, :msgid_plural],
13
+ :msgctxt => [:msgctxt, :msgid],
14
+ :msgctxt_plural => [:msgctxt, :msgid, :msgid_plural]
15
+ }
16
+
17
+ @@max_line_length = 70
18
+
19
+ # Sets the max line length.
20
+ def self.max_line_length=(len)
21
+ @@max_line_length = len
22
+ end
23
+
24
+ # Gets the max line length.
25
+ def self.max_line_length
26
+ @@max_line_length
27
+ end
28
+
29
+ # Required
30
+ attr_accessor :type # :normal, :plural, :msgctxt, :msgctxt_plural
31
+ attr_accessor :msgid
32
+ # Options
33
+ attr_accessor :msgid_plural
34
+ attr_accessor :msgctxt
35
+ attr_accessor :sources # ["file1:line1", "file2:line2", ...]
36
+ attr_accessor :comment
37
+
38
+ # Create the object. +type+ should be :normal, :plural, :msgctxt or :msgctxt_plural.
39
+ def initialize(type)
40
+ @type = type
41
+ @sources = []
42
+ @param_type = PARAMS[@type]
43
+ end
44
+
45
+ # Support for extracted comments. Explanation s.
46
+ # http://www.gnu.org/software/gettext/manual/gettext.html#Names
47
+ def add_comment(new_comment)
48
+ if (new_comment and ! new_comment.empty?)
49
+ @comment ||= ""
50
+ @comment += new_comment
51
+ end
52
+ to_s
53
+ end
54
+
55
+ # Returns a parameter representation suitable for po-files
56
+ # and other purposes.
57
+ def escaped(param_name)
58
+ orig = self.send param_name
59
+ orig.gsub(/"/, '\"').gsub(/\r/, '')
60
+ end
61
+
62
+ # Checks if the other translation target is mergeable with
63
+ # the current one. Relevant are msgid and translation context (msgctxt).
64
+ def ==(other)
65
+ other && other.msgid == self.msgid && other.msgctxt == self.msgctxt
66
+ end
67
+
68
+ # Merges two translation targets with the same msgid and returns the merged
69
+ # result. If one is declared as plural and the other not, then the one
70
+ # with the plural wins.
71
+ def merge(other)
72
+ return self unless other
73
+ raise ParseError, "Translation targets do not match: \n" \
74
+ " self: #{self.inspect}\n other: '#{other.inspect}'" unless self == other
75
+ if other.msgid_plural && !self.msgid_plural
76
+ res = other
77
+ unless (res.sources.include? self.sources[0])
78
+ res.sources += self.sources
79
+ res.add_comment(self.comment)
80
+ end
81
+ else
82
+ res = self
83
+ unless (res.sources.include? other.sources[0])
84
+ res.sources += other.sources
85
+ res.add_comment(other.comment)
86
+ end
87
+ end
88
+ res
89
+ end
90
+
91
+ # Output the po message for the po-file.
92
+ def to_po_str
93
+ raise "msgid is nil." unless @msgid
94
+ raise "sources is nil." unless @sources
95
+
96
+ str = ""
97
+ # extracted comments
98
+ if comment
99
+ comment.split("\n").each do |comment_line|
100
+ str << "\n#. #{comment_line.strip}"
101
+ end
102
+ end
103
+
104
+ # references
105
+ curr_pos = @@max_line_length
106
+ sources.each do |e|
107
+ if curr_pos + e.size > @@max_line_length
108
+ str << "\n#:"
109
+ curr_pos = 3
110
+ else
111
+ curr_pos += (e.size + 1)
112
+ end
113
+ str << " " << e
114
+ end
115
+
116
+ # msgctxt, msgid, msgstr
117
+ str << "\nmsgctxt \"" << msgctxt << "\"" if msgctxt?
118
+ str << "\nmsgid \"" << escaped(:msgid) << "\"\n"
119
+ if plural?
120
+ str << "msgid_plural \"" << escaped(:msgid_plural) << "\"\n"
121
+ str << "msgstr[0] \"\"\n"
122
+ str << "msgstr[1] \"\"\n"
123
+ else
124
+ str << "msgstr \"\"\n"
125
+ end
126
+ str
127
+ end
128
+
129
+ # Returns true if the type is kind of msgctxt.
130
+ # And if this is a kind of msgctxt and msgctxt property
131
+ # is nil, then raise an RuntimeException.
132
+ def msgctxt?
133
+ if [:msgctxt, :msgctxt_plural].include? @type
134
+ raise "This PoMessage is a kind of msgctxt but the msgctxt property is nil. msgid: #{msgid}" unless @msgctxt
135
+ true
136
+ end
137
+ end
138
+
139
+ # Returns true if the type is kind of plural.
140
+ # And if this is a kind of plural and msgid_plural property
141
+ # is nil, then raise an RuntimeException.
142
+ def plural?
143
+ if [:plural, :msgctxt_plural].include? @type
144
+ raise "This PoMessage is a kind of plural but the msgid_plural property is nil. msgid: #{msgid}" unless @msgid_plural
145
+ true
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ # sets or extends the value of a translation target params like msgid,
152
+ # msgctxt etc.
153
+ # param is symbol with the name of param
154
+ # value - new value
155
+ def set_value(param, value)
156
+ send "#{param}=", (send(param) || '') + value.gsub(/\n/, '\n')
157
+ end
158
+
159
+ public
160
+ # For backward comatibility. This doesn't support "comment".
161
+ # ary = [msgid1, "file1:line1", "file2:line"]
162
+ def self.new_from_ary(ary)
163
+ ary = ary.dup
164
+ msgid = ary.shift
165
+ sources = ary
166
+ type = :normal
167
+ msgctxt = nil
168
+ msgid_plural = nil
169
+
170
+ if msgid.include? "\004"
171
+ msgctxt, msgid = msgid.split(/\004/)
172
+ type = :msgctxt
173
+ end
174
+ if msgid.include? "\000"
175
+ ids = msgid.split(/\000/)
176
+ msgid = ids[0]
177
+ msgid_plural = ids[1]
178
+ if type == :msgctxt
179
+ type = :msgctxt_plural
180
+ else
181
+ type = :plural
182
+ end
183
+ end
184
+ ret = self.new(type)
185
+ ret.msgid = msgid
186
+ ret.sources = sources
187
+ ret.msgctxt = msgctxt
188
+ ret.msgid_plural = msgid_plural
189
+ ret
190
+ end
191
+
192
+ def [](number)
193
+ param = @param_type[number]
194
+ raise ParseError, 'no more string parameters expected' unless param
195
+ send param
196
+ end
197
+ end
198
+
199
+ end