kazupon / vue-i18n

:globe_with_meridians: Internationalization plugin for Vue.js
https://kazupon.github.io/vue-i18n/
MIT License
7.27k stars 861 forks source link

Missing translation if key is the same as a prototype function name #968

Open MrWook opened 4 years ago

MrWook commented 4 years ago

It is possible to get string protoype function with the translate function. If you are using a plain object for the messages and the keys are looking like this

{ 
    "somekey": "Im a translation",
    "somekey.deep": "Im a translation which is deep",
    "somekey.link": "I do not work",
}

The translate function will return null if the last key is the same as a prototype function. For example somekey.link will always be empty. This is because the internal function getPathValue returns the protoype function itself.

vue & vue-i18n version

vue: 2.6.1 vue-i18n: 8.18.2

Reproduction Link

https://jsfiddle.net/MrWook/ukfdp8q1/3/

Steps to reproduce

Open the Reproduction Link and look at the rendered page

What is Expected?

The last translation key is translated as well.

What is actually happening?

The last translation key isn't translated.

zqh971026 commented 4 years ago

I also found this question on v8.21.0.This is because you has both defined "somekey" and "somekey.link", and link is a String's prototype method.The following code on vue-i18n.esm.js make this BUG.

I18nPath.prototype.getPathValue = function getPathValue (obj, path) {
  if (!isObject(obj)) { return null }

  var paths = this.parsePath(path);
  if (paths.length === 0) {
    return null
  } else {
    var length = paths.length;
    var last = obj;
    var i = 0;
    while (i < length) {
      var value = last[paths[i]];
      if (value === undefined) {
        return null
      }
      last = value;
      i++;
    }

    return last
  }
};

this function should return a String value.But when you translating "somekey.link":

I18nPath.prototype.getPathValue = function getPathValue (obj, path) {
  if (!isObject(obj)) { return null }

  var paths = this.parsePath(path); //paths = ["somekey",  "link"]
  if (paths.length === 0) {
    return null
  } else {
    var length = paths.length; // length = 2
    var last = obj; // last is your translation
    var i = 0;
    while (i < length) {
      var value = last[paths[i]];
      if (value === undefined) {
        return null
      }
      last = value;
      i++;
    }

    return last
  }
};

the loop is running like following: i = 0

    while (i < length) { // i = 0
      var value = last[paths[i]]; // value = "I am a translation"
      if (value === undefined) {
        return null
      }
      last = value; // last = "I am a translation"
      i++;
    }

i = 1

    while (i < length) { // i = 1
      var value = last[paths[i]]; // value = f()
      if (value === undefined) {
        return null
      }
      last = value; // last = f()
      i++;
    }

"I am a translation"["link"] is same as "I am a translation".link,it return a String's prototype function.So the method return a Function finally.