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