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,123 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ locale_path.rb - GetText::LocalePath
5
+
6
+ Copyright (C) 2001-2010 Masao Mutoh
7
+
8
+ You may redistribute it and/or modify it under the same
9
+ license terms as Ruby or LGPL.
10
+
11
+ =end
12
+
13
+ require 'rbconfig'
14
+ require 'gettext/core_ext/string'
15
+
16
+ module GetText
17
+ # Treats locale-path for mo-files.
18
+ class LocalePath
19
+ include Locale::Util::Memoizable
20
+
21
+ # The default locale paths.
22
+ CONFIG_PREFIX = Config::CONFIG['prefix'].gsub(/\/local/, "")
23
+ DEFAULT_RULES = [
24
+ "./locale/%{lang}/LC_MESSAGES/%{name}.mo",
25
+ "./locale/%{lang}/%{name}.mo",
26
+ "#{Config::CONFIG['datadir']}/locale/%{lang}/LC_MESSAGES/%{name}.mo",
27
+ "#{Config::CONFIG['datadir'].gsub(/\/local/, "")}/locale/%{lang}/LC_MESSAGES/%{name}.mo",
28
+ "#{CONFIG_PREFIX}/share/locale/%{lang}/LC_MESSAGES/%{name}.mo",
29
+ "#{CONFIG_PREFIX}/local/share/locale/%{lang}/LC_MESSAGES/%{name}.mo"
30
+ ].uniq
31
+
32
+ class << self
33
+ include Locale::Util::Memoizable
34
+
35
+ # Add default locale path. Usually you should use GetText.add_default_locale_path instead.
36
+ # * path: a new locale path. (e.g.) "/usr/share/locale/%{lang}/LC_MESSAGES/%{name}.mo"
37
+ # ('locale' => "ja_JP", 'name' => "textdomain")
38
+ # * Returns: the new DEFAULT_LOCALE_PATHS
39
+ def add_default_rule(path)
40
+ DEFAULT_RULES.unshift(path)
41
+ end
42
+
43
+ # Returns path rules as an Array.
44
+ # (e.g.) ["/usr/share/locale/%{lang}/LC_MESSAGES/%{name}.mo", ...]
45
+ def default_path_rules
46
+ default_path_rules = []
47
+
48
+ if ENV["GETTEXT_PATH"]
49
+ ENV["GETTEXT_PATH"].split(/,/).each {|i|
50
+ default_path_rules += ["#{i}/%{lang}/LC_MESSAGES/%{name}.mo", "#{i}/%{lang}/%{name}.mo"]
51
+ }
52
+ end
53
+ default_path_rules += DEFAULT_RULES
54
+
55
+ load_path = $LOAD_PATH.dup
56
+ if defined? ::Gem
57
+ load_path += Gem.all_load_paths
58
+ end
59
+ load_path.map!{|v| v.respond_to?("match") and v.match(/(.*?)(\/lib)*?$/); $1}
60
+ load_path.each {|path|
61
+ default_path_rules += [
62
+ "#{path}/data/locale/%{lang}/LC_MESSAGES/%{name}.mo",
63
+ "#{path}/data/locale/%{lang}/%{name}.mo",
64
+ "#{path}/locale/%{lang}/%{name}.mo"]
65
+ }
66
+ # paths existed only.
67
+ default_path_rules = default_path_rules.select{|path|
68
+ Dir.glob(path % {:lang => "*", :name => "*"}).size > 0}.uniq
69
+ default_path_rules
70
+ end
71
+ memoize_dup :default_path_rules
72
+ end
73
+
74
+ attr_reader :locale_paths, :supported_locales
75
+
76
+ # Creates a new GetText::TextDomain.
77
+ # * name: the textdomain name.
78
+ # * topdir: the locale path ("%{topdir}/%{lang}/LC_MESSAGES/%{name}.mo") or nil.
79
+ def initialize(name, topdir = nil)
80
+ @name = name
81
+
82
+ if topdir
83
+ path_rules = ["#{topdir}/%{lang}/LC_MESSAGES/%{name}.mo", "#{topdir}/%{lang}/%{name}.mo"]
84
+ else
85
+ path_rules = self.class.default_path_rules
86
+ end
87
+
88
+ @locale_paths = {}
89
+ path_rules.each do |rule|
90
+ this_path_rules = rule % {:lang => "([^\/]+)", :name => name}
91
+ Dir.glob(rule %{:lang => "*", :name => name}).each do |path|
92
+ if /#{this_path_rules}/ =~ path
93
+ @locale_paths[$1] = path.untaint unless @locale_paths[$1]
94
+ end
95
+ end
96
+ end
97
+ @supported_locales = @locale_paths.keys.sort
98
+ end
99
+
100
+ # Gets the current path.
101
+ # * lang: a Locale::Tag.
102
+ def current_path(lang)
103
+ lang_candidates = lang.to_posix.candidates
104
+ search_files = []
105
+
106
+ lang_candidates.each do |tag|
107
+ path = @locale_paths[tag.to_s]
108
+ warn "GetText::TextDomain#load_mo: mo-file is #{path}" if $DEBUG
109
+ return path if path
110
+ end
111
+
112
+ if $DEBUG
113
+ warn "MO file is not found in"
114
+ @locale_paths.each do |path|
115
+ warn " #{path[1]}"
116
+ end
117
+ end
118
+ nil
119
+ end
120
+ memoize :current_path
121
+
122
+ end
123
+ end
@@ -0,0 +1,332 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ mofile.rb - A simple class for operating GNU MO file.
5
+
6
+ Copyright (C) 2003-2009 Masao Mutoh
7
+ Copyright (C) 2002 Masahiro Sakai, Masao Mutoh
8
+ Copyright (C) 2001 Masahiro Sakai
9
+
10
+ Masahiro Sakai <s01397ms at sfc.keio.ac.jp>
11
+ Masao Mutoh <mutomasa at gmail.com>
12
+
13
+ You can redistribute this file and/or modify it under the same term
14
+ of Ruby. License of Ruby is included with Ruby distribution in
15
+ the file "README".
16
+
17
+ =end
18
+
19
+ require 'gettext/core_ext/iconv'
20
+ require 'stringio'
21
+
22
+ module GetText
23
+ class MOFile < Hash
24
+ class InvalidFormat < RuntimeError; end;
25
+
26
+ attr_reader :filename
27
+
28
+ Header = Struct.new(:magic,
29
+ :revision,
30
+ :nstrings,
31
+ :orig_table_offset,
32
+ :translated_table_offset,
33
+ :hash_table_size,
34
+ :hash_table_offset)
35
+
36
+ # The following are only used in .mo files
37
+ # with minor revision >= 1.
38
+ class HeaderRev1 < Header
39
+ attr_accessor :n_sysdep_segments,
40
+ :sysdep_segments_offset,
41
+ :n_sysdep_strings,
42
+ :orig_sysdep_tab_offset,
43
+ :trans_sysdep_tab_offset
44
+ end
45
+
46
+ MAGIC_BIG_ENDIAN = "\x95\x04\x12\xde"
47
+ MAGIC_LITTLE_ENDIAN = "\xde\x12\x04\x95"
48
+
49
+ def self.open(arg = nil, output_charset = nil)
50
+ result = self.new(output_charset)
51
+ result.load(arg)
52
+ end
53
+
54
+ def initialize(output_charset = nil)
55
+ @filename = nil
56
+ @last_modified = nil
57
+ @little_endian = true
58
+ @output_charset = output_charset
59
+ @plural_proc = nil
60
+ super()
61
+ end
62
+
63
+ def update!
64
+ if FileTest.exist?(@filename)
65
+ st = File.stat(@filename)
66
+ load(@filename) unless (@last_modified == [st.ctime, st.mtime])
67
+ else
68
+ warn "#{@filename} was lost." if $DEBUG
69
+ clear
70
+ end
71
+ self
72
+ end
73
+
74
+ def load(arg)
75
+ if arg.kind_of? String
76
+ begin
77
+ st = File.stat(arg)
78
+ @last_modified = [st.ctime, st.mtime]
79
+ rescue Exception
80
+ end
81
+ load_from_file(arg)
82
+ else
83
+ load_from_stream(arg)
84
+ end
85
+ @filename = arg
86
+ self
87
+ end
88
+
89
+ def load_from_stream(io)
90
+ magic = io.read(4)
91
+ case magic
92
+ when MAGIC_BIG_ENDIAN
93
+ @little_endian = false
94
+ when MAGIC_LITTLE_ENDIAN
95
+ @little_endian = true
96
+ else
97
+ raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
98
+ end
99
+
100
+ endian_type6 = @little_endian ? 'V6' : 'N6'
101
+ endian_type_astr = @little_endian ? 'V*' : 'N*'
102
+
103
+ header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
104
+
105
+ if header.revision == 1
106
+ # FIXME: It doesn't support sysdep correctly.
107
+ header.n_sysdep_segments = io.read(4).unpack(endian_type6)
108
+ header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
109
+ header.n_sysdep_strings = io.read(4).unpack(endian_type6)
110
+ header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
111
+ header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
112
+ elsif header.revision > 1
113
+ raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
114
+ end
115
+ io.pos = header.orig_table_offset
116
+ orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
117
+
118
+ io.pos = header.translated_table_offset
119
+ trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
120
+
121
+ original_strings = Array.new(header.nstrings)
122
+ for i in 0...header.nstrings
123
+ io.pos = orig_table_data[i * 2 + 1]
124
+ original_strings[i] = io.read(orig_table_data[i * 2 + 0])
125
+ end
126
+
127
+ clear
128
+ for i in 0...header.nstrings
129
+ io.pos = trans_table_data[i * 2 + 1]
130
+ str = io.read(trans_table_data[i * 2 + 0])
131
+
132
+ if (! original_strings[i]) || original_strings[i] == ""
133
+ if str
134
+ @charset = nil
135
+ @nplurals = nil
136
+ @plural = nil
137
+ str.each_line{|line|
138
+ if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
139
+ @charset = $1
140
+ elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
141
+ @nplurals = $1
142
+ @plural = $2
143
+ end
144
+ break if @charset and @nplurals
145
+ }
146
+ @nplurals = "1" unless @nplurals
147
+ @plural = "0" unless @plural
148
+ end
149
+ else
150
+ if @output_charset
151
+ begin
152
+ str = Iconv.conv(@output_charset, @charset, str) if @charset
153
+ rescue Iconv::Failure
154
+ if $DEBUG
155
+ warn "@charset = ", @charset
156
+ warn"@output_charset = ", @output_charset
157
+ warn "msgid = ", original_strings[i]
158
+ warn "msgstr = ", str
159
+ end
160
+ end
161
+ end
162
+ end
163
+ self[original_strings[i]] = str.freeze
164
+ end
165
+ self
166
+ end
167
+
168
+ def prime?(number)
169
+ ('1' * number) !~ /^1?$|^(11+?)\1+$/
170
+ end
171
+
172
+ begin
173
+ require 'prime'
174
+ def next_prime(seed)
175
+ Prime.instance.find{|x| x > seed }
176
+ end
177
+ rescue LoadError
178
+ def next_prime(seed)
179
+ require 'mathn'
180
+ prime = Prime.new
181
+ while current = prime.succ
182
+ return current if current > seed
183
+ end
184
+ end
185
+ end
186
+
187
+ HASHWORDBITS = 32
188
+ # From gettext-0.12.1/gettext-runtime/intl/hash-string.h
189
+ # Defines the so called `hashpjw' function by P.J. Weinberger
190
+ # [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
191
+ # 1986, 1987 Bell Telephone Laboratories, Inc.]
192
+ def hash_string(str)
193
+ hval = 0
194
+ i = 0
195
+ str.each_byte do |b|
196
+ break if b == '\0'
197
+ hval <<= 4
198
+ hval += b.to_i
199
+ g = hval & (0xf << (HASHWORDBITS - 4))
200
+ if (g != 0)
201
+ hval ^= g >> (HASHWORDBITS - 8)
202
+ hval ^= g
203
+ end
204
+ end
205
+ hval
206
+ end
207
+
208
+ #Save data as little endian format.
209
+ def save_to_stream(io)
210
+ header_size = 4 * 7
211
+ table_size = 4 * 2 * size
212
+
213
+ hash_table_size = next_prime((size * 4) / 3)
214
+ hash_table_size = 3 if hash_table_size <= 2
215
+ header = Header.new(
216
+ MAGIC_LITTLE_ENDIAN, # magic
217
+ 0, # revision
218
+ size, # nstrings
219
+ header_size, # orig_table_offset
220
+ header_size + table_size, # translated_table_offset
221
+ hash_table_size, # hash_table_size
222
+ header_size + table_size * 2 # hash_table_offset
223
+ )
224
+ io.write(header.to_a.pack('a4V*'))
225
+
226
+ ary = to_a
227
+ ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
228
+
229
+ pos = header.hash_table_size * 4 + header.hash_table_offset
230
+
231
+ orig_table_data = Array.new()
232
+ ary.each{|item, _|
233
+ orig_table_data.push(item.bytesize)
234
+ orig_table_data.push(pos)
235
+ pos += item.bytesize + 1 # +1 is <NUL>
236
+ }
237
+ io.write(orig_table_data.pack('V*'))
238
+
239
+ trans_table_data = Array.new()
240
+ ary.each{|_, item|
241
+ trans_table_data.push(item.bytesize)
242
+ trans_table_data.push(pos)
243
+ pos += item.bytesize + 1 # +1 is <NUL>
244
+ }
245
+ io.write(trans_table_data.pack('V*'))
246
+
247
+ hash_tab = Array.new(hash_table_size)
248
+ j = 0
249
+ ary[0...size].each {|key, _|
250
+ hash_val = hash_string(key)
251
+ idx = hash_val % hash_table_size
252
+ if hash_tab[idx] != nil
253
+ incr = 1 + (hash_val % (hash_table_size - 2))
254
+ begin
255
+ if (idx >= hash_table_size - incr)
256
+ idx -= hash_table_size - incr
257
+ else
258
+ idx += incr
259
+ end
260
+ end until (hash_tab[idx] == nil)
261
+ end
262
+ hash_tab[idx] = j + 1
263
+ j += 1
264
+ }
265
+ hash_tab.collect!{|i| i ? i : 0}
266
+
267
+ io.write(hash_tab.pack('V*'))
268
+
269
+ ary.each{|item, _| io.write(item); io.write("\0") }
270
+ ary.each{|_, item| io.write(item); io.write("\0") }
271
+
272
+ self
273
+ end
274
+
275
+ def load_from_file(filename)
276
+ @filename = filename
277
+ begin
278
+ File.open(filename, 'rb'){|f| load_from_stream(f)}
279
+ rescue => e
280
+ e.set_backtrace("File: #{@filename}")
281
+ raise e
282
+ end
283
+ end
284
+
285
+ def save_to_file(filename)
286
+ File.open(filename, 'wb'){|f| save_to_stream(f)}
287
+ end
288
+
289
+ def set_comment(msgid_or_sym, comment)
290
+ #Do nothing
291
+ end
292
+
293
+ def plural_as_proc
294
+ unless @plural_proc
295
+ @plural_proc = Proc.new{|n| eval(@plural)}
296
+ begin
297
+ @plural_proc.call(1)
298
+ rescue
299
+ @plural_proc = Proc.new{|n| 0}
300
+ end
301
+ end
302
+ @plural_proc
303
+ end
304
+
305
+ attr_accessor :little_endian, :path, :last_modified
306
+ attr_reader :charset, :nplurals, :plural
307
+ end
308
+
309
+ end
310
+
311
+ # Test
312
+
313
+ if $0 == __FILE__
314
+ if (ARGV.include? "-h") or (ARGV.include? "--help")
315
+ STDERR.puts("mo.rb [filename.mo ...]")
316
+ exit
317
+ end
318
+
319
+ ARGV.each{ |item|
320
+ mo = GetText::MOFile.open(item)
321
+ puts "------------------------------------------------------------------"
322
+ puts "charset = \"#{mo.charset}\""
323
+ puts "nplurals = \"#{mo.nplurals}\""
324
+ puts "plural = \"#{mo.plural}\""
325
+ puts "------------------------------------------------------------------"
326
+ mo.each do |key, value|
327
+ puts "original message = #{key.inspect}"
328
+ puts "translated message = #{value.inspect}"
329
+ puts "--------------------------------------------------------------------"
330
+ end
331
+ }
332
+ end