Repository: baianat/vee-validate
Pull request #769 (2017-08-28 13:22:26)

added Bulgarian translations
i was unable to test it, since i got 

```
You are setting the validator locale to a locale that is not defined in the dicitionary. English messages may still be generated
```

and I am still getting use to VueJS


Patch:
diff --git a/dist/vee-validate.js b/dist/vee-validate.js
index 22a7b859..a8698de8 100644
--- a/dist/vee-validate.js
+++ b/dist/vee-validate.js
@@ -701,26 +701,6 @@ var formatFileSize = function (size) {
  */
 var setDataAttribute = function (el, name, value) { return el.setAttribute(("data-vv-" + name), value); };
 
-var createProxy = function (target, handler) {
-  if (typeof Proxy === 'undefined') {
-    // TODO: Polyfill or simulate the behavior.
-  }
-
-  return new Proxy(target, handler);
-};
-
-var createFlags = function () { return ({
-  untouched: true,
-  touched: false,
-  dirty: false,
-  pristine: true,
-  valid: null,
-  invalid: null,
-  validated: false,
-  pending: false,
-  required: false
-}); };
-
 /**
  * Shallow object comparison.
  *
@@ -2013,8 +1993,7 @@ Generator.makeVM = function makeVM (vm) {
     $watch: vm.$watch ? vm.$watch.bind(vm) : function () {},
     $validator: vm.$validator ? {
       errors: vm.$validator.errors,
-      validate: vm.$validator.validate.bind(vm.$validator),
-      update: vm.$validator.update.bind(vm.$validator)
+      validate: vm.$validator.validate.bind(vm.$validator)
     } : null
   };
 };
@@ -2161,6 +2140,18 @@ Generator.resolveGetter = function resolveGetter (el, vnode, model) {
   }
 };
 
+var DEFAULT_FLAGS = {
+  untouched: true,
+  touched: false,
+  dirty: false,
+  pristine: true,
+  valid: null,
+  invalid: null,
+  validated: false,
+  pending: false,
+  required: false
+};
+
 var DEFAULT_OPTIONS = {
   targetOf: null,
   initial: false,
@@ -2202,7 +2193,7 @@ var Field = function Field (el, options) {
   options = assign({}, DEFAULT_OPTIONS, options);
   this.validity = options.validity;
   this.aria = options.aria;
-  this.flags = createFlags();
+  this.flags = assign({}, DEFAULT_FLAGS);
   this.vm = options.vm || this.vm;
   this.component = options.component || this.component;
   this.update(options);
@@ -2300,8 +2291,8 @@ Field.prototype.update = function update (options) {
   this.initial = options.initial || this.initial || false;
 
   // update errors scope if the field scope was changed.
-  if (options.scope && options.scope !== this.scope && isCallable(this.validator.update)) {
-    this.validator.update(this.id, { scope: options.scope });
+  if (options.scope && options.scope !== this.scope && this.validator.errors && isCallable(this.validator.errors.update)) {
+    this.validator.errors.update(this.id, { scope: options.scope });
   }
   this.scope = options.scope || this.scope || null;
   this.name = options.name || this.name || null;
@@ -2345,9 +2336,8 @@ Field.prototype.update = function update (options) {
 Field.prototype.reset = function reset () {
     var this$1 = this;
 
-  var def = createFlags();
   Object.keys(this.flags).forEach(function (flag) {
-    this$1.flags[flag] = def[flag];
+    this$1.flags[flag] = DEFAULT_FLAGS[flag];
   });
 
   this.addActionListeners();
@@ -2903,7 +2893,7 @@ var Validator = function Validator (validations, options) {
   this.strict = STRICT_MODE;
   this.errors = new ErrorBag();
   this.fields = new FieldBag();
-  this.flags = {};
+  this.fieldBag = {};
   this._createFields(validations);
   this.paused = false;
   this.fastExit = options.fastExit || false;
@@ -3153,7 +3143,17 @@ Validator.prototype.attach = function attach (field) {
     });
   }
 
-  this._addFlag(field, field.scope);
+  if (!field.scope) {
+    this.fieldBag = assign({}, this.fieldBag, ( obj = {}, obj[("" + (field.name))] = field.flags, obj ));
+      var obj;
+    return field;
+  }
+
+  var scopeObj = assign({}, this.fieldBag[("$" + (field.scope))] || {}, ( obj$1 = {}, obj$1[("" + (field.name))] = field.flags, obj$1 ));
+    var obj$1;
+  this.fieldBag = assign({}, this.fieldBag, ( obj$2 = {}, obj$2[("$" + (field.scope))] = scopeObj, obj$2 ));
+    var obj$2;
+
   return field;
 };
 
@@ -3185,13 +3185,13 @@ Validator.prototype.detach = function detach (name, scope) {
   field.destroy();
   this.errors.removeById(field.id);
   this.fields.remove(field);
-  var flags = this.flags;
+  var flags = this.fieldBag;
   if (field.scope) {
     delete flags[("$" + (field.scope))][field.name];
   } else {
     delete flags[field.name];
   }
-  this.flags = Object.assign({}, flags);
+  this.fieldBag = Object.assign({}, flags);
 };
 
 /**
@@ -3204,29 +3204,6 @@ Validator.prototype.extend = function extend (name, validator) {
   Validator.extend(name, validator);
 };
 
-/**
- * Updates a field, updating both errors and flags.
- *
- * @param {String} id 
- * @param {Object} diff 
- */
-Validator.prototype.update = function update (id, ref) {
-    var scope = ref.scope;
-
-  this.errors.update(this.id, { scope: scope });
-  var field = this._resolveField(("#" + id));
-  var flags = this.flags;
-
-  // remove old scope.
-  if (field.scope) {
-    delete flags[("$" + (field.scope))][field.name];
-  } else {
-    delete flags[field.name];
-  }
-
-  this._addFlag(field, scope);
-};
-
 /**
  * Just an alias to the static method for convienece.
  */
@@ -3478,26 +3455,6 @@ Validator.prototype._getFieldDisplayName = function _getFieldDisplayName (field)
   return field.displayName || this.dictionary.getAttribute(LOCALE, field.name, field.name);
 };
 
