ofm_gettext 2.0.0 → 2.0.1

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.
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