Translator

* Questions, answers and templates about scripting
* Fragen, Antworten und Beispiele zum Scripting

Translator

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:10

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

The basic idea with this translator service is that it should be possible to set up and run by several operators, and that it should be possible for end users of it to switch between operators if needed. At present, there is an experimental setup of the backend scripts at my own server, used as default in the end user LSL script below. You can switch to alternative backends by changing the URL last in the translator description and reset the translator from its dialog. The following public backends are available:
http://binders.world/cgi-bin/xl.sh (default from 2014-12-28)
http://158.69.242.69/cgi-bin/xl.sh (at Lost Paradise Grid from 2016-04-11)

A finished translator is available at the Metropolis region Media (126,136,30) hop://hypergrid.org:8002/Media/128/134/28 . There is also an upgraded version including controls to change foreground and background colors available at Metropolis region MB Sea 92 (12,128,24) hop://hypergrid.org:8002/MB%20Sea%2092/12/128/24 . You can also easily create your own by copying and pasting the LSL script below into a script in a prim and wear it as a HUD. Replace "<LINUX_HOST>" in the script with "5.9.108.75" to use the experimental setup.

This translator service is written for and tested on a server running OpenSim 0.8.0.3 Metropolis Edition PLUS [016] (Unix/Mono), run on Ubuntu 12.04.1 LTS 32 bit with Mono 2.10.8.1 and MySQL 5.5.31. It also runs on OpenSim 0.8.2.0 Dev PLUS Metropolis Edition [017] (Unix/Mono), run on Ubuntu 14.04.2 LTS 32 bit with Mono 3.12.1 and MySQL 5.5.41, and will probably run on or easily be adapted for many other (Linux/BSD) setups as well.

Regard the translator service in its present form as a beta test, and please provide feedback on any bugs or strange behaviour you discover, or features you come up with. I have tested it some and I believe ironed out the major bugs, but I am sure there are more to discover.

