Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translate messages on password strength #5509

Closed
rorteg opened this issue Jul 6, 2016 · 59 comments
Closed

Translate messages on password strength #5509

rorteg opened this issue Jul 6, 2016 · 59 comments
Assignees
Labels
bug report Fixed in 2.2.x The issue has been fixed in 2.2 release line Issue: Format is not valid Gate 1 Failed. Automatic verification of issue format is failed Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development Reproduced on 2.1.x The issue has been reproduced on latest 2.1 release

Comments

@rorteg
Copy link

rorteg commented Jul 6, 2016

Steps to reproduce

  1. Install Magento 2.1
  2. Try to translate the messages on password strength (Translate inline or source)

Expected result

  1. Translated.

Actual result

  1. Not translated.

captura de tela 2016-07-06 as 18 06 24

Thanks.

@rorteg
Copy link
Author

rorteg commented Jul 6, 2016

We try to translate the theme package:

"Minimum length of this field must be equal or greater than %1 symbols. Leading and trailing spaces will be ignored.","O tamanho mínimo desse campo deve ser maior ou igual a %1 caracteres. Espaços a direita serão ignorados."

And in the translation package that we have:

"Minimum length of this field must be equal or greater than %1 symbols. Leading and trailing spaces will be ignored.","O tamanho mínimo desse campo deve ser maior ou igual a %1 caracteres. Espaços a direita serão ignorados.",module,Magento_Customer or Magento_Ui

@zolthan
Copy link

zolthan commented Jul 25, 2016

A fix would be highly appreciated!

@antboiko antboiko added Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development PS bug report labels Jul 25, 2016
@antboiko
Copy link

Hi @rorteg , thanks for reporting. We've created internal ticket MAGETWO-55900 to fix this.

Best,
Anton.

@jernejh
Copy link

jernejh commented Jul 29, 2016

same problem here!

@jdavisonc
Copy link

Meanwhile the issue is not fixed, a temporal solution is to override validation.js with your translated string.

http://devdocs.magento.com/guides/v2.0/javascript-dev-guide/javascript/custom_js.html

@Choufourax
Copy link

Thanks @jdavisonc for this temporary solution.

For those who can't wait, here is a more detailled way to achieve this :

In your theme folde, edit requirejs-config.js, and add the mage/validation line :

var config = {
        map: {
            '*': {
                // ...
                'mage/validation':'js/validation'
                // ...
            }
        },
        ...
    };

Then, copy the file /lib/web/mage/validation.js in your theme in the subfolder "/web/js" and edit the file to do your translation.

@Choufourax
Copy link

