Knockout-Validation icon indicating copy to clipboard operation
Knockout-Validation copied to clipboard

Live localization

Open yohanb opened this issue 11 years ago • 19 comments

Hi Eric,

Thanks for your great contribution.

Here's my issue: I need to localize the validation message (i.e.: Field is required) when clicking a "localize" button. My application needs to be able to switch languages (fr-en) in a "live" fashion.

How would you proceed to accomplish this using your plugin?

Thanks!

Yohan

yohanb avatar Oct 16 '12 18:10 yohanb

Hi Yohan,

I've had a few stabs at trying to get what you're asking for working without changing the library itself. Eventually, the simplest solution I found was simply to cause a revalidation to take place: http://jsfiddle.net/IanYates83/CVMRA/3/

Ideally, applying localisation would happen live and only update the messages currently shown, but maybe the above will be enough for your needs. I think anything more would require a code change.

Hope that helps

IFYates avatar Oct 23 '12 12:10 IFYates

Hi Ian,

Thanks for your response. Your jsfiddle doesn't seem to work as I would have expected. When the Toggle method is called, the validation message is changed but the observable isn't re-validated so the text doesn't change. You have to change the text inputs and re-validate the controls to get the new message.

yohanb avatar Oct 23 '12 18:10 yohanb

Interesting. I only have IE8 at work (don't get me started), and the button causes the displayed messages to update immediately... I'll take another look at home where I have some sensible browsers.

IFYates avatar Oct 24 '12 09:10 IFYates

Sorry about reviving a zombie, but I'm having the same type of issue described above. @IanYates83 I hope you made it home ;)

I'm running ko.validation.localize() to switch the output text when I click the localize button. But my text is never changing either live or on revalidate. I noticed in the validator:

    // create a handler to correctly return an error message
            var errorMsgAccessor = function () {
                if (!config.messagesOnModified || isModified) {
                    return isValid ? null : obsv.error;
                } else {
                    return null;
                }
            };

The second time I run the localize function, the "obsv.error" always returns the first one.

Kikketer avatar May 05 '14 20:05 Kikketer

@Kikketer You need to revalidate the observable. You might achieve this with observable.valueHasMutated - after applying localization.

crissdev avatar Nov 17 '14 19:11 crissdev

This might land in some future version if more people will ask for it. Add your :+1: in the comments if you find this feature needed. We still have to see how it may fit in the current code base though.

crissdev avatar Feb 06 '15 16:02 crissdev

:+1:

ghost avatar Feb 18 '15 19:02 ghost

We need this for the koco project & koco-i18next module. We might try to fork + pull request if we find the time to do it. :+1:

RCMax avatar Jun 16 '15 13:06 RCMax

:+1:

colincbc avatar Jun 16 '15 13:06 colincbc

Hello Guys. First, i recently start using knockoutjs and i really love it. Right now, i'm facing a big problem and its very irritating because so far everything was good. I think i'm facing the same problem as this ticket. Actually i'm using Globalizejs, a javaScript library for internationalization and localization to translate all JS variable in my application without refreshing the page. The message is assign to an observer and should change, but it doesn't. Please help.

Problem Context:

I think the ko.extenders.required messages are stick to the first assigned text on page load. It doesn't support live validation. Here a example of the code i'm using :

<span data-bind="text: Resource.helloText">Hello default value</span>  // Works
<input type="radio" value="1" data-bind="checked: Person.GenderId, checkedValue: 1" />
<input type="radio" value="2" data-bind="checked: Person.GenderId, checkedValue: 2" />
<span class="text-danger" data-bind="validationMessage: Person.GenderId">Please this field is required</span> // Doesn't switch the text
function ViewModel() {

    self.Person = {
        genderId : ko.observable(false)
    }

    [...]

    /*   // Resource.json English file
    *     'helloText' : 'Hello',
    *     'pageTitle' : 'My Application', 
    *     'genderRequired': 'Please this field is required'

    *    // Resource.json french file
    *     'helloText' : 'Bonjour',
    *     'pageTitle' : 'Mon application ',
    *     'genderRequired': 'Svp ce champ est requis'
    */

    $.getJSON("server/ressource." + Globalize.culture().name.toString() + ".json", function (data) {
            ko.mapping.fromJS(data, {}, self.Resource);
     });

    // Language switcher
    self.SwitchLanguage = function () {
        var activeLang = 'en-CA';

        if (self.LanguageAppId() == 1) {
            self.LanguageAppId(2);
        }
        else {
            self.LanguageAppId(1);
            activeLang = 'fr-CA';
        }

        Globalize.culture(activeLang);

        $.getJSON("server/ressource." + Globalize.culture().name.toString() + ".json", function (data) {
            ko.mapping.fromJS(data, {}, self.Resource);
        });

    }

    [...]

    self.Person.GenderId.extend({ required: { message: self.Resource.genderRequired() } }); 

}

Source : https://github.com/jquery/globalize

one-geek avatar Feb 21 '16 04:02 one-geek

I'm a lurker on here as I use KO extensively but don't yet use the validation plugin. However, from what I can see in the code given by @guerson, if the message parameter was updated such that it could take an observable as well as a static string then you'd be gold.

That is, the final line could be

self.Person.GenderId.extend({ required: { message: self.Resource.genderRequired } }); 

Note that the genderRequired observable is not being evaluated.

Knowing very little about the code of this library it doesn't seem like it'd be too big an imposition or change to have the message renderer to use ko.unwrap to grab the message. Actually, for all I know it already does that - give it a try @guerson

IanYates avatar Feb 21 '16 22:02 IanYates

Each time the observable is not being evaluated, i have this traceback.

Uncaught TypeError: message.replace is not a function
with a traceback of :
ko.validation.formatMessage @ knockout.validation.debug.js:279validateSync @
knockout.validation.debug.js:877ko.validation.validateObservable @
knockout.validation.debug.js:947(anonymous function) @
knockout.validation.debug.js:831computedFn.evaluateImmediate_CallReadThenEndDependencyDetection @ knockout
3.4.0.debug.js:2142computedFn.evaluateImmediate_CallReadWithDependencyDetection @ knockout
3.4.0.debug.js:2114computedFn.evaluateImmediate @ knockout
3.4.0.debug.js:2078computedFn.evaluatePossiblyAsync @ knockout
3.4.0.debug.js:2044ko_subscribable_fn.notifySubscribers @ knockout-
[...]

one-geek avatar Feb 22 '16 01:02 one-geek

My guess is that being not evaluated, the code that's calling ko.validation.formatMessage function which is not anymore a string but something not evaluated.

one-geek avatar Feb 22 '16 01:02 one-geek

Is there a way to remove the validation and re-apply it without refreshing the page?

one-geek avatar Feb 22 '16 01:02 one-geek

@guerson You can remove validation of an observable using the validatable extender:

// Set up validation
var obs = ko.observable().extend({required: true, email: true});

// Remove validation
obs.extend({validatable: false});

crissdev avatar Feb 22 '16 21:02 crissdev

Thank you for your quick answer.

Finally, since all extenders messages are evaluated immediately, I'll change them through a function later and notify all subscribers directly. Only problem is (valueHasMutated/notifySubscribers()) will trigger an form validation.

self.myExtend = self.Person.GenderId.extend({
    required: { message: self.Resource.genderRequired()},
    customExtender: { message: self.Resource.extenderRequired()} 
});

[...]

var obsv = self.myExtend;
obsv.rules()[0].message = self.Resource['genderRequired'](); // required
obsv.rules()[1].message = self.Resource['extenderRequired'](); // customExtender

if (obsv.name == 'observable' && typeof obsv.valueHasMutated === "function") {
    obsv.valueHasMutated();
} else if (observ.name == 'computedObservable' && typeof observ.notifySubscribers === "function") {
    obsv.notifySubscribers();
}

It seems that is the only way for me right now to support my Single-page application.

one-geek avatar Feb 23 '16 15:02 one-geek

We have override the validation template and use a custom binding that uses the validation "message" as a key to look up a localised version of the display message. The localisation is tied to the current culture via a computed and will update all strings on a page without having to re-evaluate the validation conditions if the language is updated. The use the jQuery Globalize library based on Unicode/CLDR, to load and query

slaneyrw avatar Feb 23 '16 22:02 slaneyrw

I've tried numerous ways of doing what you say you are @slaneyrw . Whatever I try to manipulate the element with, it's overriden by the validation plugin and unsuccessful in doing so.

Even this simple thing; and if I were to make that span's text-data-binding a ko.computed it won't ever be overwritten. It's always the validation message from the plugin.

I can't even get it to update the message with the proper localization bundles. if I have loaded english and swedish, and try to switch with ko.validation.locale("en") and ("se"), having populated the localization with "en" and "se" before attempting to do so and the javascript files for the localization are loaded, it won't update the DOM when doing ko.validation.locale() . If I check the global scope, it has changed for instance ko.validation.rules.required to the appropriate locale, but the DOM is not updated.

omnoms avatar Mar 14 '16 15:03 omnoms

KnockoutValidationFix.zip

For what it's worth, I fixed this. See the above link. Unzip and load page.html in a browser for the demo.

I modified knockout.validation.js and saved as knockout.validation.modified.js. My changes are marked "// Dan". I changed the error message object to a knockout observable. I also added some hooks in knockout.validation.fix.js to handle updating error messages when the bounded language changes. Finally local.js contains a small localization API and the localized strings are stored in localModel.js.

github-clearthought avatar Jun 19 '18 21:06 github-clearthought