allegro-cl archives 1997-2-5 | home index prev H thread prev K thread next J next L |
From: Matthew_Haine Subject: How to do truly multilingual ACL apps Date: 1997-2-5 17:05 Earlier I asked the list for experiences with multilingual ACL apps. I didn't get any responses that were terribly informative, but I was able to solve the problem myself eventually, so I'll give a bullet-point overview of the important points. My goal was to allow the end user to be able to localize my program (through the use of a built-in editor) without ever having to see the code. Here is how I did it: * Don't do any string surgery on text that will be displayed. (You never know where characters begin and end in those pesky multibyte fonts.) * As long as all text goes to the screen from draw-string-in-box calls, Windows will handle all of the foreign-language fonts. Just let the user choose the fonts, since ASCII might not be the right character set. (I've tested this on Japanese, which is multibyte.) * How do you know how big to make the boxes? As it turns out, draw-string-in-box can be modified to also act as a "measure-string-in-box" function. The code follows as risk::dsib3. DSIB is based on a call to the windows function "drawText". Perusing MSDN reveals that drawText returns the HEIGHT of the text and has a flag for NOT DRAWING. That's all that's needed. * Don't hard-code displayed text in your code. But, since it can get tedious to maintain a separate file of string-constants. You can define a character reader macro like this: ;; Ignore the defframe macro--read it like a modified defclass macro. (defframe named-mixin () ((display-name1 :no-accessor? T :editable? string :watched? T) (display-name2 :no-accessor? T :editable? string :watched? T)) :mixin? T) (defframe text-frame (named-mixin) ()) (defmethod display-name ((frame named-mixin)) (or (slot-value frame 'display-name2) (slot-value frame 'display-name1))) (defun ml-lookup (token) ;; 109 bytes (display-name (gethash token *text-frame-hash*))) (defun |#"-reader| (stream subchar arg) (declare (ignore subchar arg)) ;; 307 bytes ;; Back up so we start at the first quote (unread-char stream) (let* ((str (read stream t nil)) (frame-symbol (intern str "RISK"))) (unless (gethash token *text-frame-hash*) (make-instance 'text-frame :name frame-symbol :display-name1 str)) `(ml-lookup ',frame-symbol))) (set-dispatch-macro-character #\# #\" #'|#"-reader|) * Provide the user with an editor that allows the user to enter his or her own string in the display-name2 slot. Also allow the user the ability to choose display fonts. ;; CODE FOR DRAW-STRING-IN-BOX (defun risk::dsib3 (stream string box &key (h-just :left) (v-just :top) (measure? NIL) (wrap? T) (no-clip? T) (remember? T)) (when (and remember? (not measure?) (not risk::*redrawing?*) (risk::memorex-p stream)) (push (list box (risk::my-copy-graphics-context stream) #'risk::dsib3 string box :h-just h-just :v-just v-just :wrap? wrap? :no-clip? no-clip?) (risk::memory stream))) (let* ((length (length-vector string)) (format-word (ilogior (if (or measure? wrap?) DT_TOP (case v-just (:center DT_VCENTER) (:bottom DT_BOTTOM) (t DT_TOP))) (case h-just (:right DT_RIGHT) (:center DT_CENTER) (t DT_LEFT)) (if measure? DT_CALCRECT 0) (if no-clip? DT_NOCLIP 0) DT_NOPREFIX (if wrap? DT_WORDBREAK DT_SINGLELINE))) (result (ccallocate int))) ;; simple DrawText call ;; Use slot-value here since (setf dirty-p) is not yet defined (setf (slot-value stream 'dirty-p) t) ;; due to a bug in DrawText we must set text align first (windows-graphics-call stream SetTextAlign #.(ilogior TA_LEFT TA_TOP ta_noupdatecp)) (windows-graphics-call stream DrawText string length box format-word result) ;; restore text alignment (windows-graphics-call stream SetTextAlign #.(ilogior TA_LEFT TA_TOP TA_UPDATECP)) (cond (measure? box) ((or wrap? (not (eq :center v-just))) (setf (box-bottom box) (+ result (box-top box)))) (t (+ result (box-top box)))) )) |