And here is my french translation...

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
/*jshint regexdash:true eqnull:true browser:true jquery:true*/
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define([
            'jquery',
            'jquery/ui',
            'jquery/validate',
            'mage/translate'
        ], factory);
    } else {
        factory(jQuery);
    }
}(function ($) {
    "use strict";
    $.extend(true, $, {
        // @TODO: Move methods 'isEmpty', 'isEmptyNoTrim', 'parseNumber', 'stripHtml' in file with utility functions
        mage: {
            /**
             * Check if string is empty with trim
             * @param {string} value
             */
            isEmpty: function (value) {
                return (value === '' || value === undefined || (value == null) || (value.length === 0) || /^\s+$/.test(value));
            },

            /**
             * Check if string is empty no trim
             * @param {string} value
             */
            isEmptyNoTrim: function (value) {
                return (value === '' || (value == null) || (value.length === 0));
            },


            /**
             * Checks if {value} is between chiffres {from} and {to}
             * @param {string} value
             * @param {string} from
             * @param {string} to
             * @returns {boolean}
             */
            isBetween: function (value, from, to) {
                return ($.mage.isEmpty(from) || value >= $.mage.parseNumber(from)) &&
                    ($.mage.isEmpty(to) || value <= $.mage.parseNumber(to));
            },

            /**
             * Parse price string
             * @param {string} value
             */
            parseNumber: function (value) {
                if (typeof value !== 'string') {
                    return parseFloat(value);
                }
                var isDot = value.indexOf('.');
                var isComa = value.indexOf(',');
                if (isDot !== -1 && isComa !== -1) {
                    if (isComa > isDot) {
                        value = value.replace('.', '').replace(',', '.');
                    } else {
                        value = value.replace(',', '');
                    }
                } else if (isComa !== -1) {
                    value = value.replace(',', '.');
                }
                return parseFloat(value);
            },

            /**
             * Removes HTML tags and space caractères, chiffres and punctuation.
             * @param value Value being stripped.
             * @return {*}
             */
            stripHtml: function (value) {
                return value.replace(/<.[^<>]*?>/g, ' ').replace(/&nbsp;|&#160;/gi, ' ')
                    .replace(/[0-9.(),;:!?%#$'"_+=\/-]*/g, '');
            }
        }
    });

    $.validator.addMethod = function (name, method, message, dontSkip) {
        $.validator.methods[name] = method;
        $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];

        if (method.length < 3 || dontSkip) {
            $.validator.addClassRules(name, $.validator.normalizeRule(name));
        }
    };

    /**
     * Javascript object with credit card types
     * 0 - regexp for card number
     * 1 - regexp for cvn
     * 2 - check or not credit card number trough Luhn algorithm by
     */
    var creditCartTypes = {
        'SO': [new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
        'SM': [new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
        'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
        'MC': [new RegExp('^5[1-5][0-9]{14}$'), new RegExp('^[0-9]{3}$'), true],
        'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],
        'DI': [new RegExp('^(30[0-5][0-9]{13}|3095[0-9]{12}|35(2[8-9][0-9]{12}|[3-8][0-9]{13})|36[0-9]{12}|3[8-9][0-9]{14}|6011(0[0-9]{11}|[2-4][0-9]{11}|74[0-9]{10}|7[7-9][0-9]{10}|8[6-9][0-9]{10}|9[0-9]{11})|62(2(12[6-9][0-9]{10}|1[3-9][0-9]{11}|[2-8][0-9]{12}|9[0-1][0-9]{11}|92[0-5][0-9]{10})|[4-6][0-9]{13}|8[2-8][0-9]{12})|6(4[4-9][0-9]{13}|5[0-9]{14}))$'), new RegExp('^[0-9]{3}$'), true],
        'JCB': [new RegExp('^(30[0-5][0-9]{13}|3095[0-9]{12}|35(2[8-9][0-9]{12}|[3-8][0-9]{13})|36[0-9]{12}|3[8-9][0-9]{14}|6011(0[0-9]{11}|[2-4][0-9]{11}|74[0-9]{10}|7[7-9][0-9]{10}|8[6-9][0-9]{10}|9[0-9]{11})|62(2(12[6-9][0-9]{10}|1[3-9][0-9]{11}|[2-8][0-9]{12}|9[0-1][0-9]{11}|92[0-5][0-9]{10})|[4-6][0-9]{13}|8[2-8][0-9]{12})|6(4[4-9][0-9]{13}|5[0-9]{14}))$'), new RegExp('^[0-9]{3}$'), true],
        'OT': [new RegExp('^([0-9]+)$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), false],
        'DN': [new RegExp('^3((0([0-5]\\d*)?)|[689]\\d*)?$'), new RegExp('^[0-9]{3}$'), true],
        'UN': [new RegExp('^6(2\\d*)?$'), new RegExp('^[0-9]{3}$'), true],
        'MI': [new RegExp('^(5(0|[6-9])|63|67(?!59|6770|6774))\\d*$'), new RegExp('^[0-9]{3}$'), true],
        'MD': [new RegExp('^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\d*$'), new RegExp('^[0-9]{3}$'), true]
    };

    /**
     * validate credit card number using mod10
     * @param s
     * @return {Boolean}
     */
    function validateCreditCard(s) {
        // remove non-numerics
        var v = "0123456789",
            w = "", i, j, k, m, c, a, x;
        for (i = 0; i < s.length; i++) {
            x = s.charAt(i);
            if (v.indexOf(x, 0) != -1)
                w += x;
        }
        // validate number
        j = w.length / 2;
        k = Math.floor(j);
        m = Math.ceil(j) - k;
        c = 0;
        for (i = 0; i < k; i++) {
            a = w.charAt(i * 2 + m) * 2;
            c += a > 9 ? Math.floor(a / 10 + a % 10) : a;
        }
        for (i = 0; i < k + m; i++) {
            c += w.charAt(i * 2 + 1 - m) * 1;
        }
        return (c % 10 === 0);
    }

    /**
     * validate all table required inputs at once, using single hidden input
     * @param {String} value
     * @param {HTMLElement} element
     *
     * @return {Boolean}
     */
    function tableSingleValidation(value, element) {
        var empty = $(element).closest('table')
            .find('input.required-option:visible')
            .filter(function (i, el) {
                return $.mage.isEmpty(el.value);
            })
            .length;
        return empty === 0;
    }

    /**
     * Collection of validation rules including rules from additional-methods.js
     * @type {Object}
     */
    var rules = {
        "max-words": [
            function (value, element, params) {
                return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length < params;
            },
            'Veuillez saisir {0} mots maximum.'
        ],
        "min-words": [
            function (value, element, params) {
                return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params;
            },
            'Veuillez saisir au moins {0} mots.'
        ],
        "range-words": [
            function (value, element, params) {
                return this.optional(element) ||
                    $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params[0] &&
                    value.match(/bw+b/g).length < params[1];
            },
            'Veuillez saisir entre {0} et {1} mots.'
        ],
        "letters-with-basic-punc": [
            function (value, element) {
                return this.optional(element) || /^[a-z\-.,()'\"\s]+$/i.test(value);
            },
            'Lettres ou ponctuation uniquement svp'
        ],
        "alphanumeric": [
            function (value, element) {
                return this.optional(element) || /^\w+$/i.test(value);
            },
            'Lettre, chiffres, espaces ou underscores uniquement svp'
        ],
        "letters-only": [
            function (value, element) {
                return this.optional(element) || /^[a-z]+$/i.test(value);
            },
            'Lettre uniquement svp'
        ],
        "no-whitespace": [
            function (value, element) {
                return this.optional(element) || /^\S+$/i.test(value);
            },
            'Pas d’espace svp'
        ],
        "zip-range": [
            function (value, element) {
                return this.optional(element) || /^90[2-5]-\d{2}-\d{4}$/.test(value);
            },
            'Le code postal doit être compris entre 902xx-xxxx et 905-xx-xxxx'
        ],
        "integer": [
            function (value, element) {
                return this.optional(element) || /^-?\d+$/.test(value);
            },
            'Un entier positif ou négatif uniquement svp'
        ],
        "vinUS": [
            function (v) {
                if (v.length !== 17) {
                    return false;
                }
                var i, n, d, f, cd, cdv;
                var LL = ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
                var VL = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9];
                var FL = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
                var rs = 0;
                for (i = 0; i < 17; i++) {
                    f = FL[i];
                    d = v.slice(i, i + 1);
                    if (i === 8) {
                        cdv = d;
                    }
                    if (!isNaN(d)) {
                        d *= f;
                    } else {
                        for (n = 0; n < LL.length; n++) {
                            if (d.toUpperCase() === LL[n]) {
                                d = VL[n];
                                d *= f;
                                if (isNaN(cdv) && n === 8) {
                                    cdv = LL[n];
                                }
                                break;
                            }
                        }
                    }
                    rs += d;
                }
                cd = rs % 11;
                if (cd === 10) {
                    cd = "X";
                }
                if (cd === cdv) {
                    return true;
                }
                return false;
            },
            'Le numéro d’identification (VIN) est invalide.'
        ],
        "dateITA": [
            function (value, element) {
                var check = false;
                var re = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
                if (re.test(value)) {
                    var adata = value.split('/');
                    var gg = parseInt(adata[0], 10);
                    var mm = parseInt(adata[1], 10);
                    var aaaa = parseInt(adata[2], 10);
                    var xdata = new Date(aaaa, mm - 1, gg);
                    if ((xdata.getFullYear() === aaaa) &&
                        (xdata.getMonth() === mm - 1) && (xdata.getDate() === gg )) {
                        check = true;
                    } else {
                        check = false;
                    }
                } else {
                    check = false;
                }
                return this.optional(element) || check;
            },
            'Veuillez saisir a correct date'
        ],
        "dateNL": [
            function (value, element) {
                return this.optional(element) || /^\d\d?[\.\/-]\d\d?[\.\/-]\d\d\d?\d?$/.test(value);
            },
            'Vul hier een geldige datum in.'
        ],
        "time": [
            function (value, element) {
                return this.optional(element) || /^([01]\d|2[0-3])(:[0-5]\d){0,2}$/.test(value);
            },
            'Veuillez saisir une heure valide, between 00:00 and 23:59'
        ],
        "time12h": [
            function (value, element) {
                return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))$/i.test(value);
            },
            'Veuillez saisir une heure valide, between 00:00 am and 12:00 pm'
        ],
        "phoneUS": [
            function (phone_number, element) {
                phone_number = phone_number.replace(/\s+/g, "");
                return this.optional(element) || phone_number.length > 9 &&
                    phone_number.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
            },
            'Veuillez indiquer un téléphone valide'
        ],
        "phoneUK": [
            function (phone_number, element) {
                return this.optional(element) || phone_number.length > 9 &&
                    phone_number.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/);
            },
            'Veuillez indiquer un téléphone valide'
        ],
        "mobileUK": [
            function (phone_number, element) {
                return this.optional(element) || phone_number.length > 9 &&
                    phone_number.match(/^((0|\+44)7(5|6|7|8|9){1}\d{2}\s?\d{6})$/);
            },
            'Veuillez indiquer un numéro de téléphone valide'
        ],
        "stripped-min-length": [
            function (value, element, param) {
                return $(value).text().length >= param;
            },
            'Veuillez saisir au moins {0} caractères'
        ],
        "email2": [
            function (value, element) {
                return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
            },
            $.validator.messages.email
        ],
        "url2": [
            function (value, element) {
                return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
            },
            $.validator.messages.url
        ],
        "credit-card-types": [
            function (value, element, param) {
                if (/[^0-9-]+/.test(value)) {
                    return false;
                }
                value = value.replace(/\D/g, "");

                var validTypes = 0x0000;

                if (param.mastercard) {
                    validTypes |= 0x0001;
                }
                if (param.visa) {
                    validTypes |= 0x0002;
                }
                if (param.amex) {
                    validTypes |= 0x0004;
                }
                if (param.dinersclub) {
                    validTypes |= 0x0008;
                }
                if (param.enroute) {
                    validTypes |= 0x0010;
                }
                if (param.discover) {
                    validTypes |= 0x0020;
                }
                if (param.jcb) {
                    validTypes |= 0x0040;
                }
                if (param.unknown) {
                    validTypes |= 0x0080;
                }
                if (param.all) {
                    validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;
                }
                if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard
                    return value.length === 16;
                }
                if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa
                    return value.length === 16;
                }
                if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex
                    return value.length === 15;
                }
                if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub
                    return value.length === 14;
                }
                if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute
                    return value.length === 15;
                }
                if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover
                    return value.length === 16;
                }
                if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb
                    return value.length === 16;
                }
                if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb
                    return value.length === 15;
                }
                if (validTypes & 0x0080) { //unknown
                    return true;
                }
                return false;
            },
            'Veuillez saisir un numéro de carte valide.'
        ],
        "ipv4": [
            function (value, element) {
                return this.optional(element) || /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);
            },
            'Veuillez saisir une adresse IP V4 valide.'
        ],
        "ipv6": [
            function (value, element) {
                return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
            },
            'Veuillez saisir une adresse IP V6 valide.'
        ],
        "pattern": [
            function (value, element, param) {
                return this.optional(element) || param.test(value);
            },
            'Invalid format.'
        ],
        "allow-container-className": [
            function (element) {
                if (element.type === 'radio' || element.type === 'checkbox') {
                    return $(element).hasClass('change-container-classname');
                }
            },
            ''
        ],
        "validate-no-html-tags": [
            function (value) {
                return !/<(\/)?\w+/.test(value);
            },
            'Les balises HTML ne sont pas autorisées.'
        ],
        "validate-select": [
            function (value) {
                return ((value !== "none") && (value != null) && (value.length !== 0));
            },
            'Veuillez choisir une option.'
        ],
        "validate-no-empty": [
            function (value) {
                return !$.mage.isEmpty(value);
            },
            'Empty Value.'
        ],
        "validate-alphanum-with-spaces": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9 ]+$/.test(v);
            },
            'Ne mettre que des lettres (a-z or A-Z), chiffres (0-9) or spaces only dans ce champ.'
        ],
        "validate-data": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);
            },
            'Ne mettre que des lettres (a-z or A-Z), chiffres (0-9) or underscore (_) dans ce champ, et le premier caractère doit être une lettre.'
        ],
        "validate-street": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v);
            },
            'Ne mettre que des lettres (a-z or A-Z), chiffres (0-9), spaces and "#" dans ce champ.'
        ],
        "validate-phoneStrict": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
            },
            'Veuillez saisir un téléphone valide. Par exemple (123) 456-7890 or 123-456-7890.'
        ],
        "validate-phoneLax": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^((\d[\-. ]?)?((\(\d{3}\))|\d{3}))?[\-. ]?\d{3}[\-. ]?\d{4}$/.test(v);
            },
            'Veuillez saisir un téléphone valide. Par exemple (123) 456-7890 or 123-456-7890.'
        ],
        "validate-fax": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
            },
            'Veuillez saisir un numéro de fax valide (Ex: 123-456-7890).'
        ],
        "validate-email": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i.test(v);
            },
            'Veuillez saisir une adresse email valide (Ex: john@exemple.fr).'
        ],
        "validate-emailSender": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[\S ]+$/.test(v);
            },
            'Veuillez saisir une adresse email valide (Ex: john@exemple.fr).'
        ],
        "validate-password": [
            function (v) {
                if (v == null) {
                    return false;
                }
                /*strip leading and trailing spaces*/
                var pass = $.trim(v);
                if (!pass.length) {
                    return true;
                }
                return !(pass.length > 0 && pass.length < 6);
            },
            'Veuillez saisir au moins 6 caractères.'
        ],
        "validate-admin-password": [
            function (v) {
                if (v == null) {
                    return false;
                }
                var pass = $.trim(v);
                /*strip leading and trailing spaces*/
                if (0 === pass.length) {
                    return true;
                }
                if (!(/[a-z]/i.test(v)) || !(/[0-9]/.test(v))) {
                    return false;
                }
                if (pass.length < 7) {
                    return false;
                }
                return true;
            },
            'Veuillez saisir 7 caractères minimum, utilisant des chiffres et des lettres.'
        ],
        "validate-customer-password": [
            function (v, elm) {
                var validator = this,
                    length = 0,
                    counter = 0;
                var passwordMinLength = $(elm).data('password-min-length');
                var passwordMincaractèresets = $(elm).data('password-min-character-sets');
                var pass = $.trim(v);
                var result = pass.length >= passwordMinLength;
                if (result == false) {
                    validator.passwordErrorMessage = $.mage.__(
                        "Ce champ doit comporter au mois %1 caractères." +
                        " Les espaces en fin ou au début seront ignorés."
                    ).replace('%1', passwordMinLength);
                    return result;
                }
                if (pass.match(/\d+/)) {
                    counter ++;
                }
                if (pass.match(/[a-z]+/)) {
                    counter ++;
                }
                if (pass.match(/[A-Z]+/)) {
                    counter ++;
                }
                if (pass.match(/[^a-zA-Z0-9]+/)) {
                    counter ++;
                }
                if (counter < passwordMincaractèresets) {
                    result = false;
                    validator.passwordErrorMessage = $.mage.__(
                        "Ce champs doit utiliser au moin %1 types de caractères différents parmi :" +
                        " minuscules, majuscules, chiffres, caractères spéciaux."
                    ).replace('%1', passwordMincaractèresets);
                }
                return result;
            }, function () {
                return this.passwordErrorMessage;
            }
        ],
        "validate-url": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = (v || '').replace(/^\s+/, '').replace(/\s+$/, '');
                return (/^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(v);

            },
            'Veuillez saisir une URL valide. Avec le préfixe (http://, https:// or ftp://).'
        ],
        "validate-clean-url": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v);

            },
            'Veuillez saisir une URL valide. Par exemple, http://www.example.com or www.example.com.'
        ],
        "validate-xml-identifier": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v);

            },
            'Veuillez saisir un identifiant XML valide (Ex: something_1, block5, id-4).'
        ],
        "validate-ssn": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v);

            },
            'Veuillez saisir un numéro de sécurité social valide (Ex: 123-45-6789).'
        ],
        "validate-zip-us": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v);

            },
            'Veuillez saisir un code postal valide (Ex: 90602 or 90602-1234).'
        ],
        "validate-date-au": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
                if ($.mage.isEmpty(v) || !regex.test(v)) {
                    return false;
                }
                var d = new Date(v.replace(regex, '$2/$1/$3'));
                return parseInt(RegExp.$2, 10) === (1 + d.getMonth()) &&
                    parseInt(RegExp.$1, 10) === d.getDate() &&
                    parseInt(RegExp.$3, 10) === d.getFullYear();

            },
            'Veuillez respecter le format : JJ/MM/AAAA. Par exemple, 31/12/2017'
        ],
        "validate-currency-dollar": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v);

            },
            'Veuillez saisir un montant valide. Par exemple $100.00.'
        ],
        "validate-not-negative-number": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);
                return !isNaN(v) && v >= 0;

            },
            'Veuillez saisir a number 0 or greater dans ce champ.'
        ],
        // validate-not-negative-number should be replaced in all places with this one and then removed
        "validate-zero-or-greater": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);
                return !isNaN(v) && v >= 0;

            },
            'Veuillez saisir un entier supérieur ou égal à 0 dans ce champ.'
        ],
        "validate-greater-than-zero": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);
                return !isNaN(v) && v > 0;
            },
            'Veuillez saisir un entier supérieur à 0 dans ce champ.'
        ],
        "validate-css-length": [
            function (v) {
                if (v !== '') {
                    return (/^[0-9]*\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(v);
                }
                return true;
            },
            'Saisir une valeur CSS valide(Ex: 100px, 77pt, 20em, .5ex or 50%).'
        ],
        /** @description Additional methods */
        "validate-number": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || (!isNaN($.mage.parseNumber(v)) && /^\s*-?\d*(\.\d*)?\s*$/.test(v));
            },
            'Veuillez saisir un nombre dans ce champ.'
        ],
        "required-number": [
            function (v) {
                return !!v.length;
            },
            'Veuillez saisir un nombre dans ce champ.'
        ],
        "validate-number-range": [
            function (v, elm, param) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                var numValue = $.mage.parseNumber(v);
                if (isNaN(numValue)) {
                    return false;
                }

                var dataAttrRange = /^(-?[\d.,]+)?-(-?[\d.,]+)?$/,
                    classNameRange = /^number-range-(-?[\d.,]+)?-(-?[\d.,]+)?$/,
                    result = true,
                    range, m, classes, ii;

                range = param;
                if (typeof range === 'object') {
                    m = dataAttrRange.exec(range);
                    if (m) {
                        result = result && $.mage.isBetween(numValue, m[1], m[2]);
                    }
                } else if (elm && elm.className) {
                    classes = elm.className.split(" ");
                    ii = classes.length;

                    while (ii--) {
                        range = classes[ii];
                        m = classNameRange.exec(range);
                        if (m) {
                            result = result && $.mage.isBetween(numValue, m[1], m[2]);
                            break;
                        }
                    }
                }

                return result;
            },
            'La valeur n’est pas comprise dans l’interval attendu.',
            true
        ],
        "validate-digits": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || !/[^\d]/.test(v);
            },
            'Veuillez saisir un nombre dans ce champ.'
        ],
        "validate-digits-range": [
            function (v, elm, param) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                var numValue = $.mage.parseNumber(v);
                if (isNaN(numValue)) {
                    return false;
                }

                var dataAttrRange = /^(-?\d+)?-(-?\d+)?$/,
                    classNameRange = /^digits-range-(-?\d+)?-(-?\d+)?$/,
                    result = true,
                    range, m, classes, ii;
                range = param;

                if (typeof range === 'object') {
                    m = dataAttrRange.exec(range);
                    if (m) {
                        result = result && $.mage.isBetween(numValue, m[1], m[2]);
                    }
                } else if (elm && elm.className) {
                    classes = elm.className.split(" ");
                    ii = classes.length;

                    while (ii--) {
                        range = classes[ii];
                        m = classNameRange.exec(range);
                        if (m) {
                            result = result && $.mage.isBetween(numValue, m[1], m[2]);
                            break;
                        }
                    }
                }

                return result;
            },
            'La valeur n’est pas comprise dans l’interval attendu.',
            true
        ],
        'validate-range': [
            function (v, elm) {
                var minValue, maxValue;
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](v)) {
                    minValue = maxValue = $.mage.parseNumber(v);
                } else {
                    var ranges = /^(-?\d+)?-(-?\d+)?$/.exec(v);

                    if (ranges) {
                        minValue = $.mage.parseNumber(ranges[1]);
                        maxValue = $.mage.parseNumber(ranges[2]);
                        if (minValue > maxValue) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
                var reRange = /^range-(-?\d+)?-(-?\d+)?$/,
                    result = true;

                var values = $(elm).prop('class').split(" ");

                for (var i = values.length - 1; i >= 0; i--) {
                    var name = values[i];
                    var validRange = reRange.exec(name);
                    if (validRange) {
                        var minValidRange = $.mage.parseNumber(validRange[1]);
                        var maxValidRange = $.mage.parseNumber(validRange[2]);
                        result = result &&
                        (isNaN(minValidRange) || minValue >= minValidRange) &&
                        (isNaN(maxValidRange) || maxValue <= maxValidRange);
                    }
                }
                return result;
            },
            'La valeur n’est pas comprise dans l’interval attendu.'
        ],
        "validate-alpha": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+$/.test(v);
            },
            'Ne saisir que des lettres (a-z or A-Z) dans ce champ.'
        ],
        "validate-code": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-z]+[a-z0-9_]+$/.test(v);
            },
            'Ne mettre que des lettres (a-z), chiffres (0-9) des underscore (_) dans ce champ, et le premier caractère doit être une lettre.'
        ],
        "validate-alphanum": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9]+$/.test(v);
            },
            'Ne mettre que des lettres (a-z or A-Z) et chiffres (0-9) dans ce champs. Pas d’espaces ni caractères spéciaux.'
        ],
        "validate-date": [
            function (v) {
                var test = new Date(v);
                return $.mage.isEmptyNoTrim(v) || !isNaN(test);
            }, 'Veuillez saisir une date valide.'

        ],
        "validate-date-range": [
            function (v, elm) {
                var m = /\bdate-range-(\w+)-(\w+)\b/.exec(elm.className);
                if (!m || m[2] === 'to' || $.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                var currentYear = new Date().getFullYear() + '';
                var normalizedTime = function (v) {
                    v = v.split(/[.\/]/);
                    if (v[2] && v[2].length < 4) {
                        v[2] = currentYear.substr(0, v[2].length) + v[2];
                    }
                    return new Date(v.join('/')).getTime();
                };

                var dependentElements = $(elm.form).find('.validate-date-range.date-range-' + m[1] + '-to');
                return !dependentElements.length || $.mage.isEmptyNoTrim(dependentElements[0].value) ||
                    normalizedTime(v) <= normalizedTime(dependentElements[0].value);
            },
            'La date de fin doit être supérieure ou égale à la date de début.'
        ],
        "validate-cpassword": [
            function () {
                var conf = $('#confirmation').length > 0 ? $('#confirmation') : $($('.validate-cpassword')[0]);
                var pass = false;
                if ($('#password')) {
                    pass = $('#password');
                }
                var passwordElements = $('.validate-password');
                for (var i = 0; i < passwordElements.length; i++) {
                    var passwordElement = $(passwordElements[i]);
                    if (passwordElement.closest('form').attr('id') === conf.closest('form').attr('id')) {
                        pass = passwordElement;
                    }
                }
                if ($('.validate-admin-password').length) {
                    pass = $($('.validate-admin-password')[0]);
                }
                return (pass.val() === conf.val());
            },
            'Verifiez que les mots de passe soient identiques.'
        ],
        "validate-identifier": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/.test(v);
            },
            'Veuillez saisir une clé d’URL valide (Ex: "exemple-page", "example.html" or "categorie/exemple-page").'
        ],
        "validate-zip-international": [
            /*function(v) {
             // @TODO: Cleanup
             return Validation.get('IsEmpty').test(v) || /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v);
             }*/
            function () {
                return true;
            },
            'Veuillez saisir un code postal valide.'
        ],
        "validate-one-required": [
            function (v, elm) {
                var p = $(elm).parent();
                var options = p.find('input');
                return options.map(function (elm) {
                        return $(elm).val();
                    }).length > 0;
            },
            'Veuillez choisir une option ci-dessus.'
        ],
        "validate-state": [
            function (v) {
                return (v !== 0 || v === '');
            },
            'Veuiller choisir un pays / une province.'
        ],
        "required-file": [
            function (v, elm) {
                var result = !$.mage.isEmptyNoTrim(v);
                if (!result) {
                    var ovId = $(elm).attr('id') + '_value';
                    if ($(ovId)) {
                        result = !$.mage.isEmptyNoTrim($(ovId).val());
                    }
                }
                return result;
            },
            'Veuillez choisir un fichier.'
        ],
        "validate-ajax-error": [
            function (v, element) {
                element = $(element);
                element.on('change.ajaxError', function () {
                    element.removeClass('validate-ajax-error');
                    element.off('change.ajaxError');
                });
                return !element.hasClass('validate-ajax-error');
            },
            ''
        ],
        "validate-optional-datetime": [
            function (v, elm, param) {
                var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]'),
                    hasWithValue = false, hasWithNoValue = false,
                    pattern = /day_part$/i;
                for (var i = 0; i < dateTimeParts.length; i++) {
                    if (!pattern.test($(dateTimeParts[i]).attr('id'))) {
                        if ($(dateTimeParts[i]).val() === "") {
                            hasWithValue = true;
                        } else {
                            hasWithNoValue = true;
                        }
                    }
                }
                return hasWithValue ^ hasWithNoValue;
            },
            'Le champ est incomplet.'
        ],
        "validate-required-datetime": [
            function (v, elm, param) {
                var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]');
                for (var i = 0; i < dateTimeParts.length; i++) {
                    if (dateTimeParts[i].value === "") {
                        return false;
                    }
                }
                return true;
            },
            'This is a required field.'
        ],
        "validate-one-required-by-name": [
            function (v, elm, selector) {
                var name = elm.name.replace(/([\\"])/g, '\\$1'),
                    container = this.currentForm,
                    selector = selector === true ? 'input[name="' + name + '"]:checked' : selector;

                return !!container.querySelectorAll(selector).length;
            },
            'Veuillez choisir une des options.'
        ],
        "less-than-equals-to": [
            function (value, element, params) {
                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
                    this.lteToVal = $(params).val();
                    return parseFloat(value) <= parseFloat($(params).val());
                }
                return true;
            },
            function () {
                var message = $.mage.__('Veuillez saisir une valeur inférieure ou égale à %s.');
                return message.replace('%s', this.lteToVal);
            }
        ],
        "greater-than-equals-to": [
            function (value, element, params) {
                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
                    this.gteToVal = $(params).val();
                    return parseFloat(value) >= parseFloat($(params).val());
                }
                return true;
            },
            function () {
                var message = $.mage.__('Veuillez saisir une valeur supérieur ou égale à %s.');
                return message.replace('%s', this.gteToVal);
            }
        ],
        "validate-emails": [
            function (value) {
                if ($.mage.isEmpty(value)) {
                    return true;
                }
                var valid_regexp = /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i,
                    emails = value.split(/[\s\n\,]+/g);
                for (var i = 0; i < emails.length; i++) {
                    if (!valid_regexp.test(emails[i].trim())) {
                        return false;
                    }
                }
                return true;
            }, "Veuillez saisir des emails valides, séparées par des virgules. Par exemple, john@exemple.fr, jean@exemple.fr."
        ],

        "validate-cc-type-select": [
            /**
             * Validate credit card type matches credit card number
             * @param value - select credit card type
             * @param element - element contains the select box for credit card types
             * @param params - selector for credit card number
             * @return {boolean}
             */
                function (value, element, params) {
                if (value && params && creditCartTypes[value]) {
                    return creditCartTypes[value][0].test($(params).val().replace(/\s+/g, ''));
                }
                return false;
            }, 'Le type de carte bleue et le numéro ne semblent pas correspondre.'
        ],
        "validate-cc-number": [
            /**
             * Validate credit card number based on mod 10
             * @param value - credit card number
             * @return {boolean}
             */
                function (value) {
                if (value) {
                    return validateCreditCard(value);
                }
                return false;
            }, 'Veuillez saisir un numéro de carte valide.'
        ],
        "validate-cc-type": [
            /**
             * Validate credit card number is for the correct credit card type
             * @param value - credit card number
             * @param element - element contains credit card number
             * @param params - selector for credit card type
             * @return {boolean}
             */
                function (value, element, params) {
                if (value && params) {
                    var ccType = $(params).val();
                    value = value.replace(/\s/g, '').replace(/\-/g, '');
                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
                        return creditCartTypes[ccType][0].test(value);
                    } else if (creditCartTypes[ccType] && !creditCartTypes[ccType][0]) {
                        return true;
                    }
                }
                return false;
            }, 'Le type de carte bleue et le numéro ne semblent pas correspondre.'
        ],
        "validate-cc-exp": [
            /**
             * Validate credit card expiration date, make sure it's within the year and not before current month
             * @param value - month
             * @param element - element contains month
             * @param params - year selector
             * @return {Boolean}
             */
                function (value, element, params) {
                var isValid = false;
                if (value && params) {
                    var month = value,
                        year = $(params).val(),
                        currentTime = new Date(),
                        currentMonth = currentTime.getMonth() + 1,
                        currentYear = currentTime.getFullYear();
                    isValid = !year || year > currentYear || (year == currentYear && month >= currentMonth);
                }
                return isValid;
            }, 'La date d’expiration est incorrecte.'
        ],
        "validate-cc-cvn": [
            /**
             * Validate credit card cvn based on credit card type
             * @param value - credit card cvn
             * @param element - element contains credit card cvn
             * @param params - credit card type selector
             * @return {*}
             */
                function (value, element, params) {
                if (value && params) {
                    var ccType = $(params).val();
                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
                        return creditCartTypes[ccType][1].test(value);
                    }
                }
                return false;
            }, 'Veuillez saisir un code de vérification valide.'
        ],
        "validate-cc-ukss": [
            /**
             * Validate Switch/Solo/Maestro issue number and start date is filled
             * @param value - input field value
             * @return {*}
             */
                function (value) {
                return value;
            }, 'Veuillez saisir une date de délivrance.'
        ],

        "validate-length": [
            function (v, elm) {
                var reMax = new RegExp(/^maximum-length-[0-9]+$/),
                    reMin = new RegExp(/^minimum-length-[0-9]+$/),
                    validator = this,
                    result = true,
                    length = 0;
                $.each(elm.className.split(' '), function (index, name) {
                    if (name.match(reMax) && result) {
                        length = name.split('-')[2];
                        validator.attrLength = length;
                        result = (v.length <= length);
                    }
                    if (name.match(reMin) && result && $.mage.isEmpty(v)) {
                        length = name.split('-')[2];
                        result = v.length >= length;
                    }
                });
                return result;
            }, function () {
                return $.mage.__("Maximum length of this field must be equal or less than %1 symbols.")
                    .replace('%1', this.attrLength);
            }
        ],
        'required-entry': [
            function (value) {
                return !$.mage.isEmpty(value);
            }, $.mage.__('This is a required field.')
        ],
        'not-negative-amount': [
            function (v) {
                if (v.length)
                    return (/^\s*\d+([,.]\d+)*\s*%?\s*$/).test(v);
                else
                    return true;
            },
            'Veuillez saisir un entier positif dans ce champ.'
        ],
        'validate-per-page-value-list': [
            function (v) {
                var isValid = !$.mage.isEmpty(v);
                var values = v.split(',');
                for (var i = 0; i < values.length; i++) {
                    if (!/^[0-9]+$/.test(values[i])) {
                        isValid = false;
                    }
                }
                return isValid;
            },
            'Veuillez saisir une valeur valide, ex: 10,20,30'
        ],
        'validate-per-page-value': [
            function (v, elm) {
                if ($.mage.isEmpty(v)) {
                    return false;
                }
                var values = $('#' + elm.id + '_values').val().split(',');
                return values.indexOf(v) != -1;
            },
            'Veuillez saisir une valeur valide'
        ],
        'validate-new-password': [
            function (v) {

                if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](v)) {
                    return false;
                }
                if ($.mage.isEmpty(v) && v !== '') {
                    return false;
                }
                return true;
            },
            'Veuillez saisir au moins 6 caractères.'
        ],
        'required-if-not-specified': [
            function (value, element, params) {
                var valid = false;

                // if there is an alternate, determine its validity
                var alternate = $(params);
                if (alternate.length > 0) {
                    valid = this.check(alternate);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        var alternateValue = alternate.val();
                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) {
                            valid = false;
                        }
                    }
                }

                if (!valid)
                    valid = !this.optional(element);

                return valid;
            },
            'This is a required field.'
        ],
        'required-if-all-sku-empty-and-file-not-loaded': [
            function (value, element, params) {
                var valid = false;
                var alternate = $(params.specifiedId);

                if (alternate.length > 0) {
                    valid = this.check(alternate);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        var alternateValue = alternate.val();
                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) {
                            valid = false;
                        }
                    }
                }

                if (!valid)
                    valid = !this.optional(element);

                $('input[' + params.dataSku + '=true]').each(function () {
                    if ($(this).val() !== '') {
                        valid = true;
                    }
                });

                return valid;
            }, 'Veuillez saisir valid SKU key.'
        ],
        'required-if-specified': [
            function (value, element, params) {
                var valid = true;

                // if there is an dependent, determine its validity
                var dependent = $(params);
                if (dependent.length > 0) {
                    valid = this.check(dependent);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        var dependentValue = dependent.val();
                        valid = typeof dependentValue != 'undefined' && dependentValue.length > 0;
                    }
                }

                if (valid) {
                    valid = !this.optional(element);
                } else {
                    valid = true; // dependent was not valid, so don't even check
                }

                return valid;
            },
            'This is a required field.'
        ],
        'required-number-if-specified': [
            function (value, element, params) {
                var valid = true,
                    dependent = $(params),
                    depeValue;

                if (dependent.length) {
                    valid = this.check(dependent);

                    if (valid) {
                        depeValue = dependent[0].value;
                        valid = !!(depeValue && depeValue.length);
                    }
                }

                return valid ? !!value.length : true;
            },
            'Veuillez saisir un nombre.'
        ],
        'datetime-validation': [
            function (value, element) {
                var isValid = true;

                if ($(element).val().length === 0) {
                    isValid = false;
                    $(element).addClass('mage-error');
                }

                return isValid;
            },
            'This is required field'
        ],
        'required-text-swatch-entry': [
            tableSingleValidation,
            'Le champs Admin est requis dans chaque ligne.'
        ],
        'required-visual-swatch-entry': [
            tableSingleValidation,
            'Le champs Admin est requis dans chaque ligne.'
        ],
        'required-dropdown-attribute-entry': [
            tableSingleValidation,
            'Le champs Admin est requis dans chaque ligne.'
        ],
        'validate-item-quantity': [
            function (value, element, params) {
                // obtain values for validation
                var qty = $.mage.parseNumber(value);

                // validate quantity
                var isMinAllowedValid = typeof params.minAllowed === 'undefined' || (qty >= $.mage.parseNumber(params.minAllowed));
                var isMaxAllowedValid = typeof params.maxAllowed === 'undefined' || (qty <= $.mage.parseNumber(params.maxAllowed));
                var isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' || (qty % $.mage.parseNumber(params.qtyIncrements) === 0);

                return isMaxAllowedValid && isMinAllowedValid && isQtyIncrementsValid && qty > 0;
            },
            ''
        ]
    };

    $.each(rules, function (i, rule) {
        rule.unshift(i);
        $.validator.addMethod.apply($.validator, rule);
    });
    $.validator.addClassRules({
        "required-option": {
            required: true
        },
        "required-options-count": {
            required: true
        },
        "validate-both-passwords": {
            'validate-cpassword': true
        }
    });
    $.validator.messages = $.extend($.validator.messages, {
        required: $.mage.__('This is a required field.')
    });

    if ($.metadata) {
        // Setting the type as html5 to enable data-validate attribute
        $.metadata.setType("html5");
    }

    var showLabel = $.validator.prototype.showLabel;
    $.extend(true, $.validator.prototype, {
        showLabel: function (element, message) {
            showLabel.call(this, element, message);

            // ARIA (adding aria-invalid & aria-describedby)
            var label = this.errorsFor(element),
                elem = $(element);

            if (!label.attr('id')) {
                label.attr('id', this.idOrName(element) + '-error');
            }
            elem.attr('aria-invalid', 'true')
                .attr('aria-describedby', label.attr('id'));
        }
    });

    /**
     * Validate form field without instantiating validate plug-in
     * @param {Element||String} element - DOM element or selector
     * @return {Boolean} validation result
     */
    $.validator.validateElement = function (element) {
        element = $(element);
        var form = element.get(0).form,
            validator = form ? $(form).data('validator') : null;
        if (validator) {
            return validator.element(element.get(0));
        } else {
            var valid = true,
                classes = element.prop('class').split(' ');
            $.each(classes, $.proxy(function (i, className) {
                if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {
                    valid = false;
                    return valid;
                }
            }, this));
            return valid;
        }
    };

    var originValidateDelegate = $.fn.validateDelegate;

    $.fn.validateDelegate = function () {
        if (!this[0].form) {
            return this;
        }

        return originValidateDelegate.apply(this, arguments);
    };

    /**
     * Validate single element.
     *
     * @param {Element} element
     * @returns {*}
     */
    $.validator.validateSingleElement = function (element) {
        var errors = {},
            valid = true,
            validateConfig = {
                errorElement: 'label',
                ignore: '.ignore-validate'
            },
            form, validator, classes;

        element = $(element).not(validateConfig.ignore);

        if (!element.length) {
            return true;
        }

        form = element.get(0).form;
        validator = form ? $(form).data('validator') : null;

        if (validator) {
            return validator.element(element.get(0));
        }

        classes = element.prop('class').split(' ');
        validator = element.parent().data('validator') ||
        $.mage.validation(validateConfig, element.parent()).validate;

        element.removeClass(validator.settings.errorClass);
        validator.toHide = validator.toShow;
        validator.hideErrors();
        validator.toShow = validator.toHide = $([]);

        $.each(classes, $.proxy(function (i, className) {
            if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {
                valid = false;
                errors[element.get(0).name] = this.messages[className];
                validator.invalid[element.get(0).name] = true;
                validator.showErrors(errors);

                return valid;
            }
        }, this));

        return valid;
    };

    $.widget("mage.validation", {
        options: {
            meta: "validate",
            onfocusout: false,
            onkeyup: false,
            onclick: false,
            ignoreTitle: true,
            errorClass: 'mage-error',
            errorElement: 'div',
            errorPlacement: function (error, element) {
                var errorPlacement = element;
                // logic for date-picker error placement
                if (element.hasClass('hasDatepicker')) {
                    errorPlacement = element.siblings('img');
                }
                // logic for field wrapper
                var fieldWrapper = element.closest('.addon');
                if (fieldWrapper.length) {
                    errorPlacement = fieldWrapper.after(error);
                }
                //logic for checkboxes/radio
                if (element.is(':checkbox') || element.is(':radio')) {
                    errorPlacement = element.siblings('label').last();
                }
                errorPlacement.after(error);
            }
        },
        /**
         * Check if form pass validation rules without submit
         * @return boolean
         */
        isValid: function () {
            return this.element.valid();
        },

        /**
         * Remove validation error messages
         */
        clearError: function () {
            if (arguments.length) {
                $.each(arguments, $.proxy(function (index, item) {
                    this.validate.prepareElement(item);
                    this.validate.hideErrors();
                }, this));
            } else {
                this.validate.resetForm();
            }
        },
        /**
         * Validation creation
         * @protected
         */
        _create: function () {
            this.validate = this.element.validate(this.options);

            // ARIA (adding aria-required attribute)
            this.element
                .find('.field.required')
                .find('.control')
                .find('input, select, textarea')
                .attr('aria-required', 'true');

            this._listenFormValidate();
        },
        /**
         * Validation listening
         * @protected
         */
        _listenFormValidate: function () {
            $('form').on('invalid-form.validate', function (event, validation) {
                var firstActive = $(validation.errorList[0].element || []),
                    lastActive = $(validation.findLastActive() || validation.errorList.length && validation.errorList[0].element || []);

                if (lastActive.is(':hidden')) {
                    var parent = lastActive.parent();
                    var windowHeight = $(window).height();
                    $('html, body').animate({
                        scrollTop: parent.offset().top - windowHeight / 2
                    });
                }

                // ARIA (removing aria attributes if success)
                var successList = validation.successList;
                if (successList.length) {
                    $.each(successList, function () {
                        $(this)
                            .removeAttr('aria-describedby')
                            .removeAttr('aria-invalid');
                    })
                }
                if (firstActive.length) {
                    firstActive.focus();
                }
            });
        }
    });

    return $.mage.validation;
}));