All scripts and documentation presented below are written by Magnuz Binder, and hereby released to the public domain, CC0 (Creative Commons http://creativecommons.org/publicdomain/).

Updates:
2015-08-22 Added info about upgraded translator and more platforms it runs on.
2016-04-12 Added info about alternative backends.
2017-04-23 Changed IP address to domain for binders.world.
Zuletzt geändert von Magnuz Binder am So 23. Apr 2017, 14:05, insgesamt 4-mal geändert.
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Translator roadmap

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:12

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

If you want to run your own translator service, below is a road map. If you just want to use the translator, use the LSL script below and replace "<LINUX_HOST>" with "binders.world".

Since Microsoft tend to shift things around rather often, I have removed outdated URL's and simplified the road map.

### Sign up for a Microsoft account and a free evaluation account at the Azure portal.
### You will need a working e-mail address, mobile phone and credit card to sign up, even if it's free of charge.
https://portal.azure.com

### Sign up for (the free version of ) Translator Text API.
https://portal.azure.com -> Sign in -> + New -> Intelligence -> Cognitive Services API -> Translator Text API, F0, ... -> Create

### <PLACEHOLDERS> below
The commands and scripts below are pretty much copy and paste, but placeholders marked similar to "<PLACEHOLDERS>" need to be replaced by actual strings. Also, I've used Emacs on Ubuntu, but that can be easily adjusted to other environments.

In the final LSL script, only one parameter needs to be edited to make it work, and that is the <LINUX_HOST> in the xl_url_default, pointing out the server where the translation scripts run. (binders.world will point to an experimental setup.) All interface texts are set in the beginning of the script, to make it easy to create e.g. language customized versions.

The LSL scripts stores its vital data in the description field of the translator prim. It's stored in the format
"any text;onoff;lang1;lang2;xl_url", where
"any text" is not used and can contain an ordinary description,
"onoff" is "0" (translator off) or "1" (translator on),
"lang1" is a language code for the users own language,
"lang2" is a language code for the language to translate to,
"xl_url" is an URL to a translator backend,
so the description can be e.g. (without quotes)
"script object;1;sv;de;http://5.9.108.75/cgi-bin/xl.sh"
The "onoff", "lang1" and "lang2" parameters are set from the script, but all parameters can be set by editing the description manually as well. The "xl_url" parameters can only be set manually, but if missing or faulty is set to the hardcoded value above by the script.

Updates:
2017-04-23 Updated road map, changed IP address to domain for binders.world.
Zuletzt geändert von Magnuz Binder am So 23. Apr 2017, 14:25, insgesamt 1-mal geändert.
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Translator backend preparations

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:14

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

### Install and configure apache, mysql, curl, gridsite-clients (on Ubuntu)
Code: Alles auswählen
ssh <LINUX_USER>@<LINUX_HOST>
sudo apt-get update
sudo apt-get install apache2, mysql-server, mysql-client, curl, gridsite-clients

... (lots of fiddling)

### Create cache database and table
Code: Alles auswählen
mysql -uroot -p
<SQL_ROOT_PASS>
CREATE DATABASE <XL_DB> DEFAULT CHARACTER SET=utf8;
GRANT ALL ON <XL_DB>.* TO '<XL_USER>' IDENTIFIED BY '<XL_PASS>';
GRANT ALL ON <XL_DB>.* TO '<XL_USER>'@'localhost' IDENTIFIED BY '<XL_PASS>';
GRANT ALL ON <XL_DB>.* TO '<XL_USER>'@'127.0.0.1' IDENTIFIED BY '<XL_PASS>';
USE <XL_DB>;
CREATE TABLE <XL_CACHE> (cache_id int(11) NOT NULL AUTO_INCREMENT, lang1 varchar(8) DEFAULT NULL, lang2 varchar(8) NOT NULL, text1 text NOT NULL, text2 text, used int(11) DEFAULT 0, ctime datetime NOT NULL, atime datetime DEFAULT NULL, PRIMARY KEY (cache_id), KEY to_text (lang2,text1(56))) ENGINE=InnoDB DEFAULT CHARSET=utf8;
QUIT;
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Translator backend access script

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:16

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

### Create shell script to get Microsoft Translator access token
# File must be executable by cron user
# auth_path file must be writable by cron user and readable by web user
emacs <SCRIPT_PATH>/xl_get_access.sh
Code: Alles auswählen
#!/bin/sh
############################################################
# xl_get_access.sh
# v0.02
# 2014-12-27 - 2017-04-23
# by Magnuz Binder in hypergrid.org CC0 2014-2017
#
# a script to get and store a Microsoft Translator API access token
############################################################

# Translator access
auth_url='https://api.cognitive.microsoft.com/sts/v1.0/issueToken' # access token URL
api_key='<API_KEY>' # either of 2 API subscription keys
auth_path='<ACCESS_DIR>/xl_access.txt' # path to access token

# Helper application paths
curl_path='/usr/bin/curl'

# Get access token
"$curl_path" --silent --header "Ocp-Apim-Subscription-Key: $api_key" --data '' "$auth_url" > "$auth_path" # request and store access token

# Done
exit 0

<Ctrl-x><Ctrl-c>y
chmod +x <SCRIPT_PATH>/xl_get_access.sh

### Create cronjob to keep access token updated
crontab -e
Code: Alles auswählen
...
*/10 * * * * <SCRIPT_PATH>/xl_get_access.sh >/dev/null 2>&1

<Ctrl-x><Ctrl-c>y

Updates:
2017-04-23 Updated script for the new Azure portal.
Zuletzt geändert von Magnuz Binder am So 23. Apr 2017, 14:30, insgesamt 1-mal geändert.
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Translator backend translation script

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:19

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

### Create CGI-script to use Microsoft Translator services
# File must be executable by web user
emacs <CGI_PATH>/xl.sh
Code: Alles auswählen
#!/bin/sh
############################################################
# xl.sh v0.06
# relays translations from Microsoft Translator API
# by Magnuz Binder in hypergrid.org CC0 2014-2015
#
# CGI accepting http(s) requests with parameters
# "from" (optional), "to", "text"
# for src language code, dst langaue code and src text
# returning a body "from|to|text"
# for src language code, dst langaue code and dst text
# fresh of from a MySQL cache
############################################################

# Database access
db_name='<XL_DB>' # translation cache database name
db_table='<XL_CACHE>' # translation cache table name
db_user='<XL_USER>' # translation cache database username
db_pass='<XL_PASS>' # translation cache database password

# Translator access
xd_url='http://api.microsofttranslator.com/V2/Http.svc/Detect' # detection URL
xl_url='http://api.microsofttranslator.com/v2/Http.svc/Translate' # translation URL
xl_codes='ar bg ca cs cy da de el en es et fa fi fr he hi ht hu id it ja ko lt lv ms mt mww nl no pl pt ro ru sk sl sv th tlh tlh-Qaak tr uk ur vi zh-CHS zh-CHT' # available language codes
auth_path='<ACCESS_DIR>/xl_access.txt' # path to access token
allowed_ips_path='<ACCESS_DIR>/xl_ips.txt' # path to optional file with allowed ips
allowed_users_path='<ACCESS_DIR>/xl_users.txt' # path to optional file with allowed users
allowed_regions_path='<ACCESS_DIR>/xl_regions.txt' # path to optional file with allowed regions

# Helper application paths
grep_path='/bin/grep'
sed_path='/bin/sed'
curl_path='/usr/bin/curl'
cut_path='/usr/bin/cut'
mysql_path='/usr/bin/mysql'
urlencode_path='/usr/bin/urlencode'
wc_path='/usr/bin/wc'

# Check if allowed
if [ -f "$allowed_ips_path" ]
then
    if [ "$("$grep_path" -x "$HTTP_HOST" "$allowed_ips_path" | "$wc_path" -l)" = "0" ]
    then
        exit 0
    fi
fi
if [ -f "$allowed_users_path" ]
then
    if [ "$("$grep_path" -x "$HTTP_X_SECONDLIFE_OWNER_NAME" "$allowed_users_path" | "$wc_path" -l)" = "0" ]
    then
        exit 0
    fi
fi
http_x_secondlife_region_name="$(echo "$HTTP_X_SECONDLIFE_REGION" | "$sed_path" 's/ ([^)]*)$//')"
if [ -f "$allowed_regions_path" ]
then
    if [ "$("$grep_path" -x "$http_x_secondlife_region_name" "$allowed_regions_path" | "$wc_path" -l)" = "0" ]
    then
        exit 0
    fi
fi

# Get access token and query input
q_acc="$(cat "$auth_path")" # get access token

q_from="$(echo "$QUERY_STRING" | "$sed_path" 's/^.*from=\([^&]*\).*$/\1/')" # get source langauge
case $QUERY_STRING in # sanity check if exists
    *from=*) q_from="$("$urlencode_path" -d "$q_from" | "$sed_path" 's/ /_/g')";;
    *) q_from=''
