LSL MultiLingual Library
[ English ]
llSay(), llSetText(), llDialog()など、文字を出力する関数を動的に多言語化するライブラリ(lsl-ml.lib)と代替関数のパッケージです。GNU Lesser General Public Licenseのもとで配布しています。
概要
lsl-ml.libについて
編集可能な言語データと、それを出力する関数を組み込んだLSL拡張ライブラリです。言語データは次の8カ国語(2008年11月現在SecondLifeビュアがサポートしている全言語)に対応しています。また、それ以外の言語であっても、文字コードUTF-8で表すことができれば拡張することが可能です。
- ドイツ語(de)
- 米語(en-us)
- スペイン語(es)
- フランス語(fr)
- 日本語(ja)
- 韓国語(ko)
- ポルトガル語(pt)
- 中国語(zh)
これら8カ国語の言語データを、ライブラリ外のメインスクリプトから選択し、動的に出力することができます。また、その際、言語データ内の一部を他の文字列に置換して出力することも可能です。
代替関数について
代替関数の種類
文字列を出力する次の9つの代替関数を収録しています。
- mlSay() … llSay()の代替関数
- mlWhisper() … llWhisper()の代替関数
- mlShout() … llShout()の代替関数
- mlRegionSay() … llRegionSay()の代替関数
- mlOwnerSay() … llOwnerSay()の代替関数
- mlInstantMessage() … llInstantMessage()の代替関数
- mlSetText() … llSetText()の代替関数
- mlDialog() … llDialog()の代替関数
- mlLoadURL() … llLoadURL()の代替関数
これら代替関数をライブラリ外のメインスクリプト内で使用することで、lsl-ml.libを制御することができます。
拡張引数
全ての代替関数は、LSL標準の関数と同じ引数を必要とします。例えば、mlSay()は、llSay()と同様に「integer channel, string msg」の2つの引数を指定する必要があり、mlSetText()には、llSetText()と同様に「string msg, vector color, float alpha」の3つの引数が必要です。
これに加え、全ての代替関数は、共通して次の2つの引数を添えて呼び出します。
- string lang_code … 言語コード[de, en-us, es, fr, ja, ko, pt, zh]の指定
- list replacement … 置換文字列の指定(複数可)
string lang_codeは、lsl-ml.lib経由で出力する際の言語を指定する引数です。これは、ISO 639言語コード(de, en-us, es, fr, ja, ko, pt, zh)をそのまま指定します。llGetAgentLanguage(key id)関数がこの値を出力しますので、組み合わせると便利です。
list replacementは、lsl-ml.lib内で置換して出力する場合に利用する引数です。例えば、lsl-ml.lib内の言語データが「こんにちは、%sさん。」となっている状態で、list replacement = [“arz Nitely”]を指定すると、「こんにちは、arz Nitelyさん。」と出力されます。この置換は、1つの言語データ内において複数指定することができます。例えば、「今日は、%s年%s月%s日です。」に対し、list replacement = [“2008”, “11”, “29”]を指定すると、「今日は、2008年11月29日です。」と、前から順番に置換され出力されます。既にお察しとは思いますが、「%s」が置換されるというわけです。
置換する必要がない場合は、list replacement = []と、空のリストを指定します。
備考
言語データの分割
非常に大きな言語データを必要とする場合、LSLの仕様である「1スクリプトに対し64kb」というメモリ上限を上回ることもあり得ます。このような場合は、1つのオブジェクト内に分割した複数のlsl-ml.libを設置することで、この問題を回避することができます。
lsl-ml.libを利用した多言語環境の構築手順
1. 多言語化を意識せず通常通りスクリプティングする
初期段階では多言語化を全く意識する必要はありません。通常、ご自身が使っている単一言語において、スクリプティングからデバッグまでの一連の作業を済ませてください。このライブラリはLSLのデフォルト関数に非常によく似た代替関数を利用し、外部のlsl-ml.libを呼び出すことで多言語化を実現します。また、スクリプト本体とライブラリとの通信は、メインスクリプト → lsl-ml.libの一方向にのみ行います。そのため、必要最小限度の修正で後から多言語環境を容易に構築することが可能となっています。
2. lsl-ml.libを導入する
このページの下の方にソースコードがあります。メインスクリプトが入っているオブジェクト内に新しくスクリプトを作成し、名前を「lsl-ml.lib」に修正し、そのコードをそのままコピー&ペーストします。しかし、まだこの段階では、lsl-ml.libを編集する必要はありません。保存後はそのままウィンドウを閉じて構いません。
3. 代替関数に置き換える
まず、代替関数に置き換える前に、このページの下方にある代替関数をあらかじめ貼り付けておいてください。代替関数は、通常のユーザ関数と同じものですので、メインスクリプト内のdefault以前であればどの位置に置いても構いません。
この作業が終わったら、多言語化したい部分を代替関数に置き換えます。llSayであればmlSay、llInstantMessageであればmlInstantMessageといった具合に、全ての代替関数ははじめの1文字を「l」から「m」に書き換えるだけで第1ステップが終了します。
次に、拡張引数を指定します。lang_codeを動的に切り替えたい場合は、その部分にllGetAgentLangage(key id)関数などを組み込みます。
4. 言語データを編集する
最も労力を必要とする作業は恐らく言語データの編集でしょう。辞書や翻訳サイト等を利用し、各言語に翻訳したことばや文章を打ち込んでください。その際、置換が必要であれば適所に「%s」を入力します。
どうしても翻訳が不可能な言語欄は空欄に(もしくは、他言語で記入)しておき、手に入れたユーザに翻訳をゆだねるという方法でもいいのではないかと思います。その場合は、「この商品は多言語ライブラリであるlsl-ml.libを組み込んでいますが、○○語の翻訳は未完です。ご了承ください。」などとパッケージ内外に記載しておくと誤解を生まずに済むでしょう。
以上の手順で多言語環境の構築は終了です。
追記:リンクメッセージについて
lsl-ml.libは、メインスクリプトとライブラリとの間でリンクメッセージを利用して通信しています。その際、llMessageLinked()関数の第2引数「integer num」の値は、デフォルトでは「-125」となっています。他のスクリプトとのリンクメッセージにおいて、既にこの数値を利用している場合は、メインスクリプト内に貼り付けた代替関数内およびlsl-ml.lib内の「integer ML_COMMAND = -125;」の部分を他の数値に書き換えてください。
ソースコード
lsl-ml.lib
- lsl-ml.lib
// ##################### LSL MultiLingual Library ##################### // ver.0.1.2-beta // // This program is free library: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // For the person who gets this, you should keep the module modifyable // when you sell or distribute the object including this. That is, you // should keep this module FULL PERMISSION and keep object MODIFYABLE. // I think that it is very important to let the person who gets it edit // the language data for him/herself. // // You should have received a copy of the GNU Lesser General Public // License along with this program. If not, see following website. // http://www.gnu.org/licenses/ // // // Copyright 2008 arz Nitely // http://arznitely.com // // _______________________________________________a_r_z___N_i_t_e_l_y__ // Language Data ====================================================== // // modify as you like! // --> each data should be written as // "msg", "de", "en-us", "es", "fr", "ja", "ko", "po", "zh" // --> %s is replaced by this library and substitute functions list lang_data = [ "HELLO_AVATAR", "Guten Tag, %s.", "Hello, %s.", "Ciao, %s", "Bonjour, %s", "こんにちは、%sさん。", "안녕하세요, %s씨.", "Oi, %s.", "你好,%s先生.", "TOUCHED", "(%s): %s berührte sich.", "(%s): %s touched.", "(%s): %s tocó.", "(%s): %s a touché.", "(%s): %sがタッチしました。", "(%s): %s가 손대었습니다.", "(%s): %s tocou.", "(%s): %s觸摸了." ]; // Script ============================================================= // // CAUTION: The following part is concerned with other scripts in the // contents. When you change some, other related scripts want modifying // at the same time. // LSL Multilingual Library Command Num integer ML_COMMAND = -125; // Function: integer mlGetLangNum(string lang_code) list lang_code_list = ["de", "en-us", "es", "fr", "ja", "ko", "pt", "zh"]; integer mlGetLangNum(string lang_code){ integer result = llListFindList(lang_code_list, [lang_code]); return result; } // Flow default{ link_message(integer sender_num, integer num, string str, key id){ string command; string msg; string lang_code; string base_params; string extend_params; list expand_data; // = [command, msg, lang_code, base_params, extend_params] list temp_data; integer channel; vector color; float alpha; string url; list buttons; list replacement; integer replacement_num; integer cursor; string temp_result; string result; if (num == ML_COMMAND){ expand_data = llParseString2List(str, ["||"], []); command = llList2String(expand_data, 0); msg = llList2String(expand_data, 1); lang_code = llList2String(expand_data, 2); base_params = llList2String(expand_data, 3); extend_params = llList2String(expand_data, 4); if (llListFindList(lang_data, [msg]) != -1){ temp_data = llParseString2List(base_params, ["|"], []); replacement = llParseString2List(extend_params, ["|"], []); replacement_num = llGetListLength(replacement); cursor = llListFindList(lang_data, [msg]) + mlGetLangNum(lang_code) + 1; temp_result = llList2String(lang_data, cursor); if (replacement_num != 0){ list split = llParseStringKeepNulls(temp_result, ["%s"], []); integer i; for (i=0; i<replacement_num + 1; i++){ result += llList2String(split, i) + llList2String(replacement, i); } }else{ result = temp_result; } if (command == "ML_SAY"){ channel = (integer)llList2String(temp_data, 0); llSay(channel, result); }else if (command == "ML_WHISPER"){ channel = (integer)llList2String(temp_data, 0); llWhisper(channel, result); }else if (command == "ML_REGION_SAY"){ channel = (integer)llList2String(temp_data, 0); llRegionSay(channel, result); }else if (command == "ML_OWNER_SAY"){ llOwnerSay(result); }else if (command == "ML_INSTANT_MESSAGE"){ llInstantMessage(id, result); }else if (command == "ML_SET_TEXT"){ color = (vector)llList2String(temp_data, 0); alpha = (float)llList2String(temp_data, 1); llSetText(result, color, alpha); }else if (command == "ML_DIALOG"){ channel = (integer)llList2String(temp_data, 0); buttons = llList2List(temp_data, 1, -1); llDialog(id, result, buttons, channel); }else if (command == "ML_LOAD_URL"){ url = llList2String(temp_data, 0); llLoadURL(id, result, url); } } } } }
代替関数
// ######## Alternative Functions for LSL MultiLingual Library ######## // // LSL Multilingual Library Command Num integer ML_COMMAND = -125; // Alternative Function: mlSay(integer channel, string msg, string lang_code, list replacement) mlSay(integer channel, string msg, string lang_code, list replacement){ string sending_msg = "ML_SAY||" + msg + "||" + lang_code + "||" + (string)channel + "||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, ""); } // Alternative Function: mlOwnerSay(string msg, string lang_code, list replacement) mlOwnerSay(string msg, string lang_code, list replacement){ string sending_msg = "ML_OWNER_SAY||" + msg + "||" + lang_code + "||NULL||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, ""); } // Alternative Function: mlWhisper(integer channel, string msg, string lang_code, list replacement) mlWhisper(integer channel, string msg, string lang_code, list replacement){ string sending_msg = "ML_WHISPER||" + msg + "||" + lang_code + "||" + (string)channel + "||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, ""); } // Alternative Function: mlRegionSay(integer channel, string msg, string lang_code, list replacement) mlRegionSay(integer channel, string msg, string lang_code, list replacement){ string sending_msg = "ML_REGION_SAY||" + msg + "||" + lang_code + "||" + (string)channel + "||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, ""); } // Alternative Function: mlInstantMessage(key id, string msg, string lang_code, list replacement) mlInstantMessage(key id, string msg, string lang_code, list replacement){ string sending_msg = "ML_INSTANT_MESSAGE||" + msg + "||" + lang_code + "||NULL||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, id); } // Alternative Function: mlSetText(string str, vector color, float alpha, string lang_code, list replacement) mlSetText(string str, vector color, float alpha, string lang_code, list replacement){ string sending_msg = "ML_SET_TEXT||" + str + "||" + lang_code + "||" + (string)color + "|" + (string)alpha + "||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, ""); } // Alternative Function: mlDialog(key id, string msg, list buttons, integer channel, string lang_code, list replacement) mlDialog(key id, string msg, list buttons, integer channel, string lang_code, list replacement){ string sending_msg = "ML_DIALOG||" + msg + "||" + lang_code + "||" + (string)channel + "|" + llDumpList2String(buttons, "|") + "||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, id); } // Alternative Function: mlLoadURL(key id, string msg, string url, string lang_code, list replacement) mlLoadURL(key id, string msg, string url, string lang_code, list replacement){ string sending_msg = "ML_LOAD_URL||" + msg + "||" + lang_code + "||" + url + "||" + llDumpList2String(replacement, "|"); llMessageLinked(LINK_SET, ML_COMMAND, sending_msg, id); } // [EOF] // Copyright 2008 arz Nitely <http://arznitely.com/> // // _______________________________________________a_r_z___N_i_t_e_l_y__ // Sample Script default{ state_entry(){ llSetText("", <1.0, 1.0, 1.0>, 1.0); key owner_key = llGetOwner(); string owner_name = llKey2Name(owner_key); string owner_lang = llGetAgentLanguage(owner_key); mlSay(0, "HELLO_AVATAR", owner_lang, [owner_name]); } touch_start(integer total_number){ key user_key = llDetectedKey(0); string user_name = llKey2Name(user_key); string user_lang = llGetAgentLanguage(user_key); mlInstantMessage(user_key, "TOUCHED", user_lang , [user_lang, user_name]); } }