grosser-fast_gettext 0.2.2 → 0.2.3
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/README.markdown +2 -3
- data/VERSION.yml +1 -1
- data/lib/fast_gettext/mo_file.rb +1 -1
- data/vendor/mofile.rb +234 -232
- metadata +1 -1
data/README.markdown
CHANGED
@@ -58,13 +58,12 @@ then e.g. controllers, so set them inside your application_controller.
|
|
58
58
|
...
|
59
59
|
|
60
60
|
#application_controller.rb
|
61
|
-
FastGettext.available_locales = ['de','en',...]
|
62
|
-
FastGettext.text_domain = 'frontend'
|
63
|
-
|
64
61
|
class ApplicationController ...
|
65
62
|
include FastGettext
|
66
63
|
before_filter :set_locale
|
67
64
|
def set_locale
|
65
|
+
FastGettext.available_locales = ['de','en',...]
|
66
|
+
FastGettext.text_domain = 'frontend'
|
68
67
|
sessions[:locale] = I18n.locale = FastGettext.locale = params[:locale] || sessions[:locale] || 'en'
|
69
68
|
end
|
70
69
|
|
data/VERSION.yml
CHANGED
data/lib/fast_gettext/mo_file.rb
CHANGED
data/vendor/mofile.rb
CHANGED
@@ -18,273 +18,275 @@
|
|
18
18
|
require 'iconv'
|
19
19
|
require 'stringio'
|
20
20
|
|
21
|
-
module
|
22
|
-
|
23
|
-
class
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
MAGIC_BIG_ENDIAN = "\x95\x04\x12\xde"
|
46
|
-
MAGIC_LITTLE_ENDIAN = "\xde\x12\x04\x95"
|
21
|
+
module FastGettext
|
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
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
result.load(arg)
|
51
|
-
end
|
46
|
+
MAGIC_BIG_ENDIAN = "\x95\x04\x12\xde"
|
47
|
+
MAGIC_LITTLE_ENDIAN = "\xde\x12\x04\x95"
|
52
48
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@output_charset = output_charset
|
58
|
-
super()
|
59
|
-
end
|
49
|
+
def self.open(arg = nil, output_charset = nil)
|
50
|
+
result = self.new(output_charset)
|
51
|
+
result.load(arg)
|
52
|
+
end
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
clear
|
54
|
+
def initialize(output_charset = nil)
|
55
|
+
@filename = nil
|
56
|
+
@last_modified = nil
|
57
|
+
@little_endian = true
|
58
|
+
@output_charset = output_charset
|
59
|
+
super()
|
68
60
|
end
|
69
|
-
self
|
70
|
-
end
|
71
61
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
62
|
+
def update!
|
63
|
+
if FileTest.exist?(@filename)
|
64
|
+
st = File.stat(@filename)
|
65
|
+
load(@filename) unless (@last_modified == [st.ctime, st.mtime])
|
66
|
+
else
|
67
|
+
warn "#{@filename} was lost." if $DEBUG
|
68
|
+
clear
|
78
69
|
end
|
79
|
-
|
80
|
-
else
|
81
|
-
load_from_stream(arg)
|
70
|
+
self
|
82
71
|
end
|
83
|
-
@filename = arg
|
84
|
-
self
|
85
|
-
end
|
86
72
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
73
|
+
def load(arg)
|
74
|
+
if arg.kind_of? String
|
75
|
+
begin
|
76
|
+
st = File.stat(arg)
|
77
|
+
@last_modified = [st.ctime, st.mtime]
|
78
|
+
rescue Exception
|
79
|
+
end
|
80
|
+
load_from_file(arg)
|
81
|
+
else
|
82
|
+
load_from_stream(arg)
|
83
|
+
end
|
84
|
+
@filename = arg
|
85
|
+
self
|
96
86
|
end
|
97
87
|
|
98
|
-
|
99
|
-
|
88
|
+
def load_from_stream(io)
|
89
|
+
magic = io.read(4)
|
90
|
+
case magic
|
91
|
+
when MAGIC_BIG_ENDIAN
|
92
|
+
@little_endian = false
|
93
|
+
when MAGIC_LITTLE_ENDIAN
|
94
|
+
@little_endian = true
|
95
|
+
else
|
96
|
+
raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
|
97
|
+
end
|
100
98
|
|
101
|
-
|
99
|
+
endian_type6 = @little_endian ? 'V6' : 'N6'
|
100
|
+
endian_type_astr = @little_endian ? 'V*' : 'N*'
|
102
101
|
|
103
|
-
|
104
|
-
# FIXME: It doesn't support sysdep correctly.
|
105
|
-
header.n_sysdep_segments = io.read(4).unpack(endian_type6)
|
106
|
-
header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
|
107
|
-
header.n_sysdep_strings = io.read(4).unpack(endian_type6)
|
108
|
-
header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
|
109
|
-
header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
|
110
|
-
elsif header.revision > 1
|
111
|
-
raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
|
112
|
-
end
|
113
|
-
io.pos = header.orig_table_offset
|
114
|
-
orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
|
102
|
+
header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
|
115
103
|
|
116
|
-
|
117
|
-
|
104
|
+
if header.revision == 1
|
105
|
+
# FIXME: It doesn't support sysdep correctly.
|
106
|
+
header.n_sysdep_segments = io.read(4).unpack(endian_type6)
|
107
|
+
header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
|
108
|
+
header.n_sysdep_strings = io.read(4).unpack(endian_type6)
|
109
|
+
header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
|
110
|
+
header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
|
111
|
+
elsif header.revision > 1
|
112
|
+
raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
|
113
|
+
end
|
114
|
+
io.pos = header.orig_table_offset
|
115
|
+
orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
|
118
116
|
|
119
|
-
|
120
|
-
|
121
|
-
io.pos = orig_table_data[i * 2 + 1]
|
122
|
-
original_strings[i] = io.read(orig_table_data[i * 2 + 0])
|
123
|
-
end
|
117
|
+
io.pos = header.translated_table_offset
|
118
|
+
trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
|
124
119
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
120
|
+
original_strings = Array.new(header.nstrings)
|
121
|
+
for i in 0...header.nstrings
|
122
|
+
io.pos = orig_table_data[i * 2 + 1]
|
123
|
+
original_strings[i] = io.read(orig_table_data[i * 2 + 0])
|
124
|
+
end
|
125
|
+
|
126
|
+
clear
|
127
|
+
for i in 0...header.nstrings
|
128
|
+
io.pos = trans_table_data[i * 2 + 1]
|
129
|
+
str = io.read(trans_table_data[i * 2 + 0])
|
130
|
+
|
131
|
+
if (! original_strings[i]) || original_strings[i] == ""
|
132
|
+
if str
|
133
|
+
@charset = nil
|
134
|
+
@nplurals = nil
|
135
|
+
@plural = nil
|
136
|
+
str.each_line{|line|
|
137
|
+
if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
|
138
|
+
@charset = $1
|
139
|
+
elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
|
140
|
+
@nplurals = $1
|
141
|
+
@plural = $2
|
142
|
+
end
|
143
|
+
break if @charset and @nplurals
|
144
|
+
}
|
145
|
+
@nplurals = "1" unless @nplurals
|
146
|
+
@plural = "0" unless @plural
|
147
|
+
end
|
148
|
+
else
|
149
|
+
if @output_charset
|
150
|
+
begin
|
151
|
+
str = Iconv.conv(@output_charset, @charset, str) if @charset
|
152
|
+
rescue Iconv::Failure
|
153
|
+
if $DEBUG
|
154
|
+
warn "@charset = ", @charset
|
155
|
+
warn"@output_charset = ", @output_charset
|
156
|
+
warn "msgid = ", original_strings[i]
|
157
|
+
warn "msgstr = ", str
|
158
|
+
end
|
157
159
|
end
|
158
160
|
end
|
159
161
|
end
|
162
|
+
self[original_strings[i]] = str.freeze
|
160
163
|
end
|
161
|
-
self
|
164
|
+
self
|
162
165
|
end
|
163
|
-
self
|
164
|
-
end
|
165
166
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
171
|
-
|
172
|
-
def next_prime(seed)
|
173
|
-
require 'mathn'
|
174
|
-
prime = Prime.new
|
175
|
-
while current = prime.succ
|
176
|
-
return current if current > seed
|
167
|
+
# Is this number a prime number ?
|
168
|
+
# http://apidock.com/ruby/Prime
|
169
|
+
def prime?(number)
|
170
|
+
('1' * number) !~ /^1?$|^(11+?)\1+$/
|
177
171
|
end
|
178
|
-
end
|
179
172
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
def hash_string(str)
|
186
|
-
hval = 0
|
187
|
-
i = 0
|
188
|
-
str.each_byte do |b|
|
189
|
-
break if b == '\0'
|
190
|
-
hval <<= 4
|
191
|
-
hval += b.to_i
|
192
|
-
g = hval & (0xf << (HASHWORDBITS - 4))
|
193
|
-
if (g != 0)
|
194
|
-
hval ^= g >> (HASHWORDBITS - 8)
|
195
|
-
hval ^= g
|
173
|
+
def next_prime(seed)
|
174
|
+
require 'mathn'
|
175
|
+
prime = Prime.new
|
176
|
+
while current = prime.succ
|
177
|
+
return current if current > seed
|
196
178
|
end
|
197
179
|
end
|
198
|
-
hval
|
199
|
-
end
|
200
180
|
|
201
|
-
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
ary = to_a
|
220
|
-
ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
|
221
|
-
|
222
|
-
pos = header.hash_table_size * 4 + header.hash_table_offset
|
223
|
-
|
224
|
-
orig_table_data = Array.new()
|
225
|
-
ary.each{|item, _|
|
226
|
-
orig_table_data.push(item.size)
|
227
|
-
orig_table_data.push(pos)
|
228
|
-
pos += item.size + 1 # +1 is <NUL>
|
229
|
-
}
|
230
|
-
io.write(orig_table_data.pack('V*'))
|
231
|
-
|
232
|
-
trans_table_data = Array.new()
|
233
|
-
ary.each{|_, item|
|
234
|
-
trans_table_data.push(item.size)
|
235
|
-
trans_table_data.push(pos)
|
236
|
-
pos += item.size + 1 # +1 is <NUL>
|
237
|
-
}
|
238
|
-
io.write(trans_table_data.pack('V*'))
|
239
|
-
|
240
|
-
hash_tab = Array.new(hash_table_size)
|
241
|
-
j = 0
|
242
|
-
ary[0...size].each {|key, _|
|
243
|
-
hash_val = hash_string(key)
|
244
|
-
idx = hash_val % hash_table_size
|
245
|
-
if hash_tab[idx] != nil
|
246
|
-
incr = 1 + (hash_val % (hash_table_size - 2))
|
247
|
-
begin
|
248
|
-
if (idx >= hash_table_size - incr)
|
249
|
-
idx -= hash_table_size - incr
|
250
|
-
else
|
251
|
-
idx += incr
|
252
|
-
end
|
253
|
-
end until (hash_tab[idx] == nil)
|
181
|
+
# From gettext-0.12.1/gettext-runtime/intl/hash-string.h
|
182
|
+
# Defines the so called `hashpjw' function by P.J. Weinberger
|
183
|
+
# [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
|
184
|
+
# 1986, 1987 Bell Telephone Laboratories, Inc.]
|
185
|
+
HASHWORDBITS = 32
|
186
|
+
def hash_string(str)
|
187
|
+
hval = 0
|
188
|
+
i = 0
|
189
|
+
str.each_byte do |b|
|
190
|
+
break if b == '\0'
|
191
|
+
hval <<= 4
|
192
|
+
hval += b.to_i
|
193
|
+
g = hval & (0xf << (HASHWORDBITS - 4))
|
194
|
+
if (g != 0)
|
195
|
+
hval ^= g >> (HASHWORDBITS - 8)
|
196
|
+
hval ^= g
|
197
|
+
end
|
254
198
|
end
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
199
|
+
hval
|
200
|
+
end
|
201
|
+
|
202
|
+
def save_to_stream(io)
|
203
|
+
#Save data as little endian format.
|
204
|
+
header_size = 4 * 7
|
205
|
+
table_size = 4 * 2 * size
|
206
|
+
|
207
|
+
hash_table_size = next_prime((size * 4) / 3)
|
208
|
+
hash_table_size = 3 if hash_table_size <= 2
|
209
|
+
header = Header.new(
|
210
|
+
MAGIC_LITTLE_ENDIAN, # magic
|
211
|
+
0, # revision
|
212
|
+
size, # nstrings
|
213
|
+
header_size, # orig_table_offset
|
214
|
+
header_size + table_size, # translated_table_offset
|
215
|
+
hash_table_size, # hash_table_size
|
216
|
+
header_size + table_size * 2 # hash_table_offset
|
217
|
+
)
|
218
|
+
io.write(header.to_a.pack('a4V*'))
|
219
|
+
|
220
|
+
ary = to_a
|
221
|
+
ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
|
222
|
+
|
223
|
+
pos = header.hash_table_size * 4 + header.hash_table_offset
|
224
|
+
|
225
|
+
orig_table_data = Array.new()
|
226
|
+
ary.each{|item, _|
|
227
|
+
orig_table_data.push(item.size)
|
228
|
+
orig_table_data.push(pos)
|
229
|
+
pos += item.size + 1 # +1 is <NUL>
|
230
|
+
}
|
231
|
+
io.write(orig_table_data.pack('V*'))
|
232
|
+
|
233
|
+
trans_table_data = Array.new()
|
234
|
+
ary.each{|_, item|
|
235
|
+
trans_table_data.push(item.size)
|
236
|
+
trans_table_data.push(pos)
|
237
|
+
pos += item.size + 1 # +1 is <NUL>
|
238
|
+
}
|
239
|
+
io.write(trans_table_data.pack('V*'))
|
240
|
+
|
241
|
+
hash_tab = Array.new(hash_table_size)
|
242
|
+
j = 0
|
243
|
+
ary[0...size].each {|key, _|
|
244
|
+
hash_val = hash_string(key)
|
245
|
+
idx = hash_val % hash_table_size
|
246
|
+
if hash_tab[idx] != nil
|
247
|
+
incr = 1 + (hash_val % (hash_table_size - 2))
|
248
|
+
begin
|
249
|
+
if (idx >= hash_table_size - incr)
|
250
|
+
idx -= hash_table_size - incr
|
251
|
+
else
|
252
|
+
idx += incr
|
253
|
+
end
|
254
|
+
end until (hash_tab[idx] == nil)
|
255
|
+
end
|
256
|
+
hash_tab[idx] = j + 1
|
257
|
+
j += 1
|
258
|
+
}
|
259
|
+
hash_tab.collect!{|i| i ? i : 0}
|
259
260
|
|
260
|
-
|
261
|
+
io.write(hash_tab.pack('V*'))
|
261
262
|
|
262
|
-
|
263
|
-
|
263
|
+
ary.each{|item, _| io.write(item); io.write("\0") }
|
264
|
+
ary.each{|_, item| io.write(item); io.write("\0") }
|
264
265
|
|
265
|
-
|
266
|
-
|
266
|
+
self
|
267
|
+
end
|
267
268
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
269
|
+
def load_from_file(filename)
|
270
|
+
@filename = filename
|
271
|
+
begin
|
272
|
+
File.open(filename, 'rb'){|f| load_from_stream(f)}
|
273
|
+
rescue => e
|
274
|
+
e.set_backtrace("File: #{@filename}")
|
275
|
+
raise e
|
276
|
+
end
|
275
277
|
end
|
276
|
-
end
|
277
278
|
|
278
|
-
|
279
|
-
|
280
|
-
|
279
|
+
def save_to_file(filename)
|
280
|
+
File.open(filename, 'wb'){|f| save_to_stream(f)}
|
281
|
+
end
|
281
282
|
|
282
|
-
|
283
|
-
|
284
|
-
|
283
|
+
def set_comment(msgid_or_sym, comment)
|
284
|
+
#Do nothing
|
285
|
+
end
|
285
286
|
|
286
287
|
|
287
|
-
|
288
|
-
|
288
|
+
attr_accessor :little_endian, :path, :last_modified
|
289
|
+
attr_reader :charset, :nplurals, :plural
|
290
|
+
end
|
289
291
|
end
|
290
292
|
end
|