Stig Kølbæk
Stig Kølbæk

Reputation: 474

Adding jQuery variable into placeholder in input field

I am using the jQuery multilanguage solution from https://github.com/coderkan/multi-language-jquery to add translation to a project I am working on which is working like it should, but I have an input field with a placeholder I also need to translate, but the "tools" in the JS does not give me a way out of the box to do so since I cannot place a <span> or similar into the placeholder="" attribute, so I need to find a solution on how I can do this.

The code:

What is "normally" used to add the translation in the HTML:

<span name="translate" caption="Need_Help"></span>

The INPUT field with placeholder ("Search.." is to be translated):

<input class="form-control" id="SearchEmployee" name="translate" caption="Search_Employee" type="text" value="" placeholder="Search..">

The jQuery:

function getLangResources() {

    // Define arrays how many language you want to translate
    var da = new Array();
    var en = new Array();

    da['Need_Help'] = "Brug for hjælp?";
    en['Need_Help'] = "Need help?";

    // Added new array defined arrays.
    var resources = new Array();
    resources['da'] = da;
    resources['en'] = en;

    return resources;
}

function changeLanguage(lng) {
    var resources = getLangResources()[lng];

    $("h1[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("h2[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("h3[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("span[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("p[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("button[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    // This I have tried to deal with placeholder
    $("input[placeholder][name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
}


$(function() {
    // Default Language
    changeLanguage("da");

    $("#da_button").click(function() {
        changeLanguage("da");
    });

    $("#eng_button").click(function() {
        changeLanguage("en");
    });
});

Upvotes: 0

Views: 388

Answers (2)

Tomalak
Tomalak

Reputation: 338158

I would start with a rework of your code, you're already not headed into a good direction.

Let's start with your getLangResources() function. The data structure it returns is basically a nested object, so let's skip all those tedious (and ill-applied) arrays:

function getLangResources() {
    return {
        Need_Help: {
            da: 'Brug for hjælp?',
            en: 'Need help?'
        }
    }
}

I suppose at some point this might return data from an HTTP request that fetches some JSON file, but for now let's keep it at returning a constant value.

const resources = getLangResources();

Next, your changeLanguage() function. It shows clear signs of copy-paste programming, so let's get rid of those first. Assume that each translatable element on your page has a CSS class called translate:

function changeLanguage(lng) {
    $(".translate").each(function () {
        $(this).text(resources[this.caption][lng]);
    });
}

This is better, but it still has drawbacks.

  1. For one thing, it requires that the language resource ID is present in the caption attribute, and those are not standard HTML.
  2. And for another thing, it will only replace the element's text. This is the issue you ask about.

So let's go one step further and address those

  1. We could make a data attribute that contains the resource ID: data-resource="Need_Help"
  2. We could make another data attribute that contains the property where this needs to go: data-resource-target="text".

This would lead us to:

function changeLanguage(lng) {
    $(".translate").each(function () {
        var resourceId = $(this).data("resource");
        var resourceTarget = $(this).data("resource-target");
        $(this)[resourceTarget](resources[resourceId][lng]);
    });
}

That's better, since it allows us to choose e.g. html or text as the targets, which would enable basic HTML formatting such as bold in resource strings.

But it's still rather rigid

  • It still does not allow setting e.g. an element's title
  • It only allows setting one element property, not several.

So let's scrap that as well. What if we made up a format that allows defining multiple properties at once? I could imagine a list of id-target pairs could work:

data-lng="<resouce-id>:<resource-target>,<resouce-id>:<resource-target>,..."

We could name the targets according to HTML properties...

<a href="..." class="translate" data-lng="Need_Help:textContent,Further_Information:title"></a>

...and set them directly on the element itself, instead of going through jQuery.

function changeLanguage(lng) {
    $(".translate").each(function () {
        var lng = $(this).data("lng");
        for (let pair of lng.split(',')) {
            let [resourceId, target] of pair.split(':');
            this[target] = resources[resourceId][lng];
        }
    });
}

Now you can target the placeholder just as easily as everything else.


Going from there you could go on and define convenience shorthands, things like

  • if there only is a single item (i.e. not a pair) in the lng attribute, assume it's meant to target the element's textContent: data-lng="Need_Help"
  • Maybe you want to define a shorthand for the property name textContent, such as .: data-lng="Need_Help:.,Further_Information:title"

It's up to you, whatever you find practical.

Upvotes: 1

mortb
mortb

Reputation: 9839

You can fix it like this. Try running the snippet, the code is runnable. Your problem is that your code uses the .text(...) function which sets the inner text, that is the contents between the opening and closening html tags, for a html emlement. To set a attribute of an html element, which placeholder is, you need to use the .attr(...) or .prop(...) functions.

https://api.jquery.com/attr/

https://api.jquery.com/prop/

function getLangResources() {

    // Define arrays how many language you want to translate
    var da = new Array();
    var en = new Array();

    da['Need_Help'] = "Brug for hjælp?";
    en['Need_Help'] = "Need help?";
    
    da['Search_Employee'] = "søg efter en medarbejder";
    en['Search_Employee'] = "Search employee";

    // Added new array defined arrays.
    var resources = new Array();
    resources['da'] = da;
    resources['en'] = en;

    return resources;
}

function changeLanguage(lng) {
    var resources = getLangResources()[lng];

    $("h1[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("h2[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("h3[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("span[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("p[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("button[name='translate']").each(function(i, elt) {
        $(elt).text(resources[$(elt).attr("caption")]);
    });
    $("input[placeholder][name='translate']").each(function(i, elt) {
        $(elt).attr("placeholder", resources[$(elt).attr("caption")]);
    });
}


$(function() {
    // Default Language
    changeLanguage("da");

    $("#da_button").click(function() {
        changeLanguage("da");
    });

    $("#eng_button").click(function() {
        changeLanguage("en");
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<span name="translate" caption="Need_Help" /></span>
<input class="form-control" id="SearchEmployee" name="translate" caption="Search_Employee" type="text" value="" placeholder="Search.." />

Upvotes: 1

Related Questions