1 #
2 # tk/msgcat.rb : methods for Tcl message catalog
3 # by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
4 #
5 require 'tk'
6
7 #class TkMsgCatalog
8 class TkMsgCatalog < TkObject
9 include TkCore
10 extend Tk
11 #extend TkMsgCatalog
12
13 TkCommandNames = [
14 '::msgcat::mc'.freeze,
15 '::msgcat::mcmax'.freeze,
16 '::msgcat::mclocale'.freeze,
17 '::msgcat::mcpreferences'.freeze,
18 '::msgcat::mcload'.freeze,
19 '::msgcat::mcset'.freeze,
20 '::msgcat::mcmset'.freeze,
21 '::msgcat::mcunknown'.freeze
22 ].freeze
23
24 tk_call_without_enc('package', 'require', 'Tcl', '8.2')
25
26 PACKAGE_NAME = 'msgcat'.freeze
27 def self.package_name
28 PACKAGE_NAME
29 end
30
31 if self.const_defined? :FORCE_VERSION
32 tk_call_without_enc('package', 'require', 'msgcat', FORCE_VERSION)
33 else
34 tk_call_without_enc('package', 'require', 'msgcat')
35 end
36
37 MSGCAT_EXT = '.msg'
38
39 UNKNOWN_CBTBL = Hash.new{|hash,key| hash[key] = {}}.taint
40
41 TkCore::INTERP.add_tk_procs('::msgcat::mcunknown', 'args', <<-'EOL')
42 if {[set st [catch {eval {ruby_cmd TkMsgCatalog callback} [namespace current] $args} ret]] != 0} {
43 #return -code $st $ret
44 set idx [string first "\n\n" $ret]
45 if {$idx > 0} {
46 return -code $st \
47 -errorinfo [string range $ret [expr $idx + 2] \
48 [string length $ret]] \
49 [string range $ret 0 [expr $idx - 1]]
50 } else {
51 return -code $st $ret
52 }
53 } else {
54 return $ret
55 }
56 EOL
57
58 def self.callback(namespace, locale, src_str, *args)
59 src_str = sprintf(src_str, *args) unless args.empty?
60 cmd_tbl = TkMsgCatalog::UNKNOWN_CBTBL[TkCore::INTERP.__getip]
61 cmd = cmd_tbl[namespace]
62 cmd = cmd_tbl['::'] unless cmd # use global scope as interp default
63 return src_str unless cmd # no cmd -> return src-str (default action)
64 begin
65 cmd.call(locale, src_str)
66 rescue SystemExit
67 exit(0)
68 rescue Interrupt
69 exit!(1)
70 rescue Exception => e
71 begin
72 msg = _toUTF8(e.class.inspect) + ': ' +
73 _toUTF8(e.message) + "\n" +
74 "\n---< backtrace of Ruby side >-----\n" +
75 _toUTF8(e.backtrace.join("\n")) +
76 "\n---< backtrace of Tk side >-------"
77 if TkCore::WITH_ENCODING
78 msg.force_encoding('utf-8')
79 else
80 msg.instance_variable_set(:@encoding, 'utf-8')
81 end
82 rescue Exception
83 msg = e.class.inspect + ': ' + e.message + "\n" +
84 "\n---< backtrace of Ruby side >-----\n" +
85 e.backtrace.join("\n") +
86 "\n---< backtrace of Tk side >-------"
87 end
88 fail(e, msg)
89 end
90 end
91
92 def initialize(namespace = nil)
93 if namespace.kind_of?(TkNamespace)
94 @namespace = namespace
95 elsif namespace == nil
96 @namespace = TkNamespace.new('::') # global namespace
97 else
98 @namespace = TkNamespace.new(namespace)
99 end
100 @path = @namespace.path
101
102 @msgcat_ext = '.msg'
103 end
104 attr_accessor :msgcat_ext
105
106 def method_missing(id, *args)
107 # locale(src, trans) ==> set_translation(locale, src, trans)
108 loc = id.id2name
109 case args.length
110 when 0 # set locale
111 self.locale=(loc)
112
113 when 1 # src only, or trans_list
114 if args[0].kind_of?(Array)
115 # trans_list
116 #list = args[0].collect{|src, trans|
117 # [ Tk::UTF8_String.new(src), Tk::UTF8_String.new(trans) ]
118 #}
119 self.set_translation_list(loc, args[0])
120 else
121 # src
122 #self.set_translation(loc, Tk::UTF8_String.new(args[0]))
123 self.set_translation(loc, args[0])
124 end
125
126 when 2 # src and trans, or, trans_list and enc
127 if args[0].kind_of?(Array)
128 else
129 #self.set_translation(loc, args[0], Tk::UTF8_String.new(args[1]))
130 self.set_translation(loc, *args)
131 end
132
133 when 3 # src and trans and enc
134 self.set_translation(loc, *args)
135
136 else
137 super(id, *args)
138 # fail NameError, "undefined method `#{name}' for #{self.to_s}", error_at
139
140 end
141 end
142
143 # *args ::= form, arg, arg, ...
144 def self.translate(*args)
145 dst = args.collect{|src|
146 tk_call_without_enc('::msgcat::mc', _get_eval_string(src, true))
147 }
148 Tk.UTF8_String(sprintf(*dst))
149 end
150 class << self
151 alias mc translate
152 alias [] translate
153 end
154 def translate(*args)
155 dst = args.collect{|src|
156 @namespace.eval{tk_call_without_enc('::msgcat::mc',
157 _get_eval_string(src, true))}
158 }
159 Tk.UTF8_String(sprintf(*dst))
160 end
161 alias mc translate
162 alias [] translate
163
164 def self.maxlen(*src_strings)
165 tk_call('::msgcat::mcmax', *src_strings).to_i
166 end
167 def maxlen(*src_strings)
168 @namespace.eval{tk_call('::msgcat::mcmax', *src_strings).to_i}
169 end
170
171 def self.locale
172 tk_call('::msgcat::mclocale')
173 end
174 def locale
175 @namespace.eval{tk_call('::msgcat::mclocale')}
176 end
177
178 def self.locale=(locale)
179 tk_call('::msgcat::mclocale', locale)
180 end
181 def locale=(locale)
182 @namespace.eval{tk_call('::msgcat::mclocale', locale)}
183 end
184
185 def self.preferences
186 tk_split_simplelist(tk_call('::msgcat::mcpreferences'))
187 end
188 def preferences
189 tk_split_simplelist(@namespace.eval{tk_call('::msgcat::mcpreferences')})
190 end
191
192 def self.load_tk(dir)
193 number(tk_call('::msgcat::mcload', dir))
194 end
195
196 def self.load_rb(dir)
197 count = 0
198 preferences().each{|loc|
199 file = File.join(dir, loc + self::MSGCAT_EXT)
200 if File.readable?(file)
201 count += 1
202 eval(open(file){|f| f.read})
203 end
204 }
205 count
206 end
207
208 def load_tk(dir)
209 number(@namespace.eval{tk_call('::msgcat::mcload', dir)})
210 end
211
212 def load_rb(dir)
213 count = 0
214 preferences().each{|loc|
215 file = File.join(dir, loc + @msgcat_ext)
216 if File.readable?(file)
217 count += 1
218 @namespace.eval(open(file){|f| f.read})
219 end
220 }
221 count
222 end
223
224 def self.load(dir)
225 self.load_rb(dir)
226 end
227 alias load load_rb
228
229 def self.set_translation(locale, src_str, trans_str=None, enc='utf-8')
230 if trans_str && trans_str != None
231 trans_str = Tk.UTF8_String(_toUTF8(trans_str, enc))
232 Tk.UTF8_String(tk_call_without_enc('::msgcat::mcset',
233 locale,
234 _get_eval_string(src_str, true),
235 trans_str))
236 else
237 Tk.UTF8_String(tk_call_without_enc('::msgcat::mcset',
238 locale,
239 _get_eval_string(src_str, true)))
240 end
241 end
242 def set_translation(locale, src_str, trans_str=None, enc='utf-8')
243 if trans_str && trans_str != None
244 trans_str = Tk.UTF8_String(_toUTF8(trans_str, enc))
245 Tk.UTF8_String(@namespace.eval{
246 tk_call_without_enc('::msgcat::mcset',
247 locale,
248 _get_eval_string(src_str, true),
249 trans_str)
250 })
251 else
252 Tk.UTF8_String(@namespace.eval{
253 tk_call_without_enc('::msgcat::mcset',
254 locale,
255 _get_eval_string(src_str, true))})
256 end
257 end
258
259 def self.set_translation_list(locale, trans_list, enc='utf-8')
260 # trans_list ::= [ [src, trans], [src, trans], ... ]
261 list = []
262 trans_list.each{|src, trans|
263 if trans && trans != None
264 list << _get_eval_string(src, true)
265 list << Tk.UTF8_Stirng(_toUTF8(trans, enc))
266 else
267 list << _get_eval_string(src, true) << ''
268 end
269 }
270 number(tk_call_without_enc('::msgcat::mcmset', locale, list))
271 end
272 def set_translation_list(locale, trans_list, enc='utf-8')
273 # trans_list ::= [ [src, trans], [src, trans], ... ]
274 list = []
275 trans_list.each{|src, trans|
276 if trans && trans != None
277 list << _get_eval_string(src, true)
278 list << Tk.UTF8_String(_toUTF8(trans, enc))
279 else
280 list << _get_eval_string(src, true) << ''
281 end
282 }
283 number(@namespace.eval{
284 tk_call_without_enc('::msgcat::mcmset', locale, list)
285 })
286 end
287
288 def self.def_unknown_proc(cmd=Proc.new)
289 TkMsgCatalog::UNKNOWN_CBTBL[TkCore::INTERP.__getip]['::'] = cmd
290 end
291 def def_unknown_proc(cmd=Proc.new)
292 TkMsgCatalog::UNKNOWN_CBTBL[TkCore::INTERP.__getip][@namespace.path] = cmd
293 end
294 end
295
296 TkMsgCat = TkMsgCatalog