@jwittorf
Copy link

jwittorf commented Sep 6, 2016

Thanks @jdavisonc for the info and @Choufourax for pointing it out! Worked right away. The placement of require-config.js had me thinking a bit but yeah, just insert it on top of your theme and you're good to go. I ran a php bin/magento setup:static-content:deploy de_DE afterwards, I'll post my German translation in a bit.

@jwittorf
Copy link

jwittorf commented Sep 6, 2016

There you go my German friends:

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
/*jshint regexdash:true eqnull:true browser:true jquery:true*/
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define([
            'jquery',
            'jquery/ui',
            'jquery/validate',
            'mage/translate'
        ], factory);
    } else {
        factory(jQuery);
    }
}(function ($) {
    "use strict";
    $.extend(true, $, {
        // @TODO: Move methods 'isEmpty', 'isEmptyNoTrim', 'parseNumber', 'stripHtml' in file with utility functions
        mage: {
            /**
             * Check if string is empty with trim
             * @param {string} value
             */
            isEmpty: function (value) {
                return (value === '' || value === undefined || (value == null) || (value.length === 0) || /^\s+$/.test(value));
            },

            /**
             * Check if string is empty no trim
             * @param {string} value
             */
            isEmptyNoTrim: function (value) {
                return (value === '' || (value == null) || (value.length === 0));
            },


            /**
             * Checks if {value} is between numbers {from} and {to}
             * @param {string} value
             * @param {string} from
             * @param {string} to
             * @returns {boolean}
             */
            isBetween: function (value, from, to) {
                return ($.mage.isEmpty(from) || value >= $.mage.parseNumber(from)) &&
                    ($.mage.isEmpty(to) || value <= $.mage.parseNumber(to));
            },

            /**
             * Parse price string
             * @param {string} value
             */
            parseNumber: function (value) {
                if (typeof value !== 'string') {
                    return parseFloat(value);
                }
                var isDot = value.indexOf('.');
                var isComa = value.indexOf(',');
                if (isDot !== -1 && isComa !== -1) {
                    if (isComa > isDot) {
                        value = value.replace('.', '').replace(',', '.');
                    } else {
                        value = value.replace(',', '');
                    }
                } else if (isComa !== -1) {
                    value = value.replace(',', '.');
                }
                return parseFloat(value);
            },

            /**
             * Removes HTML tags and space characters, numbers and punctuation.
             * @param value Value being stripped.
             * @return {*}
             */
            stripHtml: function (value) {
                return value.replace(/<.[^<>]*?>/g, ' ').replace(/&nbsp;|&#160;/gi, ' ')
                    .replace(/[0-9.(),;:!?%#$'"_+=\/-]*/g, '');
            }
        }
    });

    $.validator.addMethod = function (name, method, message, dontSkip) {
        $.validator.methods[name] = method;
        $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];

        if (method.length < 3 || dontSkip) {
            $.validator.addClassRules(name, $.validator.normalizeRule(name));
        }
    };

    /**
     * Javascript object with credit card types
     * 0 - regexp for card number
     * 1 - regexp for cvn
     * 2 - check or not credit card number trough Luhn algorithm by
     */
    var creditCartTypes = {
        'SO': [new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
        'SM': [new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
        'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
        'MC': [new RegExp('^5[1-5][0-9]{14}$'), new RegExp('^[0-9]{3}$'), true],
        'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],
        'DI': [new RegExp('^(30[0-5][0-9]{13}|3095[0-9]{12}|35(2[8-9][0-9]{12}|[3-8][0-9]{13})|36[0-9]{12}|3[8-9][0-9]{14}|6011(0[0-9]{11}|[2-4][0-9]{11}|74[0-9]{10}|7[7-9][0-9]{10}|8[6-9][0-9]{10}|9[0-9]{11})|62(2(12[6-9][0-9]{10}|1[3-9][0-9]{11}|[2-8][0-9]{12}|9[0-1][0-9]{11}|92[0-5][0-9]{10})|[4-6][0-9]{13}|8[2-8][0-9]{12})|6(4[4-9][0-9]{13}|5[0-9]{14}))$'), new RegExp('^[0-9]{3}$'), true],
        'JCB': [new RegExp('^(30[0-5][0-9]{13}|3095[0-9]{12}|35(2[8-9][0-9]{12}|[3-8][0-9]{13})|36[0-9]{12}|3[8-9][0-9]{14}|6011(0[0-9]{11}|[2-4][0-9]{11}|74[0-9]{10}|7[7-9][0-9]{10}|8[6-9][0-9]{10}|9[0-9]{11})|62(2(12[6-9][0-9]{10}|1[3-9][0-9]{11}|[2-8][0-9]{12}|9[0-1][0-9]{11}|92[0-5][0-9]{10})|[4-6][0-9]{13}|8[2-8][0-9]{12})|6(4[4-9][0-9]{13}|5[0-9]{14}))$'), new RegExp('^[0-9]{3}$'), true],
        'OT': [new RegExp('^([0-9]+)$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), false],
        'DN': [new RegExp('^3((0([0-5]\\d*)?)|[689]\\d*)?$'), new RegExp('^[0-9]{3}$'), true],
        'UN': [new RegExp('^6(2\\d*)?$'), new RegExp('^[0-9]{3}$'), true],
        'MI': [new RegExp('^(5(0|[6-9])|63|67(?!59|6770|6774))\\d*$'), new RegExp('^[0-9]{3}$'), true],
        'MD': [new RegExp('^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\d*$'), new RegExp('^[0-9]{3}$'), true]
    };

    /**
     * validate credit card number using mod10
     * @param s
     * @return {Boolean}
     */
    function validateCreditCard(s) {
        // remove non-numerics
        var v = "0123456789",
            w = "", i, j, k, m, c, a, x;
        for (i = 0; i < s.length; i++) {
            x = s.charAt(i);
            if (v.indexOf(x, 0) != -1)
                w += x;
        }
        // validate number
        j = w.length / 2;
        k = Math.floor(j);
        m = Math.ceil(j) - k;
        c = 0;
        for (i = 0; i < k; i++) {
            a = w.charAt(i * 2 + m) * 2;
            c += a > 9 ? Math.floor(a / 10 + a % 10) : a;
        }
        for (i = 0; i < k + m; i++) {
            c += w.charAt(i * 2 + 1 - m) * 1;
        }
        return (c % 10 === 0);
    }

    /**
     * validate all table required inputs at once, using single hidden input
     * @param {String} value
     * @param {HTMLElement} element
     *
     * @return {Boolean}
     */
    function tableSingleValidation(value, element) {
        var empty = $(element).closest('table')
            .find('input.required-option:visible')
            .filter(function (i, el) {
                return $.mage.isEmpty(el.value);
            })
            .length;
        return empty === 0;
    }

    /**
     * Collection of validation rules including rules from additional-methods.js
     * @type {Object}
     */
    var rules = {
        "max-words": [
            function (value, element, params) {
                return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length < params;
            },
            'Bitte geben Sie {0} Wörter oder weniger ein.'
        ],
        "min-words": [
            function (value, element, params) {
                return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params;
            },
            'Bitte geben Sie mindestens {0} Wörter ein.'
        ],
        "range-words": [
            function (value, element, params) {
                return this.optional(element) ||
                    $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params[0] &&
                    value.match(/bw+b/g).length < params[1];
            },
            'Bitte geben Sie zwischen {0} und {1} Wörter ein.'
        ],
        "letters-with-basic-punc": [
            function (value, element) {
                return this.optional(element) || /^[a-z\-.,()'\"\s]+$/i.test(value);
            },
            'Bitte geben Sie nur Buchstaben und allgemeine Zeichensetzung ein.'
        ],
        "alphanumeric": [
            function (value, element) {
                return this.optional(element) || /^\w+$/i.test(value);
            },
            'Bitte geben Sie nur Buchstaben, Zahlen, Leerzeichen oder Unterstriche (_) ein.'
        ],
        "letters-only": [
            function (value, element) {
                return this.optional(element) || /^[a-z]+$/i.test(value);
            },
            'Bitte geben Sie nur Buchstaben ein.'
        ],
        "no-whitespace": [
            function (value, element) {
                return this.optional(element) || /^\S+$/i.test(value);
            },
            'Bitte geben Sie keine Leerzeichen ein.'
        ],
        "zip-range": [
            function (value, element) {
                return this.optional(element) || /^90[2-5]-\d{2}-\d{4}$/.test(value);
            },
            'Ihre Postleitzahl muss zwischen 902xx-xxxx und 905-xx-xxxx liegen.'
        ],
        "integer": [
            function (value, element) {
                return this.optional(element) || /^-?\d+$/.test(value);
            },
            'Bitte gebene Sie eine positive oder negative Dezimalzahl (Kommazahl) ein.'
        ],
        "vinUS": [
            function (v) {
                if (v.length !== 17) {
                    return false;
                }
                var i, n, d, f, cd, cdv;
                var LL = ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
                var VL = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9];
                var FL = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
                var rs = 0;
                for (i = 0; i < 17; i++) {
                    f = FL[i];
                    d = v.slice(i, i + 1);
                    if (i === 8) {
                        cdv = d;
                    }
                    if (!isNaN(d)) {
                        d *= f;
                    } else {
                        for (n = 0; n < LL.length; n++) {
                            if (d.toUpperCase() === LL[n]) {
                                d = VL[n];
                                d *= f;
                                if (isNaN(cdv) && n === 8) {
                                    cdv = LL[n];
                                }
                                break;
                            }
                        }
                    }
                    rs += d;
                }
                cd = rs % 11;
                if (cd === 10) {
                    cd = "X";
                }
                if (cd === cdv) {
                    return true;
                }
                return false;
            },
            'Die eingegebene US-amerikanische Fahrzeugidentifikationsnummer (vehicle identification number (VIN)) ist ungültig.'
        ],
        "dateITA": [
            function (value, element) {
                var check = false;
                var re = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
                if (re.test(value)) {
                    var adata = value.split('/');
                    var gg = parseInt(adata[0], 10);
                    var mm = parseInt(adata[1], 10);
                    var aaaa = parseInt(adata[2], 10);
                    var xdata = new Date(aaaa, mm - 1, gg);
                    if ((xdata.getFullYear() === aaaa) &&
                        (xdata.getMonth() === mm - 1) && (xdata.getDate() === gg )) {
                        check = true;
                    } else {
                        check = false;
                    }
                } else {
                    check = false;
                }
                return this.optional(element) || check;
            },
            'Bitte geben Sie ein korrektes italienisches Datum ein.'
        ],
        "dateNL": [
            function (value, element) {
                return this.optional(element) || /^\d\d?[\.\/-]\d\d?[\.\/-]\d\d\d?\d?$/.test(value);
            },
            'Bitte geben Sie ein korrektes niederländisches Datum ein.'
        ],
        "time": [
            function (value, element) {
                return this.optional(element) || /^([01]\d|2[0-3])(:[0-5]\d){0,2}$/.test(value);
            },
            'Bitte geben Sie eine gültige Zeit zwischen 00:00 und 23:59 ein.'
        ],
        "time12h": [
            function (value, element) {
                return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))$/i.test(value);
            },
            'Bitte geben Sie eine gültige Zeit zwischen 00:00 am und 12:00 pm ein.'
        ],
        "phoneUS": [
            function (phone_number, element) {
                phone_number = phone_number.replace(/\s+/g, "");
                return this.optional(element) || phone_number.length > 9 &&
                    phone_number.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
            },
            'Bitte geben Sie eine gültige US-amerikanische Telefonnummer an.'
        ],
        "phoneUK": [
            function (phone_number, element) {
                return this.optional(element) || phone_number.length > 9 &&
                    phone_number.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/);
            },
            'Bitte geben Sie eine gültige britische Telefonnummer an.'
        ],
        "mobileUK": [
            function (phone_number, element) {
                return this.optional(element) || phone_number.length > 9 &&
                    phone_number.match(/^((0|\+44)7(5|6|7|8|9){1}\d{2}\s?\d{6})$/);
            },
            'Bitte geben Sie eine gültige britische mobile Telefonnummer an.'
        ],
        "stripped-min-length": [
            function (value, element, param) {
                return $(value).text().length >= param;
            },
            'Bitte geben Sie mindestens {0} Zeichen ein.'
        ],
        "email2": [
            function (value, element) {
                return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
            },
            $.validator.messages.email
        ],
        "url2": [
            function (value, element) {
                return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
            },
            $.validator.messages.url
        ],
        "credit-card-types": [
            function (value, element, param) {
                if (/[^0-9-]+/.test(value)) {
                    return false;
                }
                value = value.replace(/\D/g, "");

                var validTypes = 0x0000;

                if (param.mastercard) {
                    validTypes |= 0x0001;
                }
                if (param.visa) {
                    validTypes |= 0x0002;
                }
                if (param.amex) {
                    validTypes |= 0x0004;
                }
                if (param.dinersclub) {
                    validTypes |= 0x0008;
                }
                if (param.enroute) {
                    validTypes |= 0x0010;
                }
                if (param.discover) {
                    validTypes |= 0x0020;
                }
                if (param.jcb) {
                    validTypes |= 0x0040;
                }
                if (param.unknown) {
                    validTypes |= 0x0080;
                }
                if (param.all) {
                    validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;
                }
                if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard
                    return value.length === 16;
                }
                if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa
                    return value.length === 16;
                }
                if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex
                    return value.length === 15;
                }
                if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub
                    return value.length === 14;
                }
                if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute
                    return value.length === 15;
                }
                if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover
                    return value.length === 16;
                }
                if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb
                    return value.length === 16;
                }
                if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb
                    return value.length === 15;
                }
                if (validTypes & 0x0080) { //unknown
                    return true;
                }
                return false;
            },
            'Bitte geben Sie eine gültige Kreditkartennummer ein.'
        ],
        "ipv4": [
            function (value, element) {
                return this.optional(element) || /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);
            },
            'Bitte geben Sie eine gültige v4 Adresse ein.'
        ],
        "ipv6": [
            function (value, element) {
                return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
            },
            'Bitte geben Sie eine gültige v6 Adresse ein.'
        ],
        "pattern": [
            function (value, element, param) {
                return this.optional(element) || param.test(value);
            },
            'Ungültiges Format.'
        ],
        "allow-container-className": [
            function (element) {
                if (element.type === 'radio' || element.type === 'checkbox') {
                    return $(element).hasClass('change-container-classname');
                }
            },
            ''
        ],
        "validate-no-html-tags": [
            function (value) {
                return !/<(\/)?\w+/.test(value);
            },
            'HTML Tags sind nicht erlaubt.'
        ],
        "validate-select": [
            function (value) {
                return ((value !== "none") && (value != null) && (value.length !== 0));
            },
            'Bitte wählen Sie eine Option.'
        ],
        "validate-no-empty": [
            function (value) {
                return !$.mage.isEmpty(value);
            },
            'Leerer Wert.'
        ],
        "validate-alphanum-with-spaces": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9 ]+$/.test(v);
            },
            'Bitte geben Sie nur Buchstaben (a-z oder A-Z), Zahlen (0-9) oder Leerzeichen ein.'
        ],
        "validate-data": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);
            },
            'Bitte geben Sie nur Buchstaben (a-z oder A-Z), Zahlen (0-9) oder Unterstriche (_) ein. Das erste Zeichen sollte ein Buchstabe sein.'
        ],
        "validate-street": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v);
            },
            'Bitte geben Sie nur Buchstaben (a-z oder A-Z), Zahlen (0-9), Leerzeichen und "#" ein.'
        ],
        "validate-phoneStrict": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
            },
            'Bitte geben Sie eine gültige Telefonnummer ein. Zum Beispiel (123) 456-7890 or 123-456-7890.'
        ],
        "validate-phoneLax": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^((\d[\-. ]?)?((\(\d{3}\))|\d{3}))?[\-. ]?\d{3}[\-. ]?\d{4}$/.test(v);
            },
            'Bitte geben Sie eine gültige Telefonnummer ein. Zum Beispiel (123) 456-7890 or 123-456-7890.'
        ],
        "validate-fax": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
            },
            'Bitte geben Sie eine gültige Faxnummer ein. (Zum Beispiel: 123-456-7890).'
        ],
        "validate-email": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i.test(v);
            },
            'Bitte geben Sie eine gültige E-Mailadresse an (Zum Beispiel: max-mustermann@domain.de).'
        ],
        "validate-emailSender": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[\S ]+$/.test(v);
            },
            'Bitte geben Sie eine gültige E-Mailadresse an (Zum Beispiel: max-mustermann@domain.de).'
        ],
        "validate-password": [
            function (v) {
                if (v == null) {
                    return false;
                }
                /*strip leading and trailing spaces*/
                var pass = $.trim(v);
                if (!pass.length) {
                    return true;
                }
                return !(pass.length > 0 && pass.length < 6);
            },
            'Bitte geben Sie 6 oder mehr Zeichen ein. Zuvorgestellte und abschließende Leerzeichen werden ignoriert.'
        ],
        "validate-admin-password": [
            function (v) {
                if (v == null) {
                    return false;
                }
                var pass = $.trim(v);
                /*strip leading and trailing spaces*/
                if (0 === pass.length) {
                    return true;
                }
                if (!(/[a-z]/i.test(v)) || !(/[0-9]/.test(v))) {
                    return false;
                }
                if (pass.length < 7) {
                    return false;
                }
                return true;
            },
            'Bitte geben Sie 7 oder mehr Zeichen ein, verwenden Sie numerische und alphabetische.'
        ],
        "validate-customer-password": [
            function (v, elm) {
                var validator = this,
                    length = 0,
                    counter = 0;
                var passwordMinLength = $(elm).data('password-min-length');
                var passwordMinCharacterSets = $(elm).data('password-min-character-sets');
                var pass = $.trim(v);
                var result = pass.length >= passwordMinLength;
                if (result == false) {
                    validator.passwordErrorMessage = $.mage.__(
                        "Dieses Feld muss mindestens %1 Zeichen enthalten." +
                        " Zuvorgestellte und abschließende Leerzeichen werden ignoriert."
                    ).replace('%1', passwordMinLength);
                    return result;
                }
                if (pass.match(/\d+/)) {
                    counter ++;
                }
                if (pass.match(/[a-z]+/)) {
                    counter ++;
                }
                if (pass.match(/[A-Z]+/)) {
                    counter ++;
                }
                if (pass.match(/[^a-zA-Z0-9]+/)) {
                    counter ++;
                }
                if (counter < passwordMinCharacterSets) {
                    result = false;
                    validator.passwordErrorMessage = $.mage.__(
                        "Es müssen mindestens %1 verschiedene Zeichenarten verwendet werden." +
                        " Zeichenarten: Kleinbuchstaben, Großbuchstaben, Zahlen, Sonderzeichen."
                    ).replace('%1', passwordMinCharacterSets);
                }
                return result;
            }, function () {
                return this.passwordErrorMessage;
            }
        ],
        "validate-url": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = (v || '').replace(/^\s+/, '').replace(/\s+$/, '');
                return (/^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(v);

            },
            'Bitte geben Sie eine gültige URL an. Ein Protokoll wird benötigt (http://, https:// or ftp://).'
        ],
        "validate-clean-url": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v);

            },
            'Bitte geben Sie eine gültige URL ein. Zum Beispiel http://www.beispiel.de or www.beispiel.de.'
        ],
        "validate-xml-identifier": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v);

            },
            'Bitte geben Sie einen gültigen XML Identifier ein (Zum Beispiel: etwas_1, block5, id-4).'
        ],
        "validate-ssn": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v);

            },
            'Bitte geben Sie eine gültige Sozialversicherungsnummer ein (Zum Beispiel: 123-45-6789).'
        ],
        "validate-zip-us": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v);

            },
            'Bitte geben Sie eine gültige US-amerikanische Postleitzahl (ZIP) ein (Zum Beispiel: 90602 or 90602-1234).'
        ],
        "validate-date-au": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
                if ($.mage.isEmpty(v) || !regex.test(v)) {
                    return false;
                }
                var d = new Date(v.replace(regex, '$2/$1/$3'));
                return parseInt(RegExp.$2, 10) === (1 + d.getMonth()) &&
                    parseInt(RegExp.$1, 10) === d.getDate() &&
                    parseInt(RegExp.$3, 10) === d.getFullYear();

            },
            'Bitte verwenden Sie dieses Datumsformat: tt/mm/jjjj. Zum Beispiel 17/03/2006 für den 17. März 2006.'
        ],
        "validate-currency-dollar": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v);

            },
            'Bitte geben Sie einen gültigen $ Wert an. Zum Beispiel $100.00.'
        ],
        "validate-not-negative-number": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);
                return !isNaN(v) && v >= 0;

            },
            'Bitte geben Sie keine negative Zahl ein.'
        ],
        // validate-not-negative-number should be replaced in all places with this one and then removed
        "validate-zero-or-greater": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);
                return !isNaN(v) && v >= 0;

            },
            'Bitte geben Sie eine Zahl größer oder gleich 0 ein.'
        ],
        "validate-greater-than-zero": [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);
                return !isNaN(v) && v > 0;
            },
            'Bitte geben Sie eine Zahl größer als 0 ein.'
        ],
        "validate-css-length": [
            function (v) {
                if (v !== '') {
                    return (/^[0-9]*\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(v);
                }
                return true;
            },
            'Bitte geben Sie eine valide CSS-Breite ein (Zum Beispiel: 100px, 77pt, 20em, .5ex or 50%).'
        ],
        /** @description Additional methods */
        "validate-number": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || (!isNaN($.mage.parseNumber(v)) && /^\s*-?\d*(\.\d*)?\s*$/.test(v));
            },
            'Bitte geben Sie eine gültige Nummer ein.'
        ],
        "required-number": [
            function (v) {
                return !!v.length;
            },
            'Bitte geben Sie eine gültige Nummer ein.'
        ],
        "validate-number-range": [
            function (v, elm, param) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                var numValue = $.mage.parseNumber(v);
                if (isNaN(numValue)) {
                    return false;
                }

                var dataAttrRange = /^(-?[\d.,]+)?-(-?[\d.,]+)?$/,
                    classNameRange = /^number-range-(-?[\d.,]+)?-(-?[\d.,]+)?$/,
                    result = true,
                    range, m, classes, ii;

                range = param;
                if (typeof range === 'object') {
                    m = dataAttrRange.exec(range);
                    if (m) {
                        result = result && $.mage.isBetween(numValue, m[1], m[2]);
                    }
                } else if (elm && elm.className) {
                    classes = elm.className.split(" ");
                    ii = classes.length;

                    while (ii--) {
                        range = classes[ii];
                        m = classNameRange.exec(range);
                        if (m) {
                            result = result && $.mage.isBetween(numValue, m[1], m[2]);
                            break;
                        }
                    }
                }

                return result;
            },
            'Der Wert ist nicht innerhalb des angegebenen Bereichs.',
            true
        ],
        "validate-digits": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || !/[^\d]/.test(v);
            },
            'Bitte geben Sie eine gültige Nummer ein.'
        ],
        "validate-digits-range": [
            function (v, elm, param) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                var numValue = $.mage.parseNumber(v);
                if (isNaN(numValue)) {
                    return false;
                }

                var dataAttrRange = /^(-?\d+)?-(-?\d+)?$/,
                    classNameRange = /^digits-range-(-?\d+)?-(-?\d+)?$/,
                    result = true,
                    range, m, classes, ii;
                range = param;

                if (typeof range === 'object') {
                    m = dataAttrRange.exec(range);
                    if (m) {
                        result = result && $.mage.isBetween(numValue, m[1], m[2]);
                    }
                } else if (elm && elm.className) {
                    classes = elm.className.split(" ");
                    ii = classes.length;

                    while (ii--) {
                        range = classes[ii];
                        m = classNameRange.exec(range);
                        if (m) {
                            result = result && $.mage.isBetween(numValue, m[1], m[2]);
                            break;
                        }
                    }
                }

                return result;
            },
            'Der Wert ist nicht innerhalb des angegebenen Bereichs.',
            true
        ],
        'validate-range': [
            function (v, elm) {
                var minValue, maxValue;
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](v)) {
                    minValue = maxValue = $.mage.parseNumber(v);
                } else {
                    var ranges = /^(-?\d+)?-(-?\d+)?$/.exec(v);

                    if (ranges) {
                        minValue = $.mage.parseNumber(ranges[1]);
                        maxValue = $.mage.parseNumber(ranges[2]);
                        if (minValue > maxValue) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
                var reRange = /^range-(-?\d+)?-(-?\d+)?$/,
                    result = true;

                var values = $(elm).prop('class').split(" ");

                for (var i = values.length - 1; i >= 0; i--) {
                    var name = values[i];
                    var validRange = reRange.exec(name);
                    if (validRange) {
                        var minValidRange = $.mage.parseNumber(validRange[1]);
                        var maxValidRange = $.mage.parseNumber(validRange[2]);
                        result = result &&
                        (isNaN(minValidRange) || minValue >= minValidRange) &&
                        (isNaN(maxValidRange) || maxValue <= maxValidRange);
                    }
                }
                return result;
            },
            'Der Wert ist nicht innerhalb des angegebenen Bereichs.'
        ],
        "validate-alpha": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+$/.test(v);
            },
            'Bitte geben Sie nur Buchstaben (a-z oder A-Z), Zahlen (0-9) ein.'
        ],
        "validate-code": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-z]+[a-z0-9_]+$/.test(v);
            },
            'Bitte geben Sie nur Buchstaben (a-z oder A-Z), Zahlen (0-9) oder Unterstriche (_) ein. Das erste Zeichen sollte ein Buchstabe sein.'
        ],
        "validate-alphanum": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9]+$/.test(v);
            },
            'Bitte geben Sie nur Buchstaben (a-z oder A-Z) oder Zahlen (0-9) ein. Leerzeichen und andere Sonderzeichen sind nicht erlaubt.'
        ],
        "validate-date": [
            function (v) {
                var test = new Date(v);
                return $.mage.isEmptyNoTrim(v) || !isNaN(test);
            }, 'Bitte geben Sie ein gültiges Datum ein.'

        ],
        "validate-date-range": [
            function (v, elm) {
                var m = /\bdate-range-(\w+)-(\w+)\b/.exec(elm.className);
                if (!m || m[2] === 'to' || $.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                var currentYear = new Date().getFullYear() + '';
                var normalizedTime = function (v) {
                    v = v.split(/[.\/]/);
                    if (v[2] && v[2].length < 4) {
                        v[2] = currentYear.substr(0, v[2].length) + v[2];
                    }
                    return new Date(v.join('/')).getTime();
                };

                var dependentElements = $(elm.form).find('.validate-date-range.date-range-' + m[1] + '-to');
                return !dependentElements.length || $.mage.isEmptyNoTrim(dependentElements[0].value) ||
                    normalizedTime(v) <= normalizedTime(dependentElements[0].value);
            },
            'Stellen Sie sicher, dass das "Bis"-Datum gleich oder nach dem "Vom"-Datum liegt.'
        ],
        "validate-cpassword": [
            function () {
                var conf = $('#confirmation').length > 0 ? $('#confirmation') : $($('.validate-cpassword')[0]);
                var pass = false;
                if ($('#password')) {
                    pass = $('#password');
                }
                var passwordElements = $('.validate-password');
                for (var i = 0; i < passwordElements.length; i++) {
                    var passwordElement = $(passwordElements[i]);
                    if (passwordElement.closest('form').attr('id') === conf.closest('form').attr('id')) {
                        pass = passwordElement;
                    }
                }
                if ($('.validate-admin-password').length) {
                    pass = $($('.validate-admin-password')[0]);
                }
                return (pass.val() === conf.val());
            },
            'Bitte stellen Sie sicher, dass die Passwörter übereinstimmen.'
        ],
        "validate-identifier": [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/.test(v);
            },
            'Bitte geben Sie einen gültigen URL Schlüssel ein (Zum Beispiel: "beispiel-seite", "beispiel-seite.html" or "andere-ebene/beispiel-seite").'
        ],
        "validate-zip-international": [
            /*function(v) {
             // @TODO: Cleanup
             return Validation.get('IsEmpty').test(v) || /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v);
             }*/
            function () {
                return true;
            },
            'Bitte geben Sie eine gültige Postleitzahl ein.'
        ],
        "validate-one-required": [
            function (v, elm) {
                var p = $(elm).parent();
                var options = p.find('input');
                return options.map(function (elm) {
                        return $(elm).val();
                    }).length > 0;
            },
            'Bitte wählen Sie eine der oberen Optionen.'
        ],
        "validate-state": [
            function (v) {
                return (v !== 0 || v === '');
            },
            'Bitte whälen Sie Staat/Bundesland/Provinz.'
        ],
        "required-file": [
            function (v, elm) {
                var result = !$.mage.isEmptyNoTrim(v);
                if (!result) {
                    var ovId = $(elm).attr('id') + '_value';
                    if ($(ovId)) {
                        result = !$.mage.isEmptyNoTrim($(ovId).val());
                    }
                }
                return result;
            },
            'Bitte wählen Sie eine Datei aus.'
        ],
        "validate-ajax-error": [
            function (v, element) {
                element = $(element);
                element.on('change.ajaxError', function () {
                    element.removeClass('validate-ajax-error');
                    element.off('change.ajaxError');
                });
                return !element.hasClass('validate-ajax-error');
            },
            ''
        ],
        "validate-optional-datetime": [
            function (v, elm, param) {
                var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]'),
                    hasWithValue = false, hasWithNoValue = false,
                    pattern = /day_part$/i;
                for (var i = 0; i < dateTimeParts.length; i++) {
                    if (!pattern.test($(dateTimeParts[i]).attr('id'))) {
                        if ($(dateTimeParts[i]).val() === "") {
                            hasWithValue = true;
                        } else {
                            hasWithNoValue = true;
                        }
                    }
                }
                return hasWithValue ^ hasWithNoValue;
            },
            'Das Feld ist nicht vollständig.'
        ],
        "validate-required-datetime": [
            function (v, elm, param) {
                var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]');
                for (var i = 0; i < dateTimeParts.length; i++) {
                    if (dateTimeParts[i].value === "") {
                        return false;
                    }
                }
                return true;
            },
            'Dies ist ein Pflichtfeld.'
        ],
        "validate-one-required-by-name": [
            function (v, elm, selector) {
                var name = elm.name.replace(/([\\"])/g, '\\$1'),
                    container = this.currentForm,
                    selector = selector === true ? 'input[name="' + name + '"]:checked' : selector;

                return !!container.querySelectorAll(selector).length;
            },
            'Bitte wählen Sie eine der Optionen.'
        ],
        "less-than-equals-to": [
            function (value, element, params) {
                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
                    this.lteToVal = $(params).val();
                    return parseFloat(value) <= parseFloat($(params).val());
                }
                return true;
            },
            function () {
                var message = $.mage.__('Bitte geben Sie einen Wert kleiner oder gleich %s ein.');
                return message.replace('%s', this.lteToVal);
            }
        ],
        "greater-than-equals-to": [
            function (value, element, params) {
                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
                    this.gteToVal = $(params).val();
                    return parseFloat(value) >= parseFloat($(params).val());
                }
                return true;
            },
            function () {
                var message = $.mage.__('Bitte geben Sie einen Wert größer oder gleich %s ein.');
                return message.replace('%s', this.gteToVal);
            }
        ],
        "validate-emails": [
            function (value) {
                if ($.mage.isEmpty(value)) {
                    return true;
                }
                var valid_regexp = /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i,
                    emails = value.split(/[\s\n\,]+/g);
                for (var i = 0; i < emails.length; i++) {
                    if (!valid_regexp.test(emails[i].trim())) {
                        return false;
                    }
                }
                return true;
            }, "Bitte geben Sie eine oder mehrere gültige E-Mailadresse(n) ein, per Komma getrennt. Zum Beispiel, johndoe@domain.com, johnsmith@domain.com."
        ],

        "validate-cc-type-select": [
            /**
             * Validate credit card type matches credit card number
             * @param value - select credit card type
             * @param element - element contains the select box for credit card types
             * @param params - selector for credit card number
             * @return {boolean}
             */
                function (value, element, params) {
                if (value && params && creditCartTypes[value]) {
                    return creditCartTypes[value][0].test($(params).val().replace(/\s+/g, ''));
                }
                return false;
            }, 'Der Kartentyp passt nicht zur Kreditkartennummer.'
        ],
        "validate-cc-number": [
            /**
             * Validate credit card number based on mod 10
             * @param value - credit card number
             * @return {boolean}
             */
                function (value) {
                if (value) {
                    return validateCreditCard(value);
                }
                return false;
            }, 'Bitte geben Sie eine gültige Kreditkartennummer ein.'
        ],
        "validate-cc-type": [
            /**
             * Validate credit card number is for the correct credit card type
             * @param value - credit card number
             * @param element - element contains credit card number
             * @param params - selector for credit card type
             * @return {boolean}
             */
                function (value, element, params) {
                if (value && params) {
                    var ccType = $(params).val();
                    value = value.replace(/\s/g, '').replace(/\-/g, '');
                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
                        return creditCartTypes[ccType][0].test(value);
                    } else if (creditCartTypes[ccType] && !creditCartTypes[ccType][0]) {
                        return true;
                    }
                }
                return false;
            }, 'Die Kreditkartennummer passt nicht zum Kartentypen .'
        ],
        "validate-cc-exp": [
            /**
             * Validate credit card expiration date, make sure it's within the year and not before current month
             * @param value - month
             * @param element - element contains month
             * @param params - year selector
             * @return {Boolean}
             */
                function (value, element, params) {
                var isValid = false;
                if (value && params) {
                    var month = value,
                        year = $(params).val(),
                        currentTime = new Date(),
                        currentMonth = currentTime.getMonth() + 1,
                        currentYear = currentTime.getFullYear();
                    isValid = !year || year > currentYear || (year == currentYear && month >= currentMonth);
                }
                return isValid;
            }, 'Ungültiges Ablaufdatum der Kreditkarte.'
        ],
        "validate-cc-cvn": [
            /**
             * Validate credit card cvn based on credit card type
             * @param value - credit card cvn
             * @param element - element contains credit card cvn
             * @param params - credit card type selector
             * @return {*}
             */
                function (value, element, params) {
                if (value && params) {
                    var ccType = $(params).val();
                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
                        return creditCartTypes[ccType][1].test(value);
                    }
                }
                return false;
            }, 'Bitte geben Sie eine gültige Nummer zur Validierung der Kreidtkarte (CVV) ein.'
        ],
        "validate-cc-ukss": [
            /**
             * Validate Switch/Solo/Maestro issue number and start date is filled
             * @param value - input field value
             * @return {*}
             */
                function (value) {
                return value;
            }, 'Bitte geben Sie die Ausgabenummer oder das Startdatum für switch/solo Karten ein.'
        ],

        "validate-length": [
            function (v, elm) {
                var reMax = new RegExp(/^maximum-length-[0-9]+$/),
                    reMin = new RegExp(/^minimum-length-[0-9]+$/),
                    validator = this,
                    result = true,
                    length = 0;
                $.each(elm.className.split(' '), function (index, name) {
                    if (name.match(reMax) && result) {
                        length = name.split('-')[2];
                        validator.attrLength = length;
                        result = (v.length <= length);
                    }
                    if (name.match(reMin) && result && $.mage.isEmpty(v)) {
                        length = name.split('-')[2];
                        result = v.length >= length;
                    }
                });
                return result;
            }, function () {
                return $.mage.__("Die maximale Länge muss kleiner oder gleich %1 Zeichen sein.")
                    .replace('%1', this.attrLength);
            }
        ],
        'required-entry': [
            function (value) {
                return !$.mage.isEmpty(value);
            }, $.mage.__('Dies ist ein Pflichtfeld.')
        ],
        'not-negative-amount': [
            function (v) {
                if (v.length)
                    return (/^\s*\d+([,.]\d+)*\s*%?\s*$/).test(v);
                else
                    return true;
            },
            'Bitte geben Sie eine positive Nummer ein.'
        ],
        'validate-per-page-value-list': [
            function (v) {
                var isValid = !$.mage.isEmpty(v);
                var values = v.split(',');
                for (var i = 0; i < values.length; i++) {
                    if (!/^[0-9]+$/.test(values[i])) {
                        isValid = false;
                    }
                }
                return isValid;
            },
            'Bitte geben Sie einen gültigen Wert ein, zum Beispiel: 10,20,30'
        ],
        'validate-per-page-value': [
            function (v, elm) {
                if ($.mage.isEmpty(v)) {
                    return false;
                }
                var values = $('#' + elm.id + '_values').val().split(',');
                return values.indexOf(v) != -1;
            },
            'Bitte geben Sie einen gültigen Wert aus der Liste ein.'
        ],
        'validate-new-password': [
            function (v) {

                if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](v)) {
                    return false;
                }
                if ($.mage.isEmpty(v) && v !== '') {
                    return false;
                }
                return true;
            },
            'Bitte geben Sie 6 oder mehr Zeichen ein. Zuvorgestellte und abschließende Leerzeichen werden ignoriert.'
        ],
        'required-if-not-specified': [
            function (value, element, params) {
                var valid = false;

                // if there is an alternate, determine its validity
                var alternate = $(params);
                if (alternate.length > 0) {
                    valid = this.check(alternate);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        var alternateValue = alternate.val();
                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) {
                            valid = false;
                        }
                    }
                }

                if (!valid)
                    valid = !this.optional(element);

                return valid;
            },
            'Dies ist ein Pflichtfeld.'
        ],
        'required-if-all-sku-empty-and-file-not-loaded': [
            function (value, element, params) {
                var valid = false;
                var alternate = $(params.specifiedId);

                if (alternate.length > 0) {
                    valid = this.check(alternate);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        var alternateValue = alternate.val();
                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) {
                            valid = false;
                        }
                    }
                }

                if (!valid)
                    valid = !this.optional(element);

                $('input[' + params.dataSku + '=true]').each(function () {
                    if ($(this).val() !== '') {
                        valid = true;
                    }
                });

                return valid;
            }, 'Bitte geben Sie einen gültigen Artikelnummer-Schlüssel ein.'
        ],
        'required-if-specified': [
            function (value, element, params) {
                var valid = true;

                // if there is an dependent, determine its validity
                var dependent = $(params);
                if (dependent.length > 0) {
                    valid = this.check(dependent);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        var dependentValue = dependent.val();
                        valid = typeof dependentValue != 'undefined' && dependentValue.length > 0;
                    }
                }

                if (valid) {
                    valid = !this.optional(element);
                } else {
                    valid = true; // dependent was not valid, so don't even check
                }

                return valid;
            },
            'Dies ist ein Pflichtfeld.'
        ],
        'required-number-if-specified': [
            function (value, element, params) {
                var valid = true,
                    dependent = $(params),
                    depeValue;

                if (dependent.length) {
                    valid = this.check(dependent);

                    if (valid) {
                        depeValue = dependent[0].value;
                        valid = !!(depeValue && depeValue.length);
                    }
                }

                return valid ? !!value.length : true;
            },
            'Bitte geben Sie eine gültige Nummer ein.'
        ],
        'datetime-validation': [
            function (value, element) {
                var isValid = true;

                if ($(element).val().length === 0) {
                    isValid = false;
                    $(element).addClass('mage-error');
                }

                return isValid;
            },
            'Dies ist ein Pflichtfeld.'
        ],
        'required-text-swatch-entry': [
            tableSingleValidation,
            'Admin ist ein Pflichtfeld in jeder Zeile.'
        ],
        'required-visual-swatch-entry': [
            tableSingleValidation,
            'Admin ist ein Pflichtfeld in jeder Zeile.'
        ],
        'required-dropdown-attribute-entry': [
            tableSingleValidation,
            'Admin ist ein Pflichtfeld in jeder Zeile.'
        ],
        'validate-item-quantity': [
            function (value, element, params) {
                // obtain values for validation
                var qty = $.mage.parseNumber(value);

                // validate quantity
                var isMinAllowedValid = typeof params.minAllowed === 'undefined' || (qty >= $.mage.parseNumber(params.minAllowed));
                var isMaxAllowedValid = typeof params.maxAllowed === 'undefined' || (qty <= $.mage.parseNumber(params.maxAllowed));
                var isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' || (qty % $.mage.parseNumber(params.qtyIncrements) === 0);

                return isMaxAllowedValid && isMinAllowedValid && isQtyIncrementsValid && qty > 0;
            },
            ''
        ]
    };

    $.each(rules, function (i, rule) {
        rule.unshift(i);
        $.validator.addMethod.apply($.validator, rule);
    });
    $.validator.addClassRules({
        "required-option": {
            required: true
        },
        "required-options-count": {
            required: true
        },
        "validate-both-passwords": {
            'validate-cpassword': true
        }
    });
    $.validator.messages = $.extend($.validator.messages, {
        required: $.mage.__('Dies ist ein Pflichtfeld.')
    });

    if ($.metadata) {
        // Setting the type as html5 to enable data-validate attribute
        $.metadata.setType("html5");
    }

    var showLabel = $.validator.prototype.showLabel;
    $.extend(true, $.validator.prototype, {
        showLabel: function (element, message) {
            showLabel.call(this, element, message);

            // ARIA (adding aria-invalid & aria-describedby)
            var label = this.errorsFor(element),
                elem = $(element);

            if (!label.attr('id')) {
                label.attr('id', this.idOrName(element) + '-error');
            }
            elem.attr('aria-invalid', 'true')
                .attr('aria-describedby', label.attr('id'));
        }
    });

    /**
     * Validate form field without instantiating validate plug-in
     * @param {Element||String} element - DOM element or selector
     * @return {Boolean} validation result
     */
    $.validator.validateElement = function (element) {
        element = $(element);
        var form = element.get(0).form,
            validator = form ? $(form).data('validator') : null;
        if (validator) {
            return validator.element(element.get(0));
        } else {
            var valid = true,
                classes = element.prop('class').split(' ');
            $.each(classes, $.proxy(function (i, className) {
                if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {
                    valid = false;
                    return valid;
                }
            }, this));
            return valid;
        }
    };

    var originValidateDelegate = $.fn.validateDelegate;

    $.fn.validateDelegate = function () {
        if (!this[0].form) {
            return this;
        }

        return originValidateDelegate.apply(this, arguments);
    };

    /**
     * Validate single element.
     *
     * @param {Element} element
     * @returns {*}
     */
    $.validator.validateSingleElement = function (element) {
        var errors = {},
            valid = true,
            validateConfig = {
                errorElement: 'label',
                ignore: '.ignore-validate'
            },
            form, validator, classes;

        element = $(element).not(validateConfig.ignore);

        if (!element.length) {
            return true;
        }

        form = element.get(0).form;
        validator = form ? $(form).data('validator') : null;

        if (validator) {
            return validator.element(element.get(0));
        }

        classes = element.prop('class').split(' ');
        validator = element.parent().data('validator') ||
            $.mage.validation(validateConfig, element.parent()).validate;

        element.removeClass(validator.settings.errorClass);
        validator.toHide = validator.toShow;
        validator.hideErrors();
        validator.toShow = validator.toHide = $([]);

        $.each(classes, $.proxy(function (i, className) {
            if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {
                valid = false;
                errors[element.get(0).name] = this.messages[className];
                validator.invalid[element.get(0).name] = true;
                validator.showErrors(errors);

                return valid;
            }
        }, this));

        return valid;
    };

    $.widget("mage.validation", {
        options: {
            meta: "validate",
            onfocusout: false,
            onkeyup: false,
            onclick: false,
            ignoreTitle: true,
            errorClass: 'mage-error',
            errorElement: 'div',
            errorPlacement: function (error, element) {
                var errorPlacement = element;
                // logic for date-picker error placement
                if (element.hasClass('hasDatepicker')) {
                    errorPlacement = element.siblings('img');
                }
                // logic for field wrapper
                var fieldWrapper = element.closest('.addon');
                if (fieldWrapper.length) {
                    errorPlacement = fieldWrapper.after(error);
                }
                //logic for checkboxes/radio
                if (element.is(':checkbox') || element.is(':radio')) {
                    errorPlacement = element.siblings('label').last();
                }
                errorPlacement.after(error);
            }
        },
        /**
         * Check if form pass validation rules without submit
         * @return boolean
         */
        isValid: function () {
            return this.element.valid();
        },

        /**
         * Remove validation error messages
         */
        clearError: function () {
            if (arguments.length) {
                $.each(arguments, $.proxy(function (index, item) {
                    this.validate.prepareElement(item);
                    this.validate.hideErrors();
                }, this));
            } else {
                this.validate.resetForm();
            }
        },
        /**
         * Validation creation
         * @protected
         */
        _create: function () {
            this.validate = this.element.validate(this.options);

            // ARIA (adding aria-required attribute)
            this.element
                .find('.field.required')
                .find('.control')
                .find('input, select, textarea')
                .attr('aria-required', 'true');

            this._listenFormValidate();
        },
        /**
         * Validation listening
         * @protected
         */
        _listenFormValidate: function () {
            $('form').on('invalid-form.validate', function (event, validation) {
                var firstActive = $(validation.errorList[0].element || []),
                    lastActive = $(validation.findLastActive() || validation.errorList.length && validation.errorList[0].element || []);

                if (lastActive.is(':hidden')) {
                    var parent = lastActive.parent();
                    var windowHeight = $(window).height();
                    $('html, body').animate({
                        scrollTop: parent.offset().top - windowHeight / 2
                    });
                }

                // ARIA (removing aria attributes if success)
                var successList = validation.successList;
                if (successList.length) {
                    $.each(successList, function () {
                        $(this)
                            .removeAttr('aria-describedby')
                            .removeAttr('aria-invalid');
                    })
                }
                if (firstActive.length) {
                    firstActive.focus();
                }
            });
        }
    });

    return $.mage.validation;
}));
`

@jwittorf
Copy link

jwittorf commented Sep 6, 2016

Still got one more thing that didn't work. When logged in as customer and changing password, I've entered a wrong passwort in the second field where you shall confirm it. The error message there still is english: Please enter the same value again.

I found the string in lib/web/jquery/jquery.validate.js on line 300.

So in theory it should be enough to add https://github.com/jzaefferer/jquery-validation/blob/master/src/localization/messages_de.js and it should translate, right? Did anybody by instance already ran into this problem and got a quick hint? I've done M1 a few years but am still quite new to M2 and the very best practices.

@jdavisonc
Copy link

jdavisonc commented Sep 6, 2016

I found another solution that seems to be better than the other one and works for the issue of @jwittorf .

Copy the file translate.phtml of Magento_Translation to your theme folder on {theme}/Magento_Translation/templates/translate.phtml and add the following line after line 31 (inside the if-block)

                     //TODO: Custom translation file for JS scripts, Please remove when issue resolved
                    dependencies.push('text!js/js-custom-translation.json');

Add a new file {theme}/web/js/js-custom-translation.json with the translations:

{
  "This is a required field.": "Campo requerido",
  "Please enter the same value again.": "Por favor ingresa el mismo valor denuevo."
}

Hope it helps!

@jwittorf
Copy link

jwittorf commented Sep 6, 2016

So basicly you would have just one file for all the javascript translations, no matter where they come from? Seems better than changing the javascript files directly, would be tricky with more languages. Since my shop only is in German it'll work but I'm just sayin.

I tried your approach and couldn't get it done for these kind of strings:

  • jquery.validate.js
  • validation/rules.js

This is my setup:

@ app/design/frontend/Vendor/Themen/Magento_Translation/templates/template.phtml

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
// @codingStandardsIgnoreFile
?>
<?php /** @var $block \Magento\Translation\Block\Js */ ?>
<?php if ($block->dictionaryEnabled()): ?>
    <script>
        require.config({
            deps: [
                'jquery',
                'mage/translate',
                'jquery/jquery-storageapi'
            ],
            callback: function ($) {
                'use strict';

                var dependencies = [],
                    versionObj;

                $.initNamespaceStorage('mage-translation-storage');
                $.initNamespaceStorage('mage-translation-file-version');
                versionObj = $.localStorage.get('mage-translation-file-version');

                if (versionObj.version !== '<?php /* @escapeNotVerified */
                    echo sha1($block->getTranslationFileTimestamp() . $block->getTranslationFilePath()) ?>') {
                    dependencies.push(
                        'text!<?php /*  @escapeNotVerified */ echo Magento\Translation\Model\Js\Config::DICTIONARY_FILE_NAME?>'
                    );
                    //TODO: Custom translation file for JS scripts, Please remove when issue resolved
                    dependencies.push('text!js/js-custom-translation.json');

                }

                require.config({
                    deps: dependencies,
                    callback: function (string) {
                        if (typeof string === 'string') {
                            $.mage.translate.add(JSON.parse(string));
                            $.localStorage.set('mage-translation-storage', string);
                            $.localStorage.set(
                                'mage-translation-file-version',
                                {
                                    version: '<?php /* @escapeNotVerified */
                    echo sha1($block->getTranslationFileTimestamp() . $block->getTranslationFilePath()) ?>'
                                }
                            );
                        } else {
                            $.mage.translate.add($.localStorage.get('mage-translation-storage'));
                        }
                    }
                });
            }
        });
    </script>
<?php endif; ?>

@ app/design/frontend/Vendor/Theme/web/js/js-custom-translation.json

{
    "Please enter the same value again": "Bitte geben Sie den gleichen Wert erneut ein.",
    "Please enter more or equal than {0} symbols.": "Bitte geben Sie mindestens {0} Wörter ein.",
    "Minimum of different classes of characters in password is %1.": "Es müssen mindestens %1 verschiedene Zeichenarten verwendet werden."
}

Am I missing something? Ran the static deploy afterwards but still no effect. Checked your comment at least three times but it seems to be just as you explained.

@quienti
Copy link
Contributor

quienti commented Sep 6, 2016

#5725
3109c8e

@Umfi
Copy link

Umfi commented Sep 7, 2016

I got further translations problems:

Texts in varien/js.js are also not translated, overiding the file works.

but i got a last problem with following text.
"Text length does not meet the specified text range."

It can be found in and:
/srv/www/magento2/lib/web/prototype/validation.js
/srv/www/magento2/lib/web/legacy-build.min.js

overriding the prototype/validation.js does not work, but if i change it in legacy-build.min.js in pub folder it works.

can i also override this file?

@ktruehl
Copy link

ktruehl commented Sep 12, 2016

After some testing and cursing and more testing and more cursing, I found out that the problem lies with the way Magento determines, which phrases to include in the js-translation.json file. Concatenated strings are apparently not recognized. That's why the phrase concerning the minimal length of the password is not picked up:

                    validator.passwordErrorMessage = $.mage.__(
                        "Minimum length of this field must be equal or greater than %1 symbols." +
                        " Leading and trailing spaces will be ignored."
                    ).replace('%1', passwordMinLength);

I created a patch, which removes the concatenation from a number of JS-translatable strings. I'm not sure whether I found all.
remove_concatenation_for_js-translatable_texts.diff.zip

mmansoor-magento pushed a commit that referenced this issue Sep 28, 2016
…5883 #5861

Merge remote-tracking branch 'origin/develop' into MAGETWO-55900
mmansoor-magento pushed a commit that referenced this issue Sep 28, 2016
Fixed issues: 
- MAGETWO-58237: Order status history contains wrong data after creation of a Credit Memo
- MAGETWO-58386: Eliminate dependencies on modules from lib/Magento
- MAGETWO-55120: [GitHub] No category id on the category page #5467
- MAGETWO-55900: [GitHub] Translate messages on password strength #5509 #5883 #5861
- MAGETWO-50412: [GITHUB] Issues with adding groups on attribute set creation page (#3607)
@olysenko
Copy link

Fix for the issue described in the very first post was delivered to develop branch in commit 14028aa
Backports for 2.1 and 2.0 are scheduled and will be delivered later

@cuiyang000
Copy link

@Choufourax 's temporary solution works!

@HyperC
Copy link

HyperC commented Oct 31, 2016

@Choufourax Thanks for this solution, however it doesn't work for me. The validation stops working at all. My theme didn't have the requirejs-config file so I added it and copied the content from the vendor/magento theme. I added validation.js to web/js in my theme folder but not validation occurs..

@korostii
Copy link
Contributor

@orlangur Yes, Magento's "Community Engineering" team has has great success on PR processing front lately.
Not so much regarding issue management \ bugfixing itself (yet). I also expect to see some improvement in bugfixing in future months.

However, I doubt 2.1 will be actively supported beyond the release of 2.2 and there's no reason to expect 2.2 to receive any more bugfixing effort than 2.1 had neither (moving on to 2.3: 81a184e ). So there's that.

Also, as we can see from fresh data, namely #10355, there are no changes on bug reproducal\acknowledgment front neither. There was a short burst of activity by multiple QA (adding "needs update" labels and closing obsolete issues, mostly) but I'd rather see one single person assigning labels and milestones to the new issues on a daily basis instead. Is that too much to ask?

@juanitopons
Copy link

Just a thing about the "temporary" fix...
If you replace the validation.js with the inline translations of your own store language....what about if you have multiple store views in different languages?
Because I replaced the validation.js for spanish translation, but it breaks english view translations (it's always displaying as spanish)
¿Can we actually call that a "temporary" fix? I've been almost a day looking for fixing translations bugs, and nothing gets clear.

@orlangur
Copy link
Contributor

@juanitopons can you have Spanish validation.js in separate Spanish theme inherited from English theme?

@juanitopons
Copy link

@orlangur how can I do that? I have the same theme for different store view languages.

@orlangur
Copy link
Contributor

@juanitopons you can create few themes inherited from base one which does not differ from base theme except for the validation.js file. I'm not saying it is a good solution of any sort but seems to be the best one among available workarounds and can be easily refactored when the bug is fixed.

@juanitopons
Copy link

@orlangur a quite awful solution. But as you mentioned, I don't see any other way of doing it.
Thanks !

@mamutawa
Copy link

mamutawa commented Aug 22, 2017

I Still having this issue with 2.1.8

hostep pushed a commit to hostep/magento2 that referenced this issue Sep 2, 2017
…s on password strength magento#5509 magento#5883 magento#5861

(cherry picked from commit f67e598)

Not included validation messages from magento@6ce2b5f (MAGETWO-42994)
@magento-team
Copy link
Contributor

Internal ticket to track issue progress: MAGETWO-72250

@magento-team magento-team added 2.1.x Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development bug report labels Sep 2, 2017
@andidhouse
Copy link

hmmm.. @magento-team two internal tickets to track the issue process of this one?

Internal ticket to track issue progress: MAGETWO-71380
and now
Internal ticket to track issue progress: MAGETWO-72250

This is reported over 14 month now and not fixed...

@orlangur
Copy link
Contributor

orlangur commented Sep 3, 2017

@andidhouse, there are two mistakes in your comment

  1. MAGETWO-71380 was to track PR which is already delivered to develop
  2. Missed the "reopened this on Jun 9" part in your conclusion about 14 months

@andidhouse
Copy link

@orlangur i do not get it - magento makes out a philosophy of bug reporting and tracking.
Never seen a platform where it is so complex --> report a bug --> create a internal ticket --> close the ticket --> reopen it again --> deliver it to XY branch etc. etc.

Could you guys not just simply fix the bugs in the new version? It is so time consuming for all which are in the process... Never seen a worst bug tracking and bug fixing workflow 👎

@orlangur
Copy link
Contributor

orlangur commented Sep 3, 2017

@andidhouse you are right that some processes, like issue processing on GitHub and 2.1.x fixes backporting with or without Community involvement, are not established/nontransparent yet. The things started to change somewhere around https://community.magento.com/t5/Magento-DevBlog/Update-on-Developer-Experience-Initiatives/ba-p/59704 and of course situation with bugs is not something that can change within days or weeks.

Consider current situation as an inherited tech debt which will, no doubt, be eliminated. By the way, duration of its resolution depends not only on Magento but on amount of Community enthusiasts, like you and me, doing the right things.

Don't try to draw conclusions on state of things as of 12-18 months ago and keep up the good work on raising important issues 👍

@andidhouse
Copy link

andidhouse commented Sep 3, 2017

@orlangur i agree that it takes time to change things - and i also spoke to the m2 management team about it. The "problem" in my mind is not that m2 is not a good system. The problem is that there are way too many bugs - and this since the beginning. The m2 management team agreed to change something here over 1 year ago - but i can not see such things.

Example:

A while ago (i think it was 2.1.7 or 2.1.6) a new magneto version was released. So we all expected here to be some major bugs to be fixed - and hoped for a lot of other bugs to be fixed too.
In fact the release was a security? release or something and then, again, in the new versions coming new functions are implemented?

I am not shure what sense it makes to include new functions (which cause new bugs at the moment) when there are such major bugs in the system which are not fixed.

In my opinion this is a bad management. The normal workflow (and i think all developers confirm this) is 1. get rid of ALL the major bugs 2. then think about some new functions to implement.

And this goes on and on and on since the beginning of m2. Instead of working lets say 6 month on all GitHub bugs reported the m2 management decides to "live" with major bugs and implement some new functions. And this is exactly what is the problem in first place in my view.

@magento-engcom-team magento-engcom-team added 2.1.x Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development bug report Issue: Format is not valid Gate 1 Failed. Automatic verification of issue format is failed labels Sep 11, 2017
@magento-engcom-team
Copy link
Contributor

@rorteg, thank you for your report.
The issue is already fixed in 2.2.0

@magento-engcom-team magento-engcom-team added Fixed in 2.2.x The issue has been fixed in 2.2 release line Reproduced on 2.1.x The issue has been reproduced on latest 2.1 release labels Sep 20, 2017
@magento magento locked and limited conversation to collaborators Sep 20, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug report Fixed in 2.2.x The issue has been fixed in 2.2 release line Issue: Format is not valid Gate 1 Failed. Automatic verification of issue format is failed Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development Reproduced on 2.1.x The issue has been reproduced on latest 2.1 release
Projects
None yet
Development

No branches or pull requests