esac
case " $xl_codes " in # check if valid
    *\ $q_from\ *) ;;
    *) q_from=''
esac
m_from="$(echo "$q_from" | "$sed_path" "s/'/\\\\'/g")" # SQL-escape

q_to="$(echo "$QUERY_STRING" | "$sed_path" 's/^.*to=\([^&]*\).*$/\1/')" # get destination language
case $QUERY_STRING in # sanity check if exists
    *to=*) q_to="$("$urlencode_path" -d "$q_to" | "$sed_path" 's/ /_/g')";;
    *) q_to=''
esac
case " $xl_codes " in # check if valid
    *\ $q_to\ *) ;;
    *) q_to=''
esac
m_to="$(echo "$q_to" | "$sed_path" "s/'/\\\\'/g")" # SQL-escape

q_text="$(echo -e "$QUERY_STRING" | "$sed_path" 's/^.*text=\([^&]*\).*$/\1/')" # get text to translate
case $QUERY_STRING in # sanity check if exists
    *text=*) q_text="$("$urlencode_path" -d "$q_text")";;
    *) q_text=''
esac
m_text="$(echo "$q_text" | "$sed_path" "s/'/\\\\'/g")" # SQL-escape

q_result=''
m_result=''
q_snip=''
q=''
detect=''
if [ "x$q_from" = "x" ]
then # flag that source language is not given
    detect='1'
fi

# Handle query
if [ "x$q_acc" != "x" -a "x$q_to" != "x" -a "x$q_text" != "x" ]
then # if all required data available
    if [ "x$q_from" = "x" ]
    then # search cache when no source language given but wait to avoid race conditions
        sleep 0.1
        q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt1a FROM 'SELECT * FROM ""$db_table"" WHERE lang2=? AND text1=? LIMIT 1'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; EXECUTE stmt1a USING @lang2, @text1;")"
    else # search cache when source language given or in progress
        q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt1b FROM 'SELECT * FROM ""$db_table"" WHERE (lang1=? OR lang1=?) AND lang2=? AND text1=? LIMIT 1'; SET @empty=''; SET @lang1='""$m_from""'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; EXECUTE stmt1b USING @empty, @lang1, @lang2, @text1;")"
    fi
    if [ "x$q" = "x" ]
    then # create cache entry if none
        q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt2 FROM 'INSERT INTO ""$db_table"" (lang1,lang2,text1,text2,ctime) VALUES (?,?,?,?,NOW())'; SET @lang1='""$m_from""'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; SET @text2=''; EXECUTE stmt2 USING @lang1, @lang2, @text1, @text2;")"
        if [ "x$q_from" = "x" ]
        then # request source language detection from source text snip if none given
            q_snip="$(echo "$q_text" | "$cut_path" -c1-40)"
            q="$("$curl_path" --silent --get --data-urlencode "text=$q_snip" --header "Authorization: Bearer $q_acc" "$xd_url")"
            case $q in # check if source language detected
                *\</string\>*) q_from="$(echo "$q" | "$sed_path" "s/^.*<string [^>]*>\([^<]*\)<\/string>.*$/\1/g; s/&quot;/\"/g; s/&lt;/</g; s/&gt;/>/g; s/&amp;/\&/g")";;
                *) q_from='xx'
            esac
            m_from="$(echo "$q_from" | "$sed_path" "s/'/\\\\'/g")" # SQL-escape
        fi
        if [ "x$q_from" = "xxx" -o "x$q_from" = "x$q_to" ]
        then # set empty translation if no source language detected
            q_result=''
        else # request translation
            q="$("$curl_path" --silent --get --data-urlencode "from=$q_from" --data-urlencode "to=$q_to" --data-urlencode "text=$q_text" --header "Authorization: Bearer $q_acc" "$xl_url")"
            case $q in # check if translation succeeded
                *\</string\>*) q_result="$(echo "$q" | "$sed_path" "s/^.*<string [^>]*>\([^<]*\)<\/string>.*$/\1/g; s/&quot;/\"/g; s/&lt;/</g; s/&gt;/>/g; s/&amp;/\&/g")";;
                *) q_result="$q_text"
            esac
        fi
        m_result="$(echo "$q_result" | "$sed_path" "s/'/\\\\'/g")" # SQL-escape
        q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt3 FROM 'UPDATE ""$db_table"" SET lang1=?, text2=?, used=1, atime=NOW() WHERE (lang1=? OR lang1=?) AND lang2=? AND text1=?'; SET @empty=''; SET @lang1='""$m_from""'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; SET @text2='""$m_result""'; EXECUTE stmt3 USING @lang1, @text2, @empty, @lang1, @lang2, @text1;")" # complete cache entry
    else # get cache entry
        for t in 1 2 3 4 5
        do # wait max 5 seconds for complete cache entry
            q_result="$(echo "$q" | "$cut_path" -f5)"
            if [ "x$q_result" != "x" ]
            then # get data from complete cache entry
                q_from="$(echo "$q" | "$cut_path" -f2)"
                m_from="$(echo "$q_from" | "$sed_path" "s/'/\\\\'/g")" # SQL-escape
                q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt4 FROM 'UPDATE ""$db_table"" SET used=used+1, atime=NOW() WHERE (lang1=? OR lang1=?) AND lang2=? AND text1=?'; SET @empty=''; SET @lang1='""$m_from""'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; EXECUTE stmt4 USING @empty, @lang1, @lang2, @text1;")" # update cache entry use data
                break # done with cache entry
            fi
            sleep 1 # wait another second
            if [ "x$q_from" = "x" ]
            then # search cache again when no source language given
                q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt1a FROM 'SELECT * FROM ""$db_table"" WHERE lang2=? AND text1=? LIMIT 1'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; EXECUTE stmt1a USING @lang2, @text1;")"
            else # search cache again when source language given or in progress
                q="$("$mysql_path" -u "$db_user" -p"$db_pass" -D "$db_name" -N -B -e "PREPARE stmt1b FROM 'SELECT * FROM ""$db_table"" WHERE (lang1=? OR lang1=?) AND lang2=? AND text1=? LIMIT 1'; SET @empty=''; SET @lang1='""$m_from""'; SET @lang2='""$m_to""'; SET @text1='""$m_text""'; EXECUTE stmt1b USING @empty, @lang1, @lang2, @text1;")"
            fi
        done
    fi
fi

# Respond
echo 'Content-type: text/plain; charset=utf-8'
echo ''
if [ "x$q_result" = "x" ]
then # no translation available
    echo ''
else # send translation with some metadata
    echo "$q_from|$q_to|$q_result"
fi

# Done
exit 0

<Ctrl-x><Ctrl-c>y
chmod +x <CGI_PATH>/xl.sh

Updates:
2015-08-22 v0.06 Fixed an issue creating errors in Apache logs.
Zuletzt geändert von Magnuz Binder am Sa 22. Aug 2015, 23:32, insgesamt 2-mal geändert.
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Translator backend optional access control

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:21

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

### Optionally create text file with IP addresses allowed to use the service
# If no file, all IP addresses are allowed.
# One IP address per line.
# File must be readable by web user
emacs <ACCESS_PATH>/xl_ips.txt
Code: Alles auswählen
5.9.108.75
192.128.0.1

<Ctrl-x><Ctrl-c>y

### Optionally create text file with user names allowed to use the service
# If no file, all user names are allowed.
# One user name per line.
# File must be readable by web user
emacs <ACCESS_PATH>/xl_users.txt
Code: Alles auswählen
Magnuz Binder
Maggie Binder

<Ctrl-x><Ctrl-c>y

