Search code examples
javascriptphpajaxxmlhttprequestgettext

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:


Solution

  • 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();