vue-i18n icon indicating copy to clipboard operation
vue-i18n copied to clipboard

Change the way missing translation keys are detected

Open WilliamDASILVA opened this issue 5 years ago • 14 comments

Hello,

Our translations are handled with Crowdin and one particularity with Crowdin is that, if a key wasn't translated yet and we sync them; Crowdin will return the key with an empty string instead of returning the file without the missing keys.

The thing is that, vue-i18n considers the empty string as an already-translated key, so it will uses it instead of fallbacking to the default version (english).

It would be great to override this default rule, so I can say that: "If my key is missing OR my key is empty, fallback to the default version".

An implementation suggestion would be a handler in the config that returns true or false if the key passed should be considered as missing?

WilliamDASILVA avatar Apr 16 '19 09:04 WilliamDASILVA

I just ran across this problem as well, also with Crowdin translations. As mentioned, an option when instantiating vue-i18n to consider empty string valid or not would be a good fix for this.

I would be willing to look into creating a PR for this if it will be considered for inclusion.

nekosaur avatar Apr 17 '19 09:04 nekosaur

Welcome contribution! :)

kazupon avatar Apr 30 '19 09:04 kazupon

Localisation services often provide option for deciding on behaviour of exporting empty translations. At least lokalise does provide api option export_empty_as = base | skip | empty.

rchl avatar Apr 30 '19 17:04 rchl

@rchl You're right. Crowdin have this option in their settings for some file types but not for JSON. We asked them what's up the same day I opened this issue and they added it. So my issue is "fixed".

Nonetheless, this feature would be a nice-to-have.

WilliamDASILVA avatar Apr 30 '19 20:04 WilliamDASILVA

@WilliamDASILVA Really. So it's possible to change it so that crowdin will not export empty strings? Where exactly do I set this?

nekosaur avatar Apr 30 '19 21:04 nekosaur

@nekosaur Go to your project, then click on "Settings". Go to the "General" tab, represented by the sliders icons (the first one).

On the "Export" section, ensure the "Skip untranslated strings" is checked.

Capture d’écran 2019-05-01 à 11 53 56 Capture d’écran 2019-05-01 à 11 54 04

Hope that helps! And sorry for the off-topic comment.

WilliamDASILVA avatar May 01 '19 09:05 WilliamDASILVA

@nekosaur Have you started work on this? If not I would like to have a go at it :)

makeupsomething avatar May 07 '19 07:05 makeupsomething

@makeupsomething Some things got in the way for me, so have at it 👍

nekosaur avatar May 07 '19 07:05 nekosaur

Does anyone have a fix for this yet? it would be really useful if we could set empty strings to be considered fallback. Thanks!

ssuess avatar Aug 16 '19 10:08 ssuess

@ssuess
I have workaround example. Here I replace empty strings with translation keys (that is what I needed at my project), but you could also retrieve fallback language string here if you want.

This code is placed among app bootstrapping code, after Vue.use(VueI18n);

// We decorate translation-retrieving functions so that empty strings
// (effectively untranslated strings) are replaced with translation keys (like undefined strings are).

Vue.prototype.$t = function (key, locale, values) {
    const result =  this.$i18n.t(key, locale, values);
    if (result === '') {
        return key;
    }
    return result;
};

Vue.prototype.$tc = function (key, choice, locale, values) {
    const result =  this.$i18n.tc(key, choice, locale, values);
    if (result === '') {
        return key;
    }
    return result;
};

Oleg-Arkhipov avatar Oct 28 '19 09:10 Oleg-Arkhipov

@ssuess I have workaround example. Here I replace empty strings with translation keys (that is what I needed at my project), but you could also retrieve fallback language string here if you want.

This code is placed among app bootstrapping code, after Vue.use(VueI18n);

// We decorate translation-retrieving functions so that empty strings
// (effectively untranslated strings) are replaced with translation keys (like undefined strings are).

Vue.prototype.$t = function (key, locale, values) {
    const result =  this.$i18n.t(key, locale, values);
    if (result === '') {
        return key;
    }
    return result;
};

Vue.prototype.$tc = function (key, choice, locale, values) {
    const result =  this.$i18n.tc(key, choice, locale, values);
    if (result === '') {
        return key;
    }
    return result;
};

You code is working in most cases but it has a problem when render messages that include parameter. I would like to change the code as below to fix this issue

Vue.prototype.$t = function (...args) {
  const result = this.$i18n.t.apply(this.$i18n, args)
  if (result === '') {
    return args[0]
  }
  return result
}

Vue.prototype.$tc = function (...args) {
  const result = this.$i18n.tc.apply(this.$i18n, args)
  if (result === '') {
    return args[0]
  }
  return result
}

lbngoc avatar Sep 08 '20 11:09 lbngoc

@lbngoc you're right, my code fails when parameters passed but locale is not — I forgot about this case. Actually I think it's bad decision in the library that these 2 signatures of t and tc functions exist. Makes me parse args manually if I want to use locale or parameters (as I now need on my project).

Oleg-Arkhipov avatar Sep 09 '20 21:09 Oleg-Arkhipov

FYI: In vue-i18n@v9 (vue-i18n-next) and later, the composable style (composition) API will be integrated to t. https://github.com/intlify/vue-i18n-next/blob/master/examples/composable/plural/basic.html

And also $t and $tc are kept as legacy style APIs. https://github.com/intlify/vue-i18n-next/tree/master/examples/legacy

kazupon avatar Sep 10 '20 00:09 kazupon

Crowdin only fixed this for flat JSON files. But if you have something like { "abc": { "a": "A", "b": "" } } is still exports empty strings. So this improvement for vue-i81n would be very welcome.

For now, I've solved it with postTranslation:

  postTranslation: (value, path) => {
    if (value === "") {
      const parts = path.split('.');
      let message = i18n.messages[CONFIG.fallbackLocale];
      for (const key of parts) {
        if (key in message) {
          message = message[key];
        }
        else {
          return value;
        }
      }
      return message;
    }
    return value;
  }

m-mohr avatar Mar 26 '24 11:03 m-mohr