### Optionally create text file with region names allowed to use the service
# If no file, all region names are allowed.
# One region name per line.
# File must be readable by web user
emacs <ACCESS_PATH>/xl_regions.txt
Code: Alles auswählen
Magnuz's & Maggie's
Astronomy
Map

<Ctrl-x><Ctrl-c>y
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Translator frontend LSL script

Beitragvon Magnuz Binder » So 28. Dez 2014, 13:25

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

### Create LSL-script to translate and put in translator HUD prim
Code: Alles auswählen
////////////////////////////////////////////////////////////
// Chat Language Translator in LSL v0.05
// by Magnuz Binder in hypergrid.org CC0 2014-2016
//
// requires a web translation service accepting GET
// requests with parameters "from" (optional), "to", "text"
// for src language code, dst langaue code and src text
// returning a body "from|to|text"
// for src language code, dst langaue code and dst text
////////////////////////////////////////////////////////////

// Interface texts
string main_text = "Settings";
string backend_text = "Chose translator backend";
string lang1_text = "Chose your own langauge";
string lang2_text = "Chose language to translate to";
string page_text = "page";
string no_backend_text = "There are no translator backends available.";
string change_backend_text = "Changing to an available translator backend.";
string main_opt = "^";
string onoff_opt = "On/Off";
string reset_opt = "Reset";
string backend_opt = "Backend";
string lang1_opt = "Own language";
string lang2_opt = "To language";
string no_opt = "-";
string next_opt = ">";
list onoff_values = ["off", "on"];
// Translation defaults
integer chat_on_default = 1;
string lang1_default = "de";
string lang2_default = "en";
string xl_backends_url = "http://binders.world/xl/backends.txt";
string xl_url_default ="http://binders.world/cgi-bin/xl.sh";
// Available translation languages ("code=name",)
list lang_data = [
    "de=German",
    "en=English",
    "nl=Dutch",
    "fr=French",
    "es=Spanish",
    "pt=Portuguese",
    "it=Italian",
    "ja=Japanese",
    "ru=Russian",
    "sv=Swedish",
    "ar=Arabic",
    "bg=Bulgarian",
    "ca=Catalan",
    "zh-CHS=Chinese Simplified",
    "zh-CHT=Chinese Traditional",
    "cs=Czech",
    "da=Danish",
    "et=Estonian",
    "fi=Finnish",
    "el=Greek",
    "ht=Haitian Creole",
    "he=Hebrew",
    "hi=Hindi",
    "mww=Hmong Daw",
    "hu=Hungarian",
    "id=Indonesian",
    "tlh=Klingon",
    "tlh-Qaak=Klingon (pIqaD)",
    "ko=Korean",
    "lv=Latvian",
    "lt=Lithuanian",
    "ms=Malay",
    "mt=Maltese",
    "no=Norwegian",
    "fa=Persian",
    "pl=Polish",
    "ro=Romanian",
    "sk=Slovak",
    "sl=Slovenian",
    "th=Thai",
    "tr=Turkish",
    "uk=Ukrainian",
    "ur=Urdu",
    "vi=Vietnamese",
    "cy=Welsh"
];
// Constants and variables
list http_params = [
    HTTP_METHOD, "GET",
    HTTP_MIMETYPE,"text/plain;charset=utf-8",
    HTTP_BODY_MAXLENGTH, 16384,
    HTTP_VERIFY_CERT, TRUE,
    HTTP_VERBOSE_THROTTLE, TRUE,
    HTTP_PRAGMA_NO_CACHE, TRUE
];
list xl_nicks;
list xl_urls;
string xl_url;
integer chat_channel = 0;
integer chat_handle;
integer chat_on;
integer dia_channel;
integer dia_handle;
integer dia_page;
integer dia_no;
list lang_codes;
list lang_names;
string lang1;
string lang2;
key xl_req0;
list xl_ids;
list xl_reqs;
list xl_times;

// Store settings to object description
StoreSettings()
{
    llSetObjectDesc(llDumpList2String([llList2String(llParseStringKeepNulls(llGetObjectDesc(), [";"], []), 0), chat_on, lang1, lang2, xl_url], ";"));
}

