Multilingualism for Lambda MOOs
This document is the product of a four year experiment with
bilingualism at SchoolNet MOO (now called 'Moo Canada, eh?'). We were
forced to add bilingualism (English and French) to our MOO because it
was sponsored by the Canadian federal government. The code behind the
bilingual capability was excellent, and can be adapted to any MOO. Our
problem was that despite pouring massive amounts of time into
translating the core, we never attracted more than a handful of French
Our multilingualism is based around a simple tag:
"This is a normal unilingual string."
(All users see that string normally.)
"<<This is English.&&This is French>>"
(English users see the first half, French the other)
(In theory we can add as many languages as we need.)
"<<I am&&Je suis>> Fred."
(Strings can have multilingual and unilingual sections)
"<<I am&&Je suis>> Fred <<and I am here&&et je suis ici>>."
(Strings can have any number of sections)
"<<<<John&&Jean>> is here&&<<John&&Jean>> est ici>>."
(Sections can even be nested - rarely used)
There are five things that have to happen on a MOO to implement this multilingualism:
- A new property called 'language' has to be added to $player. If this
property is 1, then the player is English, 2 is French and so on. A
setting of 0 means that the player has no language and wants to see all
the tags literally (very useful when debugging). One way for players to
set their language property is to use
@english/@anglais/@french/@francais verbs on them, however the main way
is using aliases to $login:connect. If the user logs in using 'co*nnect'
then their language prop is set to 1; if the user logs in using
'ac*cede' then their language prop is set to 2.
- The player's language property determines what language
$player:tell() shows them when it is given a multilingual string. All
you have to do is insert this line somewhere in the verb (after the
tostr() and before the this:notify()):
text = $xlingual:get_lang(text, this.language);
- A new utility called $xlingual needs to be created. The following verbs should be added:
||Takes a string and returns a list of strings, one for each language.
||Takes one string in each language as arguments, and returns a multilingual string.
||Takes a multilingual string, and returns a string containing only one language.
||Same as :get_lang, except a bit slower, and can handle recursively tagged strings.
||Private verb for :get_lang_rec.
||Another private verb for :get_lang_rec.
- A number of utilities and system verbs should be made bilingually aware.
Here are some sample verbs that demonstrate multilingual coding techniques:
Selected verbs from $string_utils.
Selected verbs from $time_utils.
- All text everywhere on the MOO needs to be translated into your new
language(s) if you want people to see it in their preferred language. Have fun!
Prepositions need to be made multilingual. While this could be
done in-DB by the parser, it is much easier to make a small edit
to the server and recompile. Adding these prepositions to the MOO
server is very painless. Before anything else, write out what
you want for prepositions. Go through the list of english prepositions
by looking at $code_utils.prepositions and determine what you would
like to change.
Edit the db_verbs.c source file. Scroll down about 45 lines to
encounter the struct const char *prep_list. Read any notes
above this structure, as they contain some useful information.
Add your prepositions to each index of this list, seperated by
/'s from the current prepositions. For example, change "at/to" to
"at/to/a" to add 'a' as a preposition with at and to. You may now
do commands like '@chown object a user' inside the MOO.
Once you have completed that, recompile the MOO. How to do that
is well beyond the scope of this document, but a simple 'make' should
work on a UNIX-based system. Shutdown the MOO, copy the executable
'moo' files to where it's needed, and start it up again. Pray,
or meditate, or hope, or wish, or believe.
After the new server is rebooted, check the props on $code_utils
and make whatever updates are required.
If you are running a web server from your MOO, you will also need to
create a mechanism for people to select their language. One simple way
is to add /en, /fr or some other flag to the beginning of all your URLs.
This mechanism works well, but makes it more difficult to create links,
since they either have to be relative or they have to take the current
language into account. Another solution is to map two domains onto the
same IP (e.g. www.moo.org & www-fr.moo.org) and watch for the 'Host:'
header that comes from the browser. This mechanism also works well, but
older browsers like Mosaic and Lynx don't support 'Host:' so they would
be unable to switch out of the default language. Another very good
approach is to open a separate web port for each language. Unfortunately
it was unacceptable for us since evil firewalls prevented us from using
anything except port 80.
Another stumbling block is that most languages use special characters
such as accents. In the interest of web ports, Pueblo users and ease of
programming, we opted to encode these characters in standard HTML
notation (e.g. "é" is "é").
Sending these strings to web ports and Pueblo users is trivial; one
just sends it raw, and the user's application will render the character.
Sending these strings to regular telnet users is a bit more tricky. The
notify verb has to replace all accent codes with their binary
equivalents (e.g. "é" to "~E9") then notify() the string
while in binary mode. Of course some people are unable to receive binary
data, so there must also be an option that simply replaces the accent
with the stripped version of the character (e.g. "é" to "e").
Moo Canada (at the time called SchoolNet MOO) made a number of costly
mistakes while stumbling around trying to make the system work. Don't do
All code here belongs to the SchoolNet
section of Industry Canada. They retain the intellectual property
rights. However, they have made it very clear on multiple occasions that
they no longer have the slightest interest in any of this. Therefore it
is appropriate to consider everything here to effectively be Public
- Our first successful bilingual tags were based on a simple flag
that separated English from French. The string "Hello" is unilingual and
the string "Hello#*#Bonjour" is bilingual (#*# is an Ascii icon of the
Canadian flag). We used this mechanism for several years, but eventually
had to trash it because it was far too painfull to add two strings
together. Instead of simply string1 + string2 we had to do
$xlingual:get_lang(string1, 1) + $xlingual:get_lang(string2, 1) +
"#*#" + $xlingual:get_lang(string1, 2) + $xlingual:get_lang(string2,
2) (and that is just bilingual, not multilingual). Failure to use
this elaborate process would result in the string getting truncated
after the first #*#. One can imagine how many ticks were being consumed
just to print out simple messages.
- Web browsers send an 'Accept-Language:' header that lists (in order of
preference) the languages that the user understands. This mechanism seems
ideal as a way to determine what language to render the requested web page
in, indeed it is designed for exactly what we wish to do. Unfortunately it
is completely unsatisfactory since most users don't set (or don't know how
to set) their browser's preferred language. We switched to using /eng and
/fr, then eventually migrated to using two different domains.
- Of course the worst mistake we made was attempting to make our MOO
bilingual in the first place. Circumstances for other MOOs may be
different, but with the benefit of hindsight we see that we probably would
have been better off creating a separate all-French MOO than trying to
make a dual-language MOO. French users on our system were grossly
outnumbered by English users, so they nearly always had to speak in
English if they wanted to communicate. As a result we only got users who
were comfortable in English, meaning there was never a need for most of
our population to create French content.
April 21, 1999
For more information you can contact the
Moo Canada wizzen at