-/**
- * Adds a field flags to the flags collection.
- * @param {Field} field 
- * @param {String|null} scope 
- */
-Validator.prototype._addFlag = function _addFlag (field, scope) {
-    if ( scope === void 0 ) scope = null;
-
-  if (!scope) {
-    this.flags = assign({}, this.flags, ( obj = {}, obj[("" + (field.name))] = field.flags, obj ));
-      var obj;
-    return;
-  }
-
-  var scopeObj = assign({}, this.flags[("$" + scope)] || {}, ( obj$1 = {}, obj$1[("" + (field.name))] = field.flags, obj$1 ));
-    var obj$1;
-  this.flags = assign({}, this.flags, ( obj$2 = {}, obj$2[("$" + scope)] = scopeObj, obj$2 ));
-    var obj$2;
-};
-
 /**
  * Tests a single input value against a rule.
  *
@@ -3724,17 +3681,6 @@ Validator.prototype._validate = function _validate (field, value, silent) {
 Object.defineProperties( Validator.prototype, prototypeAccessors );
 Object.defineProperties( Validator, staticAccessors );
 
-var fakeFlags = createProxy({}, {
-  get: function get (target, key) {
-    // is a scope
-    if (String(key).indexOf('$') === 0) {
-      return fakeFlags;
-    }
-
-    return createFlags();
-  }
-});
-
 /**
  * Checks if a parent validator instance was requested.
  * @param {Object|Array} injections
@@ -3802,7 +3748,7 @@ var makeMixin = function (Vue, options) {
     // There is a validator but it isn't injected, mark as reactive.
     if (! requested && this.$validator) {
       Vue.util.defineReactive(this.$validator, 'errors', this.$validator.errors);
-      Vue.util.defineReactive(this.$validator, 'flags', this.$validator.flags);
+      Vue.util.defineReactive(this.$validator, 'fieldBag', this.$validator.fieldBag);
     }
 
     if (! this.$options.computed) {
@@ -3813,11 +3759,7 @@ var makeMixin = function (Vue, options) {
       return this.$validator.errors;
     };
     this.$options.computed[options.fieldsBagName || 'fields'] = function fieldBagGetter () {
-      if (!Object.keys(this.$validator.flags).length) {
-        return fakeFlags;
-      }
-
-      return this.$validator.flags;
+      return this.$validator.fieldBag;
     };
   };
 
diff --git a/locale/bg.js b/locale/bg.js
new file mode 100644
index 00000000..567cc1bb
--- /dev/null
+++ b/locale/bg.js
@@ -0,0 +1,52 @@
+import { formatFileSize, isDefinedGlobally } from '../src/utils';
+
+export const messages = {
+  _default: (field) => `Полето ${field} е с невалидна стойност.`,
+  alpha_dash: (field) => `Полето ${field} може да съдържа буквено-цифрови знаци, както и тирета и долни черти.`,
+  alpha_num: (field) => `Полето ${field} може да съдържа само буквено-цифрови символи.`,
+  alpha_spaces: (field) => `Полето ${field} може да съдържа само азбучни знаци, както и интервали.`,
+  alpha: (field) => `Полето ${field} може да съдържа само азбучни знаци.`,
+  between: (field, [min, max]) => `Полето ${field} може да е между ${min} и ${max}.`,
+  confirmed: (field) => `Потвърждението не съвпада за полето ${field}.`,
+  credit_card: (field) => `Полето ${field} е навалидно.`,
+  decimal: (field, [decimals] = ['*']) => `Полето ${field} трябва да е числено ${!decimals || decimals === '*' ? '' : 'и може да съдържа ${decimals} знака' }`,
+  digits: (field, [length]) => `Полето ${field} трябва да е цифрово и да съдържа точно ${length} цифри.`,
+  dimensions: (field, [width, height]) => `Полето ${field} трябва да е ${width} пиксела по ${height} пиксела.`,
+  email: (field) => `Полето ${field} трябва да е коректен Email адрес.`,
+  ext: (field) => `Полето ${field} трябва да е валиден файл.`,
+  image: (field) => `Полето ${field} трябва да е снимка.`,
+  in: (field) => `Полето ${field} трябва да е валидна стойност.`,
+  integer: (field) => `Полето ${field} трябва да е цяло число.`,
+  ip: (field) => `Полето ${field} трябва да е валиден IP адрес.`,
+  length: (field, [length, max]) => {
+    if (max) {
+      return `Полето ${field} трябва да е между ${length} и ${max}.`;
+    }
+
+    return `Полето ${field} трябва да е ${length}.`;
+  },
+  max: (field, [length]) => `Полето ${field} не може да бъде по-голямо от ${length} знака.`,
+  max_value: (field, [max]) => `Полето ${field} трябва да бъде ${max} или по-малко.`,
+  mimes: (field) => `Полето ${field} трябва да е валиден тип файл.`,
+  min: (field, [length]) => `Полето ${field} трябва да съдържа минимум ${length} символа.`,
+  min_value: (field, [min]) => `Полето ${field} трябва да минимум ${min} или повече.`,
+  not_in: (field) => `Полето ${field} трябва да е с валидна стойност.`,
+  numeric: (field) => `Полето ${field} може да съдържа само цифри.`,
+  regex: (field) => `Полето ${field} съдържа невалиден формат.`,
+  required: (field) => `Полето ${field} е задължително.`,
+  size: (field, [size]) => `Размерът на файла за полето ${field} трябва да е под ${formatFileSize(size)}.`,
+  url: (field) => `Полето ${field} не съдържа валиден URL адрес.`
+};
+
+const locale = {
+  name: 'bg',
+  messages,
+  attributes: {}
+};
+
+if (isDefinedGlobally('VeeValidate.Validator')) {
+  // eslint-disable-next-line
+  VeeValidate.Validator.addLocale(locale);
+}
+
+export default locale;
diff --git a/src/field.js b/src/field.js
index 5f2ed2c5..c118ab2a 100644
--- a/src/field.js
+++ b/src/field.js
@@ -1,6 +1,18 @@
-import { uniqId, createFlags, assign, normalizeRules, setDataAttribute, toggleClass, getInputEventName, debounce, isCallable, warn, toArray } from './utils';
+import { uniqId, assign, normalizeRules, setDataAttribute, toggleClass, getInputEventName, debounce, isCallable, warn, toArray } from './utils';
 import Generator from './generator';
 
+const DEFAULT_FLAGS = {
+  untouched: true,
+  touched: false,
+  dirty: false,
+  pristine: true,
+  valid: null,
+  invalid: null,
+  validated: false,
+  pending: false,
+  required: false
+};
+
 const DEFAULT_OPTIONS = {
   targetOf: null,
   initial: false,
@@ -41,7 +53,7 @@ export default class Field {
     options = assign({}, DEFAULT_OPTIONS, options);
     this.validity = options.validity;
     this.aria = options.aria;
-    this.flags = createFlags();
+    this.flags = assign({}, DEFAULT_FLAGS);
     this.vm = options.vm || this.vm;
     this.component = options.component || this.component;
     this.update(options);
@@ -137,8 +149,8 @@ export default class Field {
     this.initial = options.initial || this.initial || false;
 
     // update errors scope if the field scope was changed.
-    if (options.scope && options.scope !== this.scope && isCallable(this.validator.update)) {
-      this.validator.update(this.id, { scope: options.scope });
+    if (options.scope && options.scope !== this.scope && this.validator.errors && isCallable(this.validator.errors.update)) {
+      this.validator.errors.update(this.id, { scope: options.scope });
     }
     this.scope = options.scope || this.scope || null;
     this.name = options.name || this.name || null;
@@ -180,9 +192,8 @@ export default class Field {
    * Resets field flags and errors.
    */
   reset () {
-    const def = createFlags();
     Object.keys(this.flags).forEach(flag => {
-      this.flags[flag] = def[flag];
+      this.flags[flag] = DEFAULT_FLAGS[flag];
     });
 
     this.addActionListeners();
diff --git a/src/generator.js b/src/generator.js
index 898f0de6..28eda4ee 100644
--- a/src/generator.js
+++ b/src/generator.js
@@ -65,8 +65,7 @@ export default class Generator {
       $watch: vm.$watch ? vm.$watch.bind(vm) : () => {},
       $validator: vm.$validator ? {
         errors: vm.$validator.errors,
-        validate: vm.$validator.validate.bind(vm.$validator),
-        update: vm.$validator.update.bind(vm.$validator)
+        validate: vm.$validator.validate.bind(vm.$validator)
       } : null
     };
   }
diff --git a/src/mixin.js b/src/mixin.js
index 61b6963d..42d8f9c4 100644
--- a/src/mixin.js
+++ b/src/mixin.js
@@ -1,17 +1,6 @@
-import { isObject, isCallable, createProxy, createFlags } from './utils';
+import { isObject, isCallable } from './utils';
 import Validator from './validator';
 
-const fakeFlags = createProxy({}, {
-  get (target, key) {
-    // is a scope
-    if (String(key).indexOf('$') === 0) {
-      return fakeFlags;
-    }
-
-    return createFlags();
-  }
-});
-
 /**
  * Checks if a parent validator instance was requested.
  * @param {Object|Array} injections
@@ -77,7 +66,7 @@ export default (Vue, options = {}) => {
     // There is a validator but it isn't injected, mark as reactive.
     if (! requested && this.$validator) {
       Vue.util.defineReactive(this.$validator, 'errors', this.$validator.errors);
-      Vue.util.defineReactive(this.$validator, 'flags', this.$validator.flags);
+      Vue.util.defineReactive(this.$validator, 'fieldBag', this.$validator.fieldBag);
     }
 
     if (! this.$options.computed) {
@@ -88,11 +77,7 @@ export default (Vue, options = {}) => {
       return this.$validator.errors;
     };
     this.$options.computed[options.fieldsBagName || 'fields'] = function fieldBagGetter () {
-      if (!Object.keys(this.$validator.flags).length) {
-        return fakeFlags;
-      }
-
-      return this.$validator.flags;
+      return this.$validator.fieldBag;
     };
   };
 
diff --git a/src/utils.js b/src/utils.js
index d63c09d7..a6614809 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -44,26 +44,6 @@ export const formatFileSize = (size) => {
  */
 export const setDataAttribute = (el, name, value) => el.setAttribute(`data-vv-${name}`, value);
 
-export const createProxy = (target, handler) => {
-  if (typeof Proxy === 'undefined') {
-    // TODO: Polyfill or simulate the behavior.
-  }
-
-  return new Proxy(target, handler);
-};
-
-export const createFlags = () => ({
-  untouched: true,
-  touched: false,
-  dirty: false,
-  pristine: true,
-  valid: null,
-  invalid: null,
-  validated: false,
-  pending: false,
-  required: false
-});
-
 /**
  * Shallow object comparison.
  *
diff --git a/src/validator.js b/src/validator.js
index 82cbf0b5..98315c5a 100644
--- a/src/validator.js
+++ b/src/validator.js
@@ -22,7 +22,7 @@ export default class Validator {
     this.strict = STRICT_MODE;
     this.errors = new ErrorBag();
     this.fields = new FieldBag();
-    this.flags = {};
+    this.fieldBag = {};
     this._createFields(validations);
     this.paused = false;
     this.fastExit = options.fastExit || false;
@@ -266,7 +266,14 @@ export default class Validator {
       });
     }
 
-    this._addFlag(field, field.scope);
+    if (!field.scope) {
+      this.fieldBag = assign({}, this.fieldBag, { [`${field.name}`]: field.flags });
+      return field;
+    }
+
+    const scopeObj = assign({}, this.fieldBag[`$${field.scope}`] || {}, { [`${field.name}`]: field.flags });
+    this.fieldBag = assign({}, this.fieldBag, { [`$${field.scope}`]: scopeObj });
+
     return field;
   }
 
@@ -298,13 +305,13 @@ export default class Validator {
     field.destroy();
     this.errors.removeById(field.id);
     this.fields.remove(field);
-    const flags = this.flags;
+    const flags = this.fieldBag;
     if (field.scope) {
       delete flags[`$${field.scope}`][field.name];
     } else {
       delete flags[field.name];
     }
-    this.flags = Object.assign({}, flags);
+    this.fieldBag = Object.assign({}, flags);
   }
 
   /**
@@ -317,27 +324,6 @@ export default class Validator {
     Validator.extend(name, validator);
   }
 
-  /**
-   * Updates a field, updating both errors and flags.
-   *
-   * @param {String} id 
-   * @param {Object} diff 
-   */
-  update (id, { scope }) {
-    this.errors.update(this.id, { scope });
-    const field = this._resolveField(`#${id}`);
-    const flags = this.flags;
-
-    // remove old scope.
-    if (field.scope) {
-      delete flags[`$${field.scope}`][field.name];
-    } else {
-      delete flags[field.name];
-    }
-
-    this._addFlag(field, scope);
-  }
-
   /**
    * Just an alias to the static method for convienece.
    */
