From: drrobot
Subject: Using Japanese and English strings, encodings
Date: 
Message-ID: <1144931619.251700.40060@t31g2000cwb.googlegroups.com>
Hi! I'm new to lisp, a mediocre-but-trying-to-improve hobbyist
programmer, and just joined this newsgroup. If anyone can point me to
the answers for the following questions, I would greatly appreciate it.


1. In summary: How do I get a macro to turn into NOTHING at all (not
even NIL), or turn into multiple values (like is returned from the
VALUES function)?

A little explanation as to why I want this (If you know the Right
Way(TM) to do the following, please enlighten me):

I've been working a few small programs that use both Japanese and
English, and I keep wishing I could closely, reliable, and simply
couple two strings together (one in english and one in Japanese). In
the simplest case, I (naively?) dream of writing something like this:

(princ (eng "This is an english sentence.")
       (jap "これは日本語の文章です。"))

Where ENG and JAP are little macros such that setting something to 'ENG
before the macro is evaluated will make it come out as:

(princ "This is an english sentence")

or in the case of a japanese compile, the sentence tagged with JAP.

So far, I have been getting by with using *features* and #+jap or #+eng
to remove before processing, but since sometimes I want to leave the
string as part of some larger, nested list structure, it gets to be a
pain. Or, *shudder*, what if I wanted to concatenate and print BOTH
strings?

Other times, I use something like the following, where LANG is the
macro that reads the CAR of each argument, and then evaluates to the
appropriate string.

(lang (eng "blahblah")
      (jap "ブラブラ"))  --> "blahblah"

This is a little better, as the LANG macro can even warn me if zero or
more than one string was found. But still, it's a bit annoying having
to always write that LANG, and it doesn't help me when I have certain
list structures. And of course, I can think of times when I have two or
more values I'd like to use, like the following,

(format t "Current Language: ~A~%My String:~A"
        (lang (eng "English" "This is a waste of time.")
              (jap "日本語"
"こりゃ時間の無駄だよな。")))

And I'm left scratching my head as to an elegant way to do this.

What is the elegant, lispy way to do multilanguage programs like this?

2. I ran into a little problem with the (very simple) CGI library I am
using. It keeps screwing up the EUC-JP encoding of any parameters I
pass the script. I traced the problem down to where the CGI script uses
CODE-CHAR to convert the correct EUC-JP encoded byte(s) into incorrect
UTF-8 byte(s). I think CLISP is using UTF-8 internally, despite my
command-line orders to use EUC-JP for everything. Everything else works
just fine.

How do I convince CODE-CHAR to use the EUC-JP character set? Or,
skipping over CODE-CHAR entirely, how do I use WRITE-BYTE to just write
the raw byte to a character stream and ignore what sort of (multi-byte
encoded) character it is?

3. Any recommendations on good, lightweight CGI/HTML libraries that
would work with EUC-JP?

From: Kaz Kylheku
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <1144949002.272269.150410@e56g2000cwe.googlegroups.com>
drrobot wrote:
> I've been working a few small programs that use both Japanese and
> English, and I keep wishing I could closely, reliable, and simply
> couple two strings together (one in english and one in Japanese). In
> the simplest case, I (naively?) dream of writing something like this:

> (princ (eng "This is an english sentence.")
>        (jap "これは日本語の文章です。"))

Hmm, "Kore-wa Nihongo-no bunshyou desu".  Is that the right
translation? Does translation have to preserve truth value and
self-reference? Or is it acceptable to lie and just say "This is an
English sentence" ("これは英語の文章です"). :)

Maybe this should depend on the value of *PRINT-CIRCLE*. :)

和気裸白 (kazu ki-ra-haku :) has been learning some Japanese
lately. I made a nice little web-based program for drilling oneself on
Kanji meanings, using edict + CLISP + araneida. In a few months, I
memorized at least one meaning of 1200 of the beasties. Crazy!

> Where ENG and JAP are little macros such that setting something to 'ENG
> before the macro is evaluated will make it come out as:

> (princ "This is an english sentence")

The above syntax won't work because the reader will turn both macros
into some kind of object. So you are calling PRINC with two arguments.