default
{
    // Initialize
    state_entry()
    {
        // Listen to chat
        chat_handle = llListen(chat_channel, "", NULL_KEY, "");
        // Listen to dialog boxes
        dia_channel = llFloor(llFrand(-2147483648));
        dia_handle = llListen(dia_channel, "", llGetOwner(), "");
        // Parse language data
        lang_codes = [];
        lang_names = [];
        list tokens;
        integer d_hi = llGetListLength(lang_data);
        integer d;
        for ( d = 0; d < d_hi; d++ ) {
            tokens = llParseStringKeepNulls(llList2String(lang_data, d), ["="], []);
            lang_codes += [llList2String(tokens, 0)];
            lang_names += [llList2String(tokens, 1)];
        }
        // Retrieve defaults from object description
        tokens = llParseStringKeepNulls(llGetObjectDesc(), [";"], []);
        if ( llGetListLength(tokens) > 1 )
            chat_on = (integer)llList2String(tokens, 1);
        else
            chat_on = chat_on_default;
        lang1 = llList2String(tokens, 2);
        d = llListFindList(lang_codes, [lang1]);
        if ( d < 0 )
            lang1 = lang1_default;
        lang2 = llList2String(tokens, 3);
        d = llListFindList(lang_codes, [lang2]);
        if ( d < 0 )
            lang2 = lang2_default;
        xl_url = llList2String(tokens, 4);
        if ( llGetSubString(xl_url, 0, 6) != "http://" && llGetSubString(xl_url, 0, 7) != "https://" )
            xl_url = xl_url_default;
        llListenControl(chat_handle, chat_on);
        StoreSettings();
        llOwnerSay(reset_opt+" ("+lang1+">"+lang2+")");
        // Get list of backends
        xl_nicks = [];
        xl_urls = [];
        xl_req0 = llHTTPRequest(xl_backends_url, http_params, "");
    }

    // Handle touches
    touch_start(integer num)
    {
        // Handle owner touch only as main menu request
        if ( llDetectedKey(0) == llGetOwner() )
            llMessageLinked(LINK_THIS, dia_channel, main_opt, llDetectedKey(0));
    }

    // Handle chat and dialogs
    listen(integer channel, string name, key id, string str)
    {
        llMessageLinked(LINK_THIS, channel, str, id);
    }

    // Handle user input
    link_message(integer from_num, integer num, string str, key id)
    {
        // Handle chat only from agents
        if ( num == chat_channel && id == llGetOwnerKey(id) ) {
            // Keep max 10 latest translation request
            integer i_hi = llGetListLength(xl_reqs);
            if ( i_hi >= 10 ) {
                xl_ids = llDeleteSubList(xl_ids, 0, 0);
                xl_reqs = llDeleteSubList(xl_reqs, 0, 0);
                xl_times = llDeleteSubList(xl_times, 0, 0);
            }
            // Make new translation request from chat
            string query = "?";
            if ( id == llGetOwner() )
                query += "from="+llEscapeURL(lang1)+"&to="+llEscapeURL(lang2);
            else
                query += "to="+llEscapeURL(lang1);
            query +="&text="+llEscapeURL(str);
            xl_ids += [id];
            xl_times += [llGetUnixTime()];
            xl_reqs += [llHTTPRequest(xl_url+query, http_params, "")];
        }
        // Handle dialogs
        else if ( num == dia_channel && id == llGetOwner() ) {
            integer l = llListFindList(lang_codes, [str]);
            if ( ( dia_page == 1 || dia_page == 2 ) && ~l )
                dia_no = l/10;
            integer b = llListFindList(xl_nicks, [str]);
            if ( dia_page == 3 && ~b )
                dia_no = b/10;
            // Handle changed own language
            if ( l >= 0 && dia_page == 1 )
                lang1 = str;
            // Handle changed to language
            else if ( l >= 0 && dia_page == 2 )
                lang2 = str;
            // Handle changed backend
            else if ( b >= 0 && dia_page == 3 )
                xl_url = llList2String(xl_urls, b);
            // Handle language and backend browsing
            else if ( str == next_opt ) {
                dia_no++;
                integer dia_no_hi = 0;
                if ( dia_page == 1 )
                    dia_no_hi = llGetListLength(lang_codes);
                else if ( dia_page == 2 )
                    dia_no_hi = llGetListLength(lang_codes);
                else if ( dia_page == 3 )
                    dia_no_hi = llGetListLength(xl_nicks);
                if ( dia_no*10 > dia_no_hi )
                    dia_no = 0;
            }
            // Handle on/off
            else if ( str == onoff_opt ) {
                chat_on = !chat_on;
                llListenControl(chat_handle, chat_on);
            }
            // Handle reset
            else if ( str == reset_opt ) {
                llResetScript();
            }
            // Go to main menu
            else if ( str == main_opt )
                dia_page = 0;
            // Go to own language menu
            else if ( str == lang1_opt ) {
                dia_page = 1;
                dia_no = 0;
            }
            // Go to destination language menu
            else if ( str == lang2_opt ) {
                dia_page = 2;
                dia_no = 0;
            }
            // Go to backend menu
            else if ( str == backend_opt ) {
                dia_page = 3;
                dia_no = 0;
            }
            // Store settings if changed
            if ( l >= 0 || b >= 0 || str == onoff_opt )
                StoreSettings();

            // Build dialog
            string dia_text = main_text+"\n"+backend_opt+": "+xl_url+"\n"+onoff_opt+": "+llList2String(onoff_values, chat_on)+"\n"+lang1_opt+": "+lang1+"\n"+lang2_opt+": "+lang2;
            list dia_opts = [];
            // Main dialog
            if ( dia_page == 0 ) {
                dia_opts = [lang1_opt, lang2_opt, no_opt, onoff_opt, reset_opt, backend_opt];
            }
            // Language dialogs
            else if ( dia_page == 1 || dia_page == 2 ) {
                // Own language dialog
                if ( dia_page == 1 )
                    dia_text += "\n\n"+lang1_text+" ("+page_text+" "+(string)(dia_no+1)+"/"+(string)((llGetListLength(lang_codes)-1)/10+1)+")";
                // Destination language dialog
                else if (dia_page == 2 )
                    dia_text += "\n\n"+lang2_text+" ("+page_text+" "+(string)(dia_no+1)+"/"+(string)((llGetListLength(lang_codes)-1)/10+1)+")";
                // Language dialogs texts and buttons
                integer d1 = dia_no*10;
                integer d2 = d1+10;
                integer d_hi = llGetListLength(lang_codes);
                if ( d2 > d_hi )
                    d2 = d_hi;
                integer d;
                for ( d = d1; d < d2; d++ )
                    dia_text += "\n"+llList2String(lang_codes, d)+"="+llList2String(lang_names, d);
                dia_opts = llListInsertList(llList2List(lang_codes, d1, d2-1), [main_opt, next_opt], -1);
                dia_opts = llList2List(dia_opts, -3, -1)+llList2List(dia_opts, -6, -4)+llList2List(dia_opts, -9, -7)+llList2List(dia_opts, -12, -10);
            }
            // Backend dialog
            else if ( dia_page == 3 ) {
                dia_text += "\n\n"+backend_text+" ("+page_text+" "+(string)(dia_no+1)+"/"+(string)((llGetListLength(xl_nicks)-1)/10+1)+")";
                // Backend dialog texts and buttons
                integer d1 = dia_no*10;
                integer d2 = d1+10;
                integer d_hi = llGetListLength(xl_nicks);
                if ( d2 > d_hi )
                    d2 = d_hi;
                integer d;
                for ( d = d1; d < d2; d++ )
                    dia_text += "\n"+llList2String(xl_nicks, d)+"="+llList2String(xl_urls, d);
                dia_opts = llListInsertList(llList2List(xl_nicks, d1, d2-1), [main_opt, next_opt], -1);
                dia_opts = llList2List(dia_opts, -3, -1)+llList2List(dia_opts, -6, -4)+llList2List(dia_opts, -9, -7)+llList2List(dia_opts, -12, -10);
            }
            // Send dialog
            llDialog(id, dia_text, dia_opts, dia_channel);
        }
    }

    // Handle translation responses
    http_response(key req, integer status, list metadata, string body)
    {
        // Handle backend list responses
        if ( req == xl_req0 ) {
            list backends = llParseString2List(body, ["\n"], []);
            string backend;
            list params;
            xl_nicks = [];
            xl_urls = [];
            // Build backend lists
            integer b_hi = llGetListLength(backends);
            integer b;
            for ( b = 0; b < b_hi; b++ ) {
                backend = llList2String(backends, b);
                params = llParseStringKeepNulls(backend, [";"], []);
                if ( backend != "" && llGetSubString(backend, 0, 0) != "#" ) {
                    xl_nicks += [llList2String(params, 0)];
                    xl_urls += [llList2String(params, 1)];
                }
            }
            // Handle no backends
            if ( llGetListLength(xl_nicks) <= 0 ) {
                chat_on = 0;
                StoreSettings();
                llOwnerSay(no_backend_text);
            }
            // Handle non-active current backend
            else if ( llListFindList(xl_urls, [xl_url]) < 0 ) {
                xl_url = llList2String(xl_urls, 0);
                StoreSettings();
                llOwnerSay(change_backend_text);
            }
            return;
        }
        // Skip if unqueued translation request
        integer i = llListFindList(xl_reqs, [req]);
        if ( i < 0 )
            return;
        key id = (key)llList2String(xl_ids, i);
        // Unqueue found translation request
        xl_ids = llDeleteSubList(xl_ids, i, i);
        xl_reqs = llDeleteSubList(xl_reqs, i, i);
        xl_times = llDeleteSubList(xl_times, i, i);
        // Skip if no translation
        if ( body == "" )
            return;
        // Build translation output
        string name = llGetDisplayName(id);
        list tokens = llParseStringKeepNulls(body, ["|"], []);
        string l1 = llList2String(tokens, 0);
        string l2 = llList2String(tokens, 1);
        string text = llDumpList2String(llParseString2List(llDumpList2String(llDeleteSubList(tokens, 0, 1), "|"), ["\n"], []), "\n");
        // Impersonate chatter
        string oname = llGetObjectName();
        llSetObjectName(name+" ("+l1+">"+l2+")");
        // Translation from self public
        if ( id == llGetOwner() )
            llSay(chat_channel, text);
        // Translation to self private
        else
            llOwnerSay(text);
        // Restore own identity
        llSetObjectName(oname);
    }

    // Reset on rez
    on_rez(integer num)
    {
        llResetScript();
    }

    // Reset on owner-change, region crossing and restart
    changed(integer changes)
    {
        if ( changes & ( CHANGED_OWNER | CHANGED_REGION | CHANGED_REGION_START ) )
            llResetScript();
    }
}

Updates:
2016-04-13 v0.05 Handle multiple backends, make on/off status survive resets, and tidy up script some.
2015-01-04 v0.04 Reset message states selected languages.
2014-12-28 v0.03 First public release.
Zuletzt geändert von Magnuz Binder am Mi 13. Apr 2016, 23:09, insgesamt 3-mal geändert.
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Re: Translator

Beitragvon Minethere Always » So 28. Dez 2014, 14:16

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

I will go get that today. Wonderful you did this Magnuz-)
My Metro blogspace http://tinyurl.com/nlgvkk3
just another quasi-anonymous ephemeral wanderer...
Benutzeravatar
Minethere Always
 
Beiträge: 813
Registriert: Do 31. Jan 2013, 18:25

Re: Translator backend translation script

Beitragvon Magnuz Binder » Sa 22. Aug 2015, 17:45

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

I made a minor update to the translator backend translation script, xl.sh, not changing any functionality, but bumping it to version 0.06 and stopping a lot of unnecessary error messages in the server log. It appears flow control works a bit different between different flavors of shell script:

Old code:
Code: Alles auswählen
# Check if allowed
if [ -f "$allowed_ips_path" -a "$("$grep_path" -x "$HTTP_HOST" "$allowed_ips_path" | "$wc_path" -l)" = "0" ]
then
    exit 0
