Tuesday 3 April 2018

Web Tutorial: Multilingual Easter Form (Part 2/3)

Now for the second part of this web tutorial - switching languages. We already learned how to do this last February, but this time it's going to be partially front-end. In fact, the AJAX portion of the code will still be used - we're just going to change the way content is being updated.

Go to the JavaScript file. Comment out the line that causes the page to reload, because we don't want to page to reload. Now test your code. Change to another language by using the drop-down list. Then refresh the page manually. Does it stay in that language? Yes? That's because we haven't removed the AJAX code that sets the cookie which determines what language the site is currently in.

assets\javascripts\application.js
function changeLang(lang)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            //location.reload();
        }
    };
    xmlhttp.open("GET", "../../langs/index/" + lang, true);
    xmlhttp.send();
}


Instead of reloading the page, we'll set it to run the changeContent() function. And then let's define the changeContent() function. It will accept a parameter, lang.

assets\javascripts\application.js
function changeLang(lang)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            //location.reload();
            changeContent(lang);
        }
    };
    xmlhttp.open("GET", "../../langs/index/" + lang, true);
    xmlhttp.send();
}

function changeContent(lang)
{

}


Time for some JSON! First, declare an array labels.

assets\javascripts\application.js
function changeContent(lang)
{
    var labels = [];
}


Next, fill up the array with an object. The name property of that object will be the class name of the placeholder you are going to change content for.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",           
        }
    ]


Next, set the content property of the object. It will be an array with two objects.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {

                },   
                {

                }
            ]           
        }
    ]


Each object will have two properties - lang and val. lang specifies the language, and val is the translated content of that language. So in this case, the object with name lblTitle has English set to "Happy Easter 2018!" and Chinese set to "2018复活节快乐!"

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        }
    ]


Now, we've set the language content of one label. Let's write the code to change the content. First, use a For loop to iterate through the labels array.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        }
    ]

    for (var i = 0; i < labels.length; i++)
    {

    }


Next, define an array, langLabels, by getting all elements with the same class name as the current element's name property.

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);
    }


The iterate through langLabels...

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);

        for (var j = 0; j < langLabels.length; j++)
        {

        }
    }


And use the filter() method on the content array in that object to get only the elements in the content array whose lang property corresponds with the value of the lang parameter. Set this to the array content.

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);

        for (var j = 0; j < langLabels.length; j++)
        {
            var content = labels[i].content.filter
            (
                function (x)
                {
                    return x.lang == lang;
                }
            )
        }
    }


Finally, set the innerHTML property of the current element to the val property of the appropriate content element! Since, after filtering, there should be only one element left in the content array, use the first element of content!

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);

        for (var j = 0; j < langLabels.length; j++)
        {
            var content = labels[i].content.filter
            (
                function (x)
                {
                    return x.lang == lang;
                }
            )

            langLabels[j].innerHTML = content[0].val;
        }
    }


It's time to put this to the test.


See how the page title changes as well? That's because both the h1 tag and the page title are styled using the class name lblTitle.


Now, to add more elements to the labels array.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        },
        {
            "name" : "lblName",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Name"
                },
                {
                    "lang" : "cn",
                    "val" : "名称"
                }
            ]
        },
        {
            "name" : "lblEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Email"
                },
                {
                    "lang" : "cn",
                    "val" : "电子邮件"
                }
            ]
        },
        {
            "name" : "lblSendEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Send mail"
                },
                {
                    "lang" : "cn",
                    "val" : "发送邮件"
                }
            ]
        }
    ]


More testing!




Let's add more content. Go to your about View and add a div with the class lblAbout, and a nice hr tag.

about\index.erb
<div class="lblAbout">

</div>

<hr />

<%= label_tag(:txtName, "Name", class:"lblName") %>
<%= text_field_tag(:txtName) %>
<br /><br />
<%= label_tag(:txtEmail, "Email", class:"lblEmail") %>
<%= text_field_tag(:txtEmail) %>
<br /><br />
<%= button_tag("Send mail", class:"lblSendEmail") %>


Then add the element for it. While we're at it, let's add one for lblFooter!

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        },
        {
            "name" : "lblName",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Name"
                },
                {
                    "lang" : "cn",
                    "val" : "名称"
                }
            ]
        },
        {
            "name" : "lblEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Email"
                },
                {
                    "lang" : "cn",
                    "val" : "电子邮件"
                }
            ]
        },
        {
            "name" : "lblSendEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Send mail"
                },
                {
                    "lang" : "cn",
                    "val" : "发送邮件"
                }
            ]
        },
        {
            "name" : "lblAbout",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Easter is the celebration of the resurrection of Jesus from the tomb on the third day after his cruxifixion. Learn more about the meaning of Easter including the history and holiday symbols like easter eggs, the bunny, and lilies."
                },
                {
                    "lang" : "cn",
                    "val" : "复活节是在耶稣受难之后第三天从坟墓复活耶稣的庆祝活动。了解更多关于复活节的含义,包括复活节彩蛋,兔子和百合花等历史和假日符号。"
                }
            ]
        },
        {
            "name" : "lblFooter",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "&copy; 2018 Teochew Thunder "
                },
                {
                    "lang" : "cn",
                    "val" : "&copy; 潮州雷"
                }
            ]
        }
    ]


OK, this isn't the prettiest layout in the world, but the point isn't prettiness - the point is to be able to change languages on the fly without reloading the page. I think we're doing really well here!




You may have noticed that when you refresh the page, a lot of content is missing. That's because the changeContent() function isn't run on page load. Here's a simple remedy for that...


views\layouts\application.html.erb
    <body onload="changeLang(document.getElementById('ddlLang').value)">
        <div class="container">
            <div class="header">
                <div class="right">
                    <span class="lang">
                      <label class="lblLang"></label>
                      <select name="ddlLang" id="ddlLang" onchange="changeLang(this.value)">
                      <% Lang.allowedVals["languages"].each do |key, value|%>
                          <option value="<%= key %>" <%= selectedLang(key) %>><%= value %></option>
                      <% end %>
                      </select>
                    </span>
                </div>
                <h1 class="lblTitle">
                   
                </h1>
                <div class="clearfix"></div>
            </div>

            <div class="content">
                <%= yield %>
                <div class="clearfix"></div>
            </div>

            <div class="lblFooter">

            </div>
        </div>
    </body>


Next

This page was set up to send email. So while we're at it, let's do some email sending via Rails...

No comments:

Post a Comment