简体   繁体   中英

Using gettext with Javascript from PHP via XMLHttpRequest

I have an application mainly written in PHP. The translations are done using gettext().

There is a small JavaScript part which also contains strings to be translated. I wrote this simple but working method using XMLHttpRequest:

function gettext(string_to_translate) {
    var filename = get_php_script_folder() + 'gettext.php?string_to_translate=' + string_to_translate;
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", filename, false);
    xmlhttp.send();
    if (xmlhttp.status === 200) {
        var translated_string = xmlhttp.responseText;
        return translated_string;
    } else {
        console.log("Error while translating " + string_to_translate + " Status " + xmlhttp.status);
        return string_to_translate; //Just give the original string.
    }

}

The php file is also quite simple:

require_once '../../default.php'; //configures gettext, session management, etc.
//TODO: support for ngettext might be added.
$string_to_translate = filter_input(INPUT_GET, 'string_to_translate', FILTER_SANITIZE_STRING);
$translated_string = gettext($string_to_translate);
echo $translated_string;

In the JavaScript I just call:

var input_box_form_default_reason = gettext("Vacation");
document.getElementById('input_box_form_reason').value = input_box_form_default_reason;

If I call this function synchronously [xmlhttp.open("GET", filename, false);] Firefox/Chrome warns me that:

[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.

So while this method works, it may cease to do so at any time.

But if I run the code async [xmlhttp.open("GET", filename, true);], then the next line will be executed before the result is there. The value will be undefined.

Is it feasable to make async XMLHttpRequest work in this context? Should I stick to synchronously fetching the values until some clever API is written? Should I write my JS files with PHP? (I hope not.)

PS:

  1. I do not use any framework like jQuery. That is a "religious" thing. I want to fully understand and maintain the whole codebase myself.

  2. I read the following questions, which did not answer mine:

Yes, you need a callback!

function gettext(string_to_translate, obj, callback)
... //your original xmlhttprequest
xmlhttp.callback = callback;, add a callback to the xmlhttp

//set the readystate event that listens to changes in the ajax call
xmlhttp.onreadystatechange = function()
{
    //target found and request complete
    if (this.status === 200 && this.readyState == 4) {
        //send result to the callback function
        this.callback(obj, this.responseText);
    } else {
        console.log("Error while translating " + string_to_translate + " Status " + xmlhttp.status);
        this.callback(obj, string_to_translate);
    }
}

//callback function specific for this case.
function setValue(obj, value)
{
   obj.value = value;
}

To call this:

gettext("Vacation", document.getElementById('input_box_form_reason'), setValue);

An extended ajax function with callback

 function ajax(url, method, json, callBack) { //it supports get and post //returns parsed JSON, when json is set to true. | json accessible via this.JSON in the callBack //a callback can be attached where the this refers to the xmlHTTP //supply an url like you would in a get request: http://www.example.com/page.php?query=1 //start the request with xmlDoc.fire. var xmlDoc = new XMLHttpRequest xmlDoc.JSON = json ? true : false; xmlDoc.error = true; xmlDoc.errorMessage = ""; xmlDoc.errorObj = {"error" : xmlDoc.error, "object" : "XMLHttpRequest", "message" : xmlDoc.errorMessage, "url" : url, "sync" : true, "method" : (method ? "POST" : "GET")}; xmlDoc.url = url xmlDoc.method = method ? "post" : "get"; xmlDoc.preserveWhiteSpace = true; if (method == "post") { xmlDoc.pUrl = url; xmlDoc.pArg = ""; if (url.match(/\\?/)) //we need to filter out the arguments since the are send seperately. { var splitted = url.split(/\\?/); xmlDoc.pUrl = splitted[0]; xmlDoc.pArg = ""; for (var i = 1; i < splitted.length; i++) { xmlDoc.pArg += splitted[i]; //prevent additional questionmarks from being splitted. } } xmlDoc.open.apply(xmlDoc, ["post", xmlDoc.pUrl , true]); //set up the connection xmlDoc.setRequestHeader("Content-type", "application/x-www-form-urlencoded ; charset=UTF-8"); } else { //get request, no special action need, just pass the url this.xmlDoc.open("get", url, true); //true for async } xmlDoc.onreadystatechange = readyStateXML.bind(xmlDoc, callBack); xmlDoc.setRequestHeader("Pragma", "no-cache"); xmlDoc.setRequestHeader("Cache-Control", "no-cache, must-revalidate"); xmlDoc.fire = fireXmlRequest; //set up fire function. return xmlDoc; } function fireXmlRequest() { if (this.method == "post") { this.send(this.pArg); //post } else { this.send(null); //get } } function readyStateXML(callBack) { if (this.readyState == 4) { //request completed, now check the returned data //We always assume that a request fails. if (this.errorMessage == "XML Not loaded." || this.errorMessage == "") { this.error = false; //set error to false, request succeeded. this.errorObj.error = false; if (!this.responseXML && !this.JSON) { this.error = true; this.errorMessage = "invalid XML."; this.errorObj.error = this.error; this.errorObj.message = this.errorMessage; } if (this.error == false) { this.xmlData = this.responseXML; if (this.JSON) { try { this.JSON = JSON.parse(this.responseText); } catch(err) { //JSON couldn't be parsed this.error = true; this.errorMessage = err.message + "<br />" + this.responseText; this.errorObj.error = this.error; this.errorObj.message = this.errorMessage; } } } //404 or 400, not found error if (this.status == "400" || this.status == "404" || this.status == 400 || this.status == 404) { this.error = true; this.errorMessage = "404: The requested page isn't found."; this.errorObj.error = this.error; this.errorObj.message = this.errorMessage; } else if(this.status == "500") { this.error = true; this.errorMessage = "500: Internal server error."; this.errorObj.error = this.error; this.errorObj.message = this.errorMessage; } if (typeof(callBack) != "undefined") { callBack.call(this); //pass the xmlDoc object to the callBack } } else { alert("Error \\n" + this.errorMessage); if (typeof(callBack) != "undefined") { callBack.call(this); } } } else { this.error = true; this.errorMessage = "XML Not loaded."; this.errorObj.error = this.error; this.errorObj.message = this.errorMessage; } } //to use ajx = ajax("index.php?query=1", "post", true, false, function(){/*callback*/}); console.log(ajx); ajx.fire(); 

JS is an event driven programming language. What you are missing is an event that gets triggered when your request is completed. You can bind an event “ onreadystatechange ” to your xmlhttp object that'll get triggered every time readyState changes.

function gettext(string_to_translate) {
var filename = get_php_script_folder() + 'gettext.php?string_to_translate=' + string_to_translate;
var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function() {
    if (xmlhttp.status === 200) {
        var translated_string = xmlhttp.responseText;
        document.getElementById('input_box_form_reason').value = translated_string;
    } else {
        console.log("Error while translating " + string_to_translate + " Status " + xmlhttp.status);
    }
};
xmlhttp.open("GET", filename);
xmlhttp.send();
}

I would suggest reading about JS events and Callbacks.

As Mouser pointed out, there needs to be a callback. I edited my function as follows:

function gettext(string_to_translate, object, callback_function) {
    var filename = get_php_script_folder() + 'gettext.php?string_to_translate=' + string_to_translate;
    var xml_http_request = new XMLHttpRequest();

    /*
     * Default values:
     */
    if (undefined === callback_function) {
        callback_function = set_value;
    }
    /*
     * Input error handling:
     */
    if (typeof object !== "object") {
        console.log("Error:" + object + " is not an object.");
        return false;
    }
    if (typeof callback_function === "function") {
        xml_http_request.callback = callback_function; // add a callback to the xml_http_request
    } else {
        console.log("Error:" + callback_function + " is not a function.");
        return false;
    }

    xml_http_request.onreadystatechange = function ()
    {
        //target found and request complete
        if (this.status === 200 && this.readyState === 4) {
            //send result to the callback function
            this.callback(object, this.responseText);
        } else if (this.readyState === 4) {
            console.log("Error while translating " + string_to_translate + " Status " + xml_http_request.status);
            this.callback(object, string_to_translate);
        }
    };
    xml_http_request.open("GET", filename, true);
    xml_http_request.send();
}
//callback function specific for gettext
function set_value(object, value)
{
    object.value = value;
}

It can be called as follows:

gettext("Vacation", document.getElementById('input_box_form_reason'));

With "Vacation" being the string to be translated and input_box_form_reason being the object which value will be changed. For more flexibility in the assignment the set_value function might be modified. For complex translations and concatenations of text, gettext.php has to be optimized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM