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