@@ -577,21 +563,6 @@ export default class Validator {
     return field.displayName || this.dictionary.getAttribute(LOCALE, field.name, field.name);
   }
 
-  /**
-   * Adds a field flags to the flags collection.
-   * @param {Field} field 
-   * @param {String|null} scope 
-   */
-  _addFlag (field, scope = null) {
-    if (!scope) {
-      this.flags = assign({}, this.flags, { [`${field.name}`]: field.flags });
-      return;
-    }
-
-    const scopeObj = assign({}, this.flags[`$${scope}`] || {}, { [`${field.name}`]: field.flags });
-    this.flags = assign({}, this.flags, { [`$${scope}`]: scopeObj });
-  }
-
   /**
    * Tests a single input value against a rule.
    *


Test patch:
diff --git a/tests/directive.js b/tests/directive.js
index 8b22fc0c..8c074130 100644
--- a/tests/directive.js
+++ b/tests/directive.js
@@ -22,7 +22,6 @@ test('adds the field after binding', () => {
   const $validator = {
     attach: jest.fn(),
     validate: () => {},
-    update: () => {},
     fields: {
       find: jest.fn(() => field)
     }
@@ -56,7 +55,6 @@ test('evaluates field options after update', done => {
       find: jest.fn(() => field)
     },
     validate: () => {},
-    update: () => {},
     detach: jest.fn()
   };
   const VM = Vue.extend({
@@ -91,7 +89,6 @@ test('expression can contain an object containing the scope', done => {
       find: jest.fn(() => field)
     },
     validate: () => {},
-    update: () => {},
     detach: jest.fn()
   };
   const VM = Vue.extend({
@@ -125,7 +122,6 @@ test('cleans up after unbinding', () => {
       find: jest.fn(() => field)
     },
     validate: () => {},
-    update: () => {},
     detach: jest.fn()
   };
   const VM = Vue.extend({
@@ -162,7 +158,6 @@ test('revises scope after inserted', async () => {
       find: jest.fn(() => field)
     },
     validate: () => {},
-    update: () => {},
     detach: jest.fn()
   };
   const VM = Vue.extend({
diff --git a/tests/field.js b/tests/field.js
index 2fa8a27e..259b6fe4 100644
--- a/tests/field.js
+++ b/tests/field.js
@@ -389,13 +389,15 @@ test('calls the update method on the validator errors when updating scope', () =
   let el = document.createElement('input');
   const vm = {
     $validator: {
-      update: jest.fn()
+      errors: {
+        update: jest.fn()
+      }
     }
   };
   const field = new Field(el, { rules: 'required', vm });
   field.updated = true;
   field.update({ rules: 'min:3', scope: 'scope' });
-  expect(vm.$validator.update).toHaveBeenCalledTimes(1);
+  expect(vm.$validator.errors.update).toHaveBeenCalledTimes(1);
 });
 
 test('validation trigger can validate values directly instead of resolving them', () => {
diff --git a/tests/generator.js b/tests/generator.js
index 36e161db..bfcaea59 100644
--- a/tests/generator.js
+++ b/tests/generator.js
@@ -364,8 +364,7 @@ test('generates fake vm', () => {
       $watch: jest.fn(),
       $validator: {
         validate: jest.fn(),
-        errors: {},
-        update: jest.fn()
+        errors: {}
       }
     }
   };
diff --git a/tests/validator.js b/tests/validator.js
index 839ced86..a94450a5 100644
--- a/tests/validator.js
+++ b/tests/validator.js
@@ -778,9 +778,9 @@ test('it can set flags for attached fields', () => {
 
   // scoped fields
   v.attach('email', 'email', { scope: 'myscope' });
-  expect(v.flags.$myscope.email.untouched).toBe(true);
+  expect(v.fieldBag.$myscope.email.untouched).toBe(true);
   v.flag('myscope.email', { untouched: false });
-  expect(v.flags.$myscope.email.untouched).toBe(false);
+  expect(v.fieldBag.$myscope.email.untouched).toBe(false);
 
   // dotted name fields
   field = v.attach('form.title', 'alpha');