fi
if [ -f "$allowed_users_path" -a "$("$grep_path" -x "$HTTP_X_SECONDLIFE_OWNER_NAME" "$allowed_users_path" | "$wc_path" -l)" = "0" ]
then
    exit 0
fi
http_x_secondlife_region_name="$(echo "$HTTP_X_SECONDLIFE_REGION" | "$sed_path" 's/ ([^)]*)$//')"
if [ -f "$allowed_regions_path" -a "$("$grep_path" -x "$http_x_secondlife_region_name" "$allowed_regions_path" | "$wc_path" -l)" = "0" ]
then
    exit 0
fi


New code:
Code: Alles auswählen
# Check if allowed
if [ -f "$allowed_ips_path" ]
then
    if [ "$("$grep_path" -x "$HTTP_HOST" "$allowed_ips_path" | "$wc_path" -l)" = "0" ]
    then
        exit 0
    fi
fi
if [ -f "$allowed_users_path" ]
then
    if [ "$("$grep_path" -x "$HTTP_X_SECONDLIFE_OWNER_NAME" "$allowed_users_path" | "$wc_path" -l)" = "0" ]
    then
        exit 0
    fi
fi
http_x_secondlife_region_name="$(echo "$HTTP_X_SECONDLIFE_REGION" | "$sed_path" 's/ ([^)]*)$//')"
if [ -f "$allowed_regions_path" ]
then
    if [ "$("$grep_path" -x "$http_x_secondlife_region_name" "$allowed_regions_path" | "$wc_path" -l)" = "0" ]
    then
        exit 0
    fi
fi
Benutzeravatar
Magnuz Binder
 
Beiträge: 286
Registriert: Fr 22. Feb 2013, 21:01
Wohnort: Stockholm, Sweden

Re: Translator

Beitragvon Minethere Always » Sa 22. Aug 2015, 18:02

Translate to English translate to German Translate to French Translate to Italian Translate to Spanish Translate to Portuguese Translate to Czech Перевести на русский язык 翻译成中国

thank you-)
My Metro blogspace http://tinyurl.com/nlgvkk3
just another quasi-anonymous ephemeral wanderer...
Benutzeravatar
Minethere Always
 
Beiträge: 813
Registriert: Do 31. Jan 2013, 18:25

Nächste

Zurück zu Scripting

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast



Powered by phpBB

Deutsche Übersetzung durch phpBB.de
.

Style designed by Artodia.