> or in the case of a japanese compile, the sentence tagged with JAP.

> So far, I have been getting by with using *features* and #+jap or #+eng

You're on the right track if you want it to work that way, because you
do have to do it at read-time. But there are pitfalls to doing it a
read time. It's hard to dynamically load in new language definitions,
for instance!

It would be much better if you had a single LANG macro, as other
posters have pointed out. Moreover, that macro could translate to code
which does a dynamic lookup.

Really, you should have only the English string in the expression (or
whatever is the preferred language for coding). The translations would
be in an external catalog. E.g.

  (lang "Settings")

LANG doesn't have to be a macro, just a function. Perhaps a memoized
one. What it will do is search for that string in the program's
database, and fetch the appropriate translation.

Note that CLISP is internationalized using some extensions that are
based on top of GNU gettext, and these are available for
internationalizing user programs. Are you after a portable solution or
not?

> 2. I ran into a little problem with the (very simple) CGI library I am
> using. It keeps screwing up the EUC-JP encoding of any parameters I
> pass the script. I traced the problem down to where the CGI script uses
> CODE-CHAR to convert the correct EUC-JP encoded byte(s) into incorrect
> UTF-8 byte(s). I think CLISP is using UTF-8 internally, despite my
> command-line orders to use EUC-JP for everything. Everything else works
> just fine.

CLISP has some special variables for overriding the encoding used for
streams. Internally, strings are 16 bit characters, I think. Or
something like that. The encoding comes into play during I/O.

Code that reads octets from a socket and then uses CODE-CHAR bypasses
the encoding system in CLISP's streams, so of course it will break.

> How do I convince CODE-CHAR to use the EUC-JP character set? Or,
> skipping over CODE-CHAR entirely, how do I use WRITE-BYTE to just write
> the raw byte to a character stream and ignore what sort of (multi-byte
> encoded) character it is?

But characters are not necessarily bytes in Lisp. CLISP has functions
for converting between vectors of bytes and strings, through encodings:


  (EXT:CONVERT-STRING-FROM-BYTES vector encoding &KEY :START :END)
  (EXT:CONVERT-STRING-TO-BYTES string encoding &KEY :START :END)

The CGI script could maybe be hacked to use this instead of CODE-CHAR.

The encoding parameter comes from EXT:MAKE-ENCODING.

If you are dealing with encodings, its probably best to code that into
your CLISP program rather than to try to globally override it, since
that program still sits in an environment full of ordinary data. E.g.
in my Kanji learning program, it's necessary to read EDICT. So what it
does is locally switch to EUC-JP to read that data. It is then parsed
with the help of CL-PPCRE and turned into a Lisp data structure which
is written out to disk, using UTF-8. The next time the program is run,
it looks for that "compiled" version which loads much faster just using

LOAD.

Here is the function that I use to load a text file in EUC-JP:

(defun read-euc-jp-file (name)
  (letf ((*default-file-encoding* (make-encoding :charset
'charset:euc-jp)))
    (with-open-file (f name :direction :input)
      (loop for line = (read-line f nil nil)
            while line
            collecting line))))

I use short identifiers for some of these CLISP extensions, because I
put this in my package definition:

  (:import-from #:ext #:letf #:make-encoding)
  (:import-from #:custom #:*default-file-encoding*)

Note that it's LETF not LET in the above, because
*DEFAULT-FILE-ENCODING* isn't an ordinary special variable. It's
unfortunately a symbol macro.
From: drrobot
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <1145023977.835801.261390@i40g2000cwc.googlegroups.com>
Kazu, I would love to see your little kanji-study program. A two years
ago, when I first started learning Japanese, I did the same sort of
thing (although I hadn't heard of Lisp at the time and did it all in
Perl/XML), also using Jim Breen's EDICT, Kanjidic, and the list of
example sentences.

Thanks for the background on the character encodings.
From: anselmus
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <9pkv32dbapi479a0c7gn07nfoat46lksqu@4ax.com>
"drrobot" <·······@gmail.com> wrote:

>Kazu, I would love to see your little kanji-study program.

Me too, I would like to see it, if possible.
GC
From: Pascal Costanza
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <4a7c5hFroddlU1@individual.net>
drrobot wrote:
> Hi! I'm new to lisp, a mediocre-but-trying-to-improve hobbyist
> programmer, and just joined this newsgroup. If anyone can point me to
> the answers for the following questions, I would greatly appreciate it.
> 
> 
> 1. In summary: How do I get a macro to turn into NOTHING at all (not
> even NIL), or turn into multiple values (like is returned from the
> VALUES function)?
> 
> A little explanation as to why I want this (If you know the Right
> Way(TM) to do the following, please enlighten me):
> 
> I've been working a few small programs that use both Japanese and
> English, and I keep wishing I could closely, reliable, and simply
> couple two strings together (one in english and one in Japanese). In
> the simplest case, I (naively?) dream of writing something like this:
> 
> (princ (eng "This is an english sentence.")
>        (jap "これは日本語の文章です。"))
> 
> Where ENG and JAP are little macros such that setting something to 'ENG
> before the macro is evaluated will make it come out as:
> 
> (princ "This is an english sentence")
> 
> or in the case of a japanese compile, the sentence tagged with JAP.

Are you sure that these will be the only languages? Maybe you want to 
use more languages later on? Then this becomes unwieldy.

Why not use constants, like in (princ +sentence-in-current-language+)? 
Then you can collect all the constants for one language in one file, and 
easily switch and add new languages. Or put the translations into 
hashtables, so you could even change them at runtime.

 > (lang (eng "blahblah")
 >       (jap "ブラブラ"))  --> "blahblah"

Use keyword parameters, like this:

(lang :eng "blahblah"
       :jap "ブラブラ")  --> "blahblah"

Looks somewhat nicer, I think.


Pascal


-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Peter Seibel
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <m2irpd14s6.fsf@gigamonkeys.com>
"drrobot" <·······@gmail.com> writes:

> Hi! I'm new to lisp, a mediocre-but-trying-to-improve hobbyist
> programmer, and just joined this newsgroup. If anyone can point me to
> the answers for the following questions, I would greatly appreciate it.
>
>
> 1. In summary: How do I get a macro to turn into NOTHING at all (not
> even NIL), or turn into multiple values (like is returned from the
> VALUES function)?

I'm not sure if anyone else answered this specific question. In case
not, here's the answer: You can't.

> (princ (eng "This is an english sentence.")
>        (jap "����͓�{��̕��͂ł��B"))

So, here's a very brief sketch of a set of macros for doing
localization. This is probably ill-conceived in many ways and suffers
from one known bug due to esoterica of compile-time vs run-time
environments. It may be possible to work that out but I don't have the
time at the moment.

  ;; Some of these EVAL-WHENs wouldn't be needed if the test code was in
  ;; a separate file from the variabel and macro definitons.
  (eval-when (:compile-toplevel :load-toplevel :execute)
    (defvar *l10n-messages* (make-hash-table :test 'equal))
    (defvar *languages* (make-hash-table))
    (defvar *language* 'english))

  (defmacro deflanguage (name)
    `(eval-when (:compile-toplevel :load-toplevel :execute)
       (setf (gethash ',name *languages*) (make-hash-table :test 'equal))))

  ;; Default language.
  (deflanguage english)

  (defmacro defmessage (fmt language (&rest args) &body body)
    `(setf (gethash ,fmt (gethash ',language *languages*)) #'(lambda (,@args) ,@body)))

  (defmacro defsimple-message (fmt language (&rest args) localized-fmt)
    `(defmessage ,fmt ,language (,@args) (format nil ,localized-fmt ,@args)))

  (defmacro l10n (fmt &rest args)
    (let ((parameters (as-symbols args)))
      ;; This is not quite right since the compilation environment may
      ;; not be the one we want to effect. But we really do want to do
      ;; this at compile time, not when the form the l10n form expands
      ;; into is run. Not sure if there's a better way.
      (setf (gethash fmt *l10n-messages*) (list* fmt parameters))
      `(progn
         (eval-when (:compile-toplevel :load-toplevel :execute)
           (defmessage ,fmt english (,@parameters) (format nil ,fmt ,@parameters)))
         (funcall (gethash ,fmt (gethash *language* *languages*)) ,@args))))

  (defmacro with-language ((language) &body body)
    `(let ((*language* ',language))
       ,@body))

  (defun as-symbols (args)
    (loop for arg in args collect (gensym)))

  (defun dump-messages ()
    (maphash #'(lambda (k v) (format t "~a => ~s~%" k v)) *l10n-messages*))

  (defun clear-messages ()
    (clrhash *l10n-messages*))

Anyway, the idea is that whenever you want to generate a string that
may need to be localized you use the L10N macro which is more or less
equivalent to FORMAT with NIL as a first argument. However it also (at
compile time) squirrels away the string and the number of arguments
needed so you can define functions that generate eqivalent output in
other languages. For instance:

  CL-USER> (l10n "I have written ~d book~:p." 1)
  "I have written 1 book."

Now I can check what messages I may need to provide localizations for:

  CL-USER> (dump-messages)
  I have written ~d book~:p. => ("I have written ~d book~:p." #:G786)
  NIL

Okay, so define a new language:

  (deflanguage pig-latin)

and define how to render than message in my new language:

  (defmessage "I have written ~d book~:p." pig-latin (x) 
    (format nil "Iway avhay ittentray ~d ook~:pbay." x))

And a test:

  CL-USER> (defun test () (l10n "I have written ~d book~:p." 1))
  TEST
  CL-USER> (test)
  "I have written 1 book."
  CL-USER> (with-language (pig-latin) (test))
  "Iway avhay ittentray 1 ookbay."

Obviously the DEFMESSAGE forms can be arbitrarily complex, using
arbitrary Lisp code to generate appropriate gramatical output.
Obviously using the format string as the key to look up the message
function is a bit fragile--if you fix a typo in the English message
you then have to go fix all the localized versions of that message.
But that's could be construed as a feature, as long as the system can
tell you which messages need to be localized in a particular language.
At least this system allows the original code to be written in a way
that works for the default language with hardly any extra effort and
automatically identifies strings that needs to be localized.

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: drrobot
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <1145026873.615443.162290@i40g2000cwc.googlegroups.com>
Wow! I never expected that the guy who wrote one of my favorite books
on lisp would also be the one answering my questions! Many thanks on
the book; I refer to it often.

I'm a bit surprised that Lisp macros have to evaluate always to a list
or a single value; I was sure there was some sort of way of getting
them to evaluate to multiple or zero values like the ordinary functions
they (superficially) resemble.

As a bit of background may clarify why I wanted to do this. I'm an
american grad student in Japan right now, and I often find myself
writing documents twice, once in english, and once in japanese, over
and over again, and getting annoyed at the constant back-and-forth
revisions required to both documents.

I found it a useful time-saver to write all my research reports as Lisp
source file, and use some scripts I wrote a while ago to transform the
lisp source file into something  else (either LaTeX document or
HTML/XML) using some very, very simple macros and some perl scripts
from before I came to Japan.

Lisp's nice treatment of code and data is especially convenient, as
sometimes I need to write more of a "program" for engineering problems,
where the 'lookup table' approach to localization works great because I
rarely need to change the program's strings and if they are seperated
in different files it doesn't matter so much. Other times, however, I
need to write more of a "document" which works for multiple languages,
and since the lisp-expressions->LaTeX stuff I wrote doesn't know
anything about Japanese, I was hoping a nice macro hack could save me
the trouble of having to mess with my old code again.

So basically, I was willing to forgive something that was a little
ugly/weird, if it meant that I could continue using just simple lists
that my simple scripts could process as both executable code when
convenient, or other times treat it like the static data, as XML in
s-expression form. Which is why I'm still sort of mixing my solutions
to the multiple-language problem.

Anyway, thanks again for your help. I like the dump-messages idea, that
would be nice for those cases where it's tolerable to put the
translations in seperate places.
From: Peter Seibel
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <m264lc19b7.fsf@gigamonkeys.com>
"drrobot" <·······@gmail.com> writes:

> Wow! I never expected that the guy who wrote one of my favorite books
> on lisp would also be the one answering my questions! Many thanks on
> the book; I refer to it often.

Glad you like it!

> As a bit of background may clarify why I wanted to do this. I'm an
> american grad student in Japan right now, and I often find myself
> writing documents twice, once in english, and once in japanese, over
> and over again, and getting annoyed at the constant back-and-forth
> revisions required to both documents.
>
> I found it a useful time-saver to write all my research reports as
> Lisp source file, and use some scripts I wrote a while ago to
> transform the lisp source file into something else (either LaTeX
> document or HTML/XML) using some very, very simple macros and some
> perl scripts from before I came to Japan.

Ah, so if it was me, I'd solve that probem with my Markup library[1].
Then you could write something like this:

  \en{This is an English sentence.}\pl{Isthay isway anway Igpay
  Atinlay entencesay.}

Or you might choose to alternate at the paragraph level. Anyway, you
can think of Markup as a sort of alternative sexp syntax optimized for
stuff that is mostly text. You can then use Common Lisp to manipulate
the resulting sexps however you want though there are already backends
for generating HTML and PDF. (I believe I have a patch from someone
that provides a LaTeX backend but I haven't had a chance to look at it
yet.)

-Peter

[1] <http://www.gigamonkeys.com/lisp/markup/>

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: Pascal Bourguignon
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <871ww0kpme.fsf@thalassa.informatimago.com>
"drrobot" <·······@gmail.com> writes:
> [...]
> I'm a bit surprised that Lisp macros have to evaluate always to a list
> or a single value; I was sure there was some sort of way of getting
> them to evaluate to multiple or zero values like the ordinary functions
> they (superficially) resemble.

Usually, you can return from a macro a side-effect-free form, like
NIL, and it will be ignored at the "call" site.

(defconstant +debug+ nil)
(defmacro when-debug (&body body)
  (when +debug+ `(progn ,body)))

(macroexpand '(when-debug (print :debugging))
--> NIL ;
    T

Therefore, when you use it:

   (progn 
      (print 'working)
      (when-debug (print :debugging))
      (print 'hard!))

it expands to:

#+clisp (ext:expand-form '(progn 
                             (print 'working)
                             (when-debug (print :debugging))
                             (print 'hard!)))
-->
(PROGN (PRINT 'WORKING) NIL (PRINT 'HARD!)) ; T                     
                        ^^^

but since evaluating NIL returns NIL with no side effect and it's
immediately ignored, the compiler doesn't generate code for it.

(disassemble (compile nil (lambda () 
                             (print 'working)
                             (when-debug (print :debugging))
                             (print 'hard!))))

Disassembly of function NIL
(CONST 0) = WORKING
(CONST 1) = HARD!
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
7 byte-code instructions:
0     (CONST&PUSH 0)                      ; WORKING
1     (PUSH-UNBOUND 1)
3     (CALLS1 132)                        ; PRINT  
5     (CONST&PUSH 1)                      ; HARD!
6     (PUSH-UNBOUND 1)
8     (CALLS1 132)                        ; PRINT
10    (SKIP&RET 1)
NIL

See?  Nothing between the PRINT and HARD!


Now, since you _imposed_ a usage pattern, the solution Common Lisp
provide is the _reader_ macros #+ and #-; but of course, this means
that you can only choose between the language when you _read_ the
_source_ of your program.

If you want to be able to select the language at run-time, then you
shouldn't impose this usage pattern, or if you insist, you must bear
the cost: you'd have to write your own parser and loader, since you're
not reading s-expr anymore.

[You could write a reader macro that would parse un-parenthesized
 tokens and build the form to select the language at run-time:

  (print #L (eng "Hello") (jap "こんにちは, コンニチハ"))

 You could try, but the reader macro function would need to look ahead
 to know when to stop reading #L forms, and since we can unread only
 one character on standard streams, we'd be stuck with such forms:

  (print #L (eng "Hello") (jap "こんにちは, コンニチハ") output-stream) ]

> As a bit of background may clarify why I wanted to do this. I'm an
> american grad student in Japan right now, and I often find myself
> writing documents twice, once in english, and once in japanese, over
> and over again, and getting annoyed at the constant back-and-forth
> revisions required to both documents.
>
> I found it a useful time-saver to write all my research reports as Lisp
> source file, and use some scripts I wrote a while ago to transform the
> lisp source file into something  else (either LaTeX document or
> HTML/XML) using some very, very simple macros and some perl scripts
> from before I came to Japan.


Here's the start of the source of my tri-lingual CV:

(defparameter pascal-bourguignon
  (quote
   (:resume
    (:person 
     (:name "Pascal BOURGUIGNON")
     (:nationality (:text (:en "(French)") (:fr "(Français)") (:es "(Frances)")))
     (:birth-date 2005 3 15)
     (:birth-place "Hayange, France")
     (:address "Apartado de correo 69 "
               "30380 La Manga del Mar Menor "
               "España")
     (:mail ····@informatimago.com")
     (:web "www.informatimago.com")
     (:phone "+34 968 140 492"))
    (:summary
     (:text (:en "Common Lisp Application Development "
                 "& Web application development (UncommonWeb).")
            (:fr "Développement d'applications en Common Lisp "
                 "& applications Web (UncommonWeb).")
            (:es "Desarrollo de aplicaciones Common Lisp "
                 "y aplicaciones Web (UncommonWeb)."))
     (:text (:en "OpenStep (MacOSX or GNUstep) & WebObject (GNUstepWeb) "
                 "Application Development")
            (:fr "Développement d'applications OpenStep (MacOSX ou GNUstep) "
                 "& WebObject (GNUstepWeb)")
            (:es "Desarrollo de aplicaciones OpenStep (MacOSX o GNUstep) "
                 "y WebObject (GNUstepWeb)."))
     (:text (:en "UNIX System & Application Development.")
            (:fr "Développement d'applications & développement système UNIX.")
            (:es "Desarrollo de aplicaciones y sistema UNIX."))
     (:text (:en "Internet UNIX Server Administration.")
            (:fr "Administration de serveurs Internet UNIX.")
            (:es "Administración de servidores Internet UNIX.")))
    (:skills
     (:category
      (:text (:en "Compilers;  "
                  "Operating Systems;  "
                  "Object-Oriented Development.")
             (:fr "Compilateurs;  "
                  "Systèmes d'exploitation;  "
                  "Développement orienté-objet.")
             (:es "Desarrollo de compiladores; "
                  "Desarrollo Orientado a Objetos; "
                  "Sistemas Operativos.")))
     (:category (:text (:en "Operating systems: ")
                       (:fr "Systèmes d'exploitation : ")
                       (:es "Sistemas operativos : "))
                (:list
                 "UNIX (Linux, BSD, MacOSX, Solaris) "
                 "MacOS "
                 (:text (:en "Programming (system & applications);")
                        (:fr "Programmation (système & applications); ")
                        (:es "Programación (Sistema & applicaciones); "))
                 (:text (:en "Unix system administration.")
                        (:fr "Administration de systèmes Unix.")
                        (:es "Administración de sistemas Unix."))))
     (:category (:text (:en "Internet:") (:fr "Internet :") (:es "Internet :"))
                (:flow
                 "SMTP"
                 "POP3"
                 "IMAP"
                 "HTTP/HTML/CGI"
                 "FTP"
                 "DNS"
                 "NFS" "..."))
     (:category (:text (:en "Programming Languages: ")
                       (:fr "Langages de programmation : ")
                       (:es "Lenguajes de programación : "))
                (:flow
                 "Common-Lisp"
                 "emacs-lisp"
                 "Smalltalk"
                 "Java"
                 "C/C++"
                 (:text (:en "& other OO, procedural & scripting languages.")
                        (:fr "& autres langages OO ou procéduraux.")
                        (:es "& otros lenguajes OO, procedural y de script."))))
     (:category (:text (:en "Development environment: ")
                       (:fr "Environnement de développement : ")
                       (:es "Entornos de desarrollo : "))
                (:list 
                 (:text (:en "UNIX: emacs, MacOSX/GNUstep development tools, "
                             "Standard UNIX & GNU development tools.")
                        (:fr "UNIX: emacs, outils de développement MacOSX "
                             "(Xcode) & GNUstep (Gorm); "
                             "Outils de développement standard UNIX & GNU.")
                        (:es "UNIX: emacs, Herramientas de desarrolo MacOSX "
                             "(Xcode) y GNUstep (Gorm); "
                             "Herramientas de desarrolo UNIX & GNU."))
                 (:text (:en "MacOS: CodeWarrior, MPW."))))
     (:category (:text (:en "CASE Tools: ")
                       (:fr "Ateliers de génie logiciel : ")
                       (:es "Heramientas CASE : "))
                (:list
                 (:text (:en "Objecteering UML (from Softeam SA); ")
                        (:fr "Objecteering UML (de Softeam SA); ")
                        (:es "Objecteering UML (de Softeam SA); "))
                 (:text (:en "ObjectTeam (from Cayenne Inc.); ")
                        (:fr "ObjectTeam (de Cayenne Inc.); ")
                        (:es "ObjectTeam (de Cayenne Inc.); "))
                 (:text (:en "OMTool (from Martin Marietta, Inc.). ")
                        (:fr "OMTool (de Martin Marietta, Inc.). ")
                        (:es "OMTool (de Martin Marietta, Inc.). "))))
     (:category (:text (:en "Methodologies: ")
                       (:fr "Méthodologies : ")
                       (:es "Metodologias : "))
                (:flow "UML" "OMT" "Booch"))
     (:category (:text (:en "Databases: ")
                       (:fr "Bases de données : ")
                       (:es "Bases de datos : "))
                (:flow "PostgreSQL" "MySQL" "Oracle" "Sybase")))
   ...)))

You can see the rest and the code I use to generate the HTML versions
of my CV at http://www.informatimago.com/cv.lisp  

It's rather Q&D, but it should give you some idea.  You can also fetch
the markup code Peter used to write "Practical Common Lisp", which
could be more practical for less structured documents, merging the
multi-lingual stuff with it.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

WARNING: This product attracts every other piece of matter in the
universe, including the products of other manufacturers, with a
force proportional to the product of the masses and inversely
proportional to the distance between them.
From: Alan Manuel K. Gloria
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <1145085805.013416.201590@i39g2000cwa.googlegroups.com>
Say, maybe you could use the #+ syntax?

http://www.lisp.org/HyperSpec/Body/sec_2-4-8-17.html
http://www.lisp.org/HyperSpec/Body/sec_24-1-2-1.html
http://www.lisp.org/HyperSpec/Body/var_stfeaturesst.html

something like:
(princ #+:eng "This is an english sentence"
      #+:jap "something that isn't english" )
From: drrobot
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <1145114194.413031.185090@e56g2000cwe.googlegroups.com>
Thanks, Pascal (cool name, by the way!). You're right about me trying
to impose a usage pattern that won't work with run-time selection of
language (which sometimes I just didn't care about). Really, I would
like to get better at playing along _with_ the lispy way of doing
things rather than attempt to reinvent the wheel...but for now I'm
still learning what the lispy way of doing things, and trying to figure
out in which directions lisp code distorts the most conveniently. It's
going to be a lot of trial and error, I think.

Coincidentally, if your CV is correct our birthdates are the same day
(March 15?), although I'm a fair bit younger. You got your Bacc. degree
the same year I was born...
From: Thomas A. Russ
Subject: Re: Using Japanese and English strings, encodings
Date: 
Message-ID: <ymifykcxidb.fsf@sevak.isi.edu>
"drrobot" <·······@gmail.com> writes:

> Hi! I'm new to lisp, a mediocre-but-trying-to-improve hobbyist
> programmer, and just joined this newsgroup. If anyone can point me to
> the answers for the following questions, I would greatly appreciate it.
> 
> 
> 1. In summary: How do I get a macro to turn into NOTHING at all (not
> even NIL), or turn into multiple values (like is returned from the
> VALUES function)?

Well, as you have discovered, macros need to return only a single value,
since that is the contract that they have with the compiler and
interpreter.  There is no way to get nothing returned, since even for
regular functions a return of (values) will show up a NIL if a value is
expected.

The best that you can do is to use PROGN to package up values.  You then
put however many return forms you need inside this.  Remember that only
the value of the last of the PROGN forms is returned, and that this will
be NIL if there are no forms present.

(PROGN)

(PROGN form1 form2 form3)

etc.

-- 
Thomas A. Russ,  USC/Information Sciences Institute