magento / magento2

Prior to making any Submission(s), you must sign an Adobe Contributor License Agreement, available here at: https://opensource.adobe.com/cla.html. All Submissions you make to Adobe Inc. and its affiliates, assigns and subsidiaries (collectively “Adobe”) are subject to the terms of the Adobe Contributor License Agreement.
http://www.magento.com
Open Software License 3.0
11.55k stars 9.32k forks source link

Translate messages on password strength #5509

Closed rorteg closed 7 years ago

rorteg commented 8 years ago

Steps to reproduce

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

    Expected result

  3. Translated.

    Actual result

  4. Not translated.
captura de tela 2016-07-06 as 18 06 24

Thanks.

rorteg commented 8 years ago

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 commented 8 years ago

A fix would be highly appreciated!

antboiko commented 8 years ago

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

Best, Anton.

jernejh commented 8 years ago

same problem here!

jdavisonc commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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:

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 commented 8 years ago

https://github.com/magento/magento2/pull/5725 https://github.com/magento/magento2/pull/5725/commits/3109c8e96bcd08041f558be65a2a4e3d213d22a4

Umfi commented 8 years ago

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 commented 8 years ago

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

olysenko commented 8 years ago

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 commented 8 years ago

@Choufourax 's temporary solution works!

HyperC commented 8 years ago

@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..

HyperC commented 8 years ago

Ah I had to re-deploy. Works now. Thanks!

bh-ref commented 7 years ago

seems not yet fixed in CE 2.1.3

olysenko commented 7 years ago

@bb-ref, you are right. Fix to 2.1 is scheduled for future release

slackerzz commented 7 years ago

issue closed (3 month ago), bug still present :rage3:

gesell commented 7 years ago

Hopefully it will be fixed within the next version. It is not really inspiring confidence for the customer if the password settings appear in a different language. Please fix!

Above solutions do not work for Magento 2.1.3.

p-makowski commented 7 years ago

It doesn't work in 2.1.4 either.

@olysenko Could you please give us an insight into the schedule for future release?

maksek commented 7 years ago

Fix will be delivered in 2.1.7, 2.0.15 and 2.2.0

jonathanribas commented 7 years ago

@maksek do you have an upcoming releases schedule to share with us please? Is there a link where we can have such information?

maksek commented 7 years ago

@jonathanribas i am working on make it more transparent and publish our schedule and scope. For now 2.1.5 will be copyright, 2.1.6 security and 2.1.7 will have bugfixing.

jonathanribas commented 7 years ago

@maksek thank you, waiting for it!

ccasciotti commented 7 years ago

@jdavisonc @Choufourax Thank you, for me it works the solution that explain to put requirejs-config.js and validation.js under <module_dir>/view/frontend/web and <module_dir>/view/frontend/web/js respectively

marcosdsdba commented 7 years ago

Problem Persisits in 2.1.5

daveyx commented 7 years ago

Problem persisits in 2.1.6

mike-happy commented 7 years ago

@maksek is this fix delivered in 2.1.7? I can't find anything of bugfixes in the release notes and the problem still persists while using 2.1.7.

kanduvisla commented 7 years ago

I can also confirm that this problem still persists in 2.1.7

alankent commented 7 years ago

Reopening due to reports its not fixed yet.

andidhouse commented 7 years ago

Still present in 2.1.7 - what does the magento team do here? Backport, internal ticket - this is now over 1 year reported and still not fixed. Magento team pls tell us why? What keeps you away from solving bugs here?

kunzi commented 7 years ago

Still having this issue too with 2.1.7. Not able to translate password strengths.

vroselin commented 7 years ago

Combining the error message in one string instead of 2, and wrapping it into $.mage.__('string') worked for me. Needs single quotes instead of double quotes.

andidhouse commented 7 years ago

It´s really a shame what @magento-team is doing with the translations. There are so many bugs/bugreports etc. but nothing is fixed.

And translation not a small bug as commerce is of course multilingual today.

magento-team commented 7 years ago

Internal ticket to track issue progress: MAGETWO-71380

andidhouse commented 7 years ago

Really @magento-team? Now a ticket again to track the process of the issue?

So we can expect this not to be fixed for another year? Makes reporting bugs kind of senseless if a bug is maybe fixed after 2 years.

orlangur commented 7 years ago

@andidhouse calm down :wink: How would you track progress without a ticket?

As you can see this issue was not ignored for all this time, but seems like there was mistake somewhere around

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

then revealed as unfixed and reopened.

As you may notice, a more generic fix was delivered in #10445 recently and thus I don't think backporting will take years.

andidhouse commented 7 years ago

Come on @orlangur ;-) i am not shure if it makes sense what the m2 team is doing right now. Look at all the bugs with internal tickets not fixed. And now to these bugs tickets are opened internally to track the process?

Normally the process would be to simply fix bugs in a valid time period with internal timesheets. In the magento2 case more tickets are created then bugs are fixed. Does this make any sense to you?

Also you suggested to update our 2.1.7 system which has a known and reported PayPal express error to 2.1.8 - so 2.1.8 has a major indexing problem (and other problems reported also here). So what now? Staying with this massive bug on 2.1.7? Updating to a magento2.1.8 version which can not be reindexed?

Not shure if this makes any sense with a magento2 version any more. Or do you have a plan for us how we should handle a magento2 system whith these major bugs? waiting for 2.2 is no possibility - our clients are not intrested in that - they just want a system they can order products 👎

andidhouse commented 7 years ago

It is not only me thinking m2 has a major bug-problem since two years. for example: https://github.com/magento/magento2/issues/10355#issuecomment-323321973

korostii commented 7 years ago

@andidhouse Well, to be fair, @orlangur said nothing about bugfixing in general. He just pointed out that this single specific bug isn't being ignored completely and that there's both past effort on fixing it and plans to continue doing it. Nothing more.

orlangur commented 7 years ago

Normally the process would be to simply fix bugs in a valid time period with internal timesheets

JIRA tickets referenced here are exactly such "internal timesheets" as to me.

Also you suggested to update our 2.1.7 system

I suggested to check if the issue is present in 2.1.8, nothing more. It does not make sense to report issue for 2.1.2 or 2.1.7 if it was fixed in 2.1.8, you know. Of course, upgrade to 2.1.8 is not an option for production until there are other essential bugs.

@andidhouse @korostii regarding bugfixing in general, I have some belief as well. I remember when I lost motivation in creating pull requests as they were simply not processed, even the most trivial ones. From recent times I see a huge progress in PRs processing (from 300+ to just 20-30 actively progressing pull requests) and 2.1.x bugfixes backporting. I see no reason why the same will not happen to GitHub issues. It does not really make sense to extrapolate conclusions on data from 1-2 years ago.

korostii commented 7 years ago

@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: 81a184e5 ). 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 commented 7 years ago

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 commented 7 years ago

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

juanitopons commented 7 years ago

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

orlangur commented 7 years ago

@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.