mashpie / i18n-node

Lightweight simple translation module for node.js / express.js with dynamic json storage. Uses common __('...') syntax in app and templates.
MIT License
3.09k stars 421 forks source link

Key shown when not in other locale #80

Open thenitai opened 10 years ago

thenitai commented 10 years ago

Hi,

ExpressJS and Jade here. Using your fine library but running into an issue when changing locale. That is, that the key is being shown instead of the default locale (english in my case).

en.json contains this:

"select_language" : "Select language"

de.json does NOT contain it at all and what we see instead of "Select language" is the "select_language".

Using 0.4.1 (the one that comes by default when doing a npm install i18n)

Thanks for the feedback.

Kind Regards, Nitai

mashpie commented 10 years ago

Hey :)

in short: this is intended. Let me explain what happens:

by default i18n will return the "key" as is, as long there is no translation found for current language. The key itself is intended to be an expression like "please translate me" and will get returned until finally translated.

2nd thing is determining languages: i18n determines them by req headers in several ways. DefaultLanguage only get's set when req doesn't provide one of the configured languages, no matter of status of their underlying translations.

So in your case, i18n won't switch to DefaultLanguage but will try to translate that term in current language, without finding that translated term.

That behavior is borrowed from different gettext implementations where you start working with terms in a default language (mostly en), and will add i18n later by simply adding some i18n files. So i18n should return the given key as kind of "default term".

I am just thinking about a config switch to fallback to defaultLanguage on empty terms... should work, just needs some time + testing :)

thenitai commented 10 years ago

Thank you for your explanations. Coming from, say Java .properties files, the default behaviour is to fall back to English translation and show the English translated text, if the key could not be found in the current locale.

This makes totally sense, especially in a production environment. There you usually don't want that customers see a message like "please translate me". Say, I'm in German and instead of "Bitte wählen Sie eine Sprache" I see a "select_locals" (if that would be my key). I don't think that goes down well.

(Just a view of someone who has deployed a couple of applications) :-)

According to your answer above, you say that if I don't set any languages in the config, this behaviour will change? If you need someone to test, please call on me.

Thank you for all your work and time. Much appreciated.

mashpie commented 10 years ago

both scenarios have their pros and cons (actually I do use both depending on use case, for more than 15years now). OK no need to discuss as there is a solution: implementing a config switch, which could even be env depended. that way you can condigure to get untranslated terms on dev and foreign language terms on prod...

thenitai commented 10 years ago

Yes a config setting would be awesome. Thank you for making an awesome library even better :-)

dirwin517 commented 9 years ago

:+1: A config Setting for 'partial' fallback!

pedrodelgallego commented 9 years ago

+1

rkorach commented 8 years ago

+1 for having a fallback system between supported locales if keys in one locale is only partially translated.

I've seen this has been also discussed on 18n-node-2 : https://github.com/jeresig/i18n-node-2/pull/29

mashpie commented 8 years ago

well fallbacks are in for a while now, see https://github.com/mashpie/i18n-node/blob/master/test/i18n.fallbacks.js and https://github.com/mashpie/i18n-node#list-of-all-configuration-options

anything still missing for you?

rkorach commented 8 years ago

Sorry, I may have understood it wrong.

The way I understand the doc

What I am looking for - maybe 'fallback' wasn't a good term to use

I couldn't manage to make this work, but let me know if I got everything wrong I'll try that once again.

mashpie commented 8 years ago

Let's try to sum up a little:

So you are trying to achieve a "partial" translation, right? The only intended "fallback" is the key itself.

__('Hi Dear') will put "Hi Dear" until translated in the corresponding fr.json file:

{
    "Hi Dear": "Salut mon cher"
}

all fallback logics right now happen in stage of language guessing and represent the current state for all phrases managed.

So what you want is a fallback for translating certain phrases which will lead to mixed translated output with one phrase in fr and another de and probably some untranslated in en. I won't discuss on user experience...

use cases covered right now are:

  1. use key as fallback for any untranslated phrase (ie: "return original pharse until translated in my locale")
  2. use defaultLocale for all phrases in case of request with unsupported locale (ie: "use en unless requested locale is handled")
  3. use an explicit fallback for all phrases from one locale to another (ie: "use de for request in ch")

so what you want is

use a master translation for all untranslated phrases (ie: "I translated 'Greeting' with 'Hello' in en but not yet in de and expect "Hello" from defaultLocale en instead of "Greeting" for all supported and unsupported languages which by itself will also have to follow 1-3 from above)

it could work (@see https://github.com/mashpie/i18n-node/blob/master/i18n.js#L834) by declaring multiple fallbacks, but I haven't tried yet:

i18n.configure({
  // setup supported locales
  locales:['en', 'fr', 'de'],

  // fall back to en phrases for untranslated phrases
  fallbacks:{'fr': 'en', 'de': 'en'},

  // default locale for any unsported requests
  defaultLocale: 'fr'
});
rkorach commented 8 years ago

tl;dr: the Translation Management service I use (PhraseApp) now has a configuration to to exactly this.

I still might prefer the feature to be in the i18n module.

You got it perfectly right. As for the user experience, I agree with you. Sometimes though hot features could use a quick launch instead of waiting for translations. As long as you are aware of what you do (and don't mix several languages at the same place). Anyway, let's keep the debate aside.

I did try the fallback strategy in the past (I thought this is how it worked), and tried it again now to be sure but no, fallback only seems to work if the language is not in the locales array when configuring i18n.

I thought of a dirty hack (check if a translation is equal to its key and if so force to defaultLocale translation) , but it would have been really ugly to scale it to the whole code base.

Quick note: missing translation falling back to the key is blocking for me since I use an object notation for my keys.

Luckily for me, PhraseApp does provide this feature now.

Thanks a lot for your time Marcus.

eloquence commented 7 years ago

Regarding the original request, I believe that Paxa's (unmeged) "retryInDefaultLocale" PR attempted to provide a basic fix for this, using the configured default locale as a fallback: https://github.com/mashpie/i18n-node/pull/206/commits/ee0da09cfe21c2f82f57913f94eacc2e2e4b7c15

It had an infinite recursion bug though, which I've fixed here: https://github.com/eloquence/i18n-node/commit/b2e67a8ea81da6fa5c473fcfc428e5b37f818cfa

FlyveHest commented 6 years ago

Was this ever implemented?

The discussion about a "master language" that you always fall back on is exactly what I am in need of.

I can't even get a single fallback to work right now, if the key isn't defined in one language, but is in the other, I still get the key value back when querying.

kaaax0815 commented 3 years ago

This issue is still relevant!

mashpie commented 3 years ago

How could we live without fallbacks for so many years?!

In short: i18n is about "translate on phrase to mutliple languages" while l10n is responsible to deliver one translation to the user... Or in a bit longer: https://blog.mozilla.org/l10n/2011/12/14/i18n-vs-l10n-whats-the-diff/

So for i18n (transfare one 'hello' into multiple 'Hi', 'Salut', 'Hei', 'Witam', 'Olá') we are set. For l10n we are not. Despite some features, that can be configured like:

  // fallback from Dutch to German and from any localized German (de-at, de-li etc.) to German
  fallbacks: { nl: 'de', 'de-*': 'de' },

  // you may alter a site wide default locale
  defaultLocale: 'en',

  // will return translation from defaultLocale in case current locale doesn't provide it
  retryInDefaultLocale: false,

Because, okay - over time i18n and l10n got used more and more as synonyms and so we finally that ended up in adding some basic l10n to i18n.

Still I think the desired feature is best outlined in vue-i18n fallback strategy: https://kazupon.github.io/vue-i18n/guide/fallback.html with lot's of options to rethink the l10n part of the whole i18n story in addition to what we have.

But to come back to you @kaaax0815 : What part of "The issue" do you refer to, exactly?

Maybe, just in case, you could add some failing tests to https://github.com/mashpie/i18n-node/blob/master/test/i18n.retryInDefaultLocale.js that should properly resolve and describe your issue better?

kaaax0815 commented 3 years ago

@mashpie Very cool comment! I have a bit which I wanted to localize. The user could set an env bar to a local and its not avaible use the default. I found a work around for my particular approach. By checking if the env var lang is in the languages array if not use the default. A native approach would be nicer, but not necessarily needed

mashpie commented 3 years ago

doesn't that config option do the trick?

  // will return translation from defaultLocale in case current locale doesn't provide it
  retryInDefaultLocale: false,

while talking about env var lang... do you use i18n within a cli or http context?

If talking about cli usage only, I'd recommend y18n https://www.npmjs.com/package/y18n to be much better focused on your use case.

kaaax0815 commented 3 years ago

@mashpie I use it for my discord bot. and retryInDefaultLocale: false didnt work maybe because i run setLocale and this changes the default lang asaik

mashpie commented 3 years ago

of course it should be set to true - setLocale does not change defaultLocale. setLocale sets the locale on the registered object, see

https://github.com/mashpie/i18n-node#some-words-on-register-option

and

https://github.com/mashpie/i18n-node/blob/master/test/i18n.setLocale.js

kaaax0815 commented 3 years ago

i meant true. my bad. it didnt work nevertheless

mashpie commented 3 years ago

Briefly looking at your repository it seams, that you are using a global singleton for both: cli and web requests. And I can't find any configure() at all, which means that i18n will act as a singleton sharing state between all and everything within your app.

This will lead to issues with concurrency as any change to the current locale will change locales in all given contexts. In addition, without configuration there is no context nor request object and this results in a defaultLanguage of nullor undefined - so without any defaults there is nothing to look up, right?

So, please: Use an instance. Configure that instance with i18n.configure({...}). Bind that to a context thru register on configure manually or use i18n.init(req, res, next) to automatically bind to req and res objects as context.

And please if and only if you insist on directly using i18n as a global singleton, do so by configuring it with the register: global option. Checkout examples in tests, esp. this one: https://github.com/mashpie/i18n-node/blob/master/test/i18n.api.global.js

kaaax0815 commented 3 years ago

@mashpie I do configure a instance here: https://github.com/kaaaxcreators/Discord-MusicBot/blob/824527b00dc96d514c93db81b4e681bf7101a972/src/index.ts#L52 and because i dont have any express or stuff like that i dont have any req or res

mashpie commented 3 years ago

Ok, registers to global. So setLocale should work for that Single process. Same as __()

I was looking at Server.ts and commands...

https://github.com/kaaaxcreators/Discord-MusicBot/blob/824527b00dc96d514c93db81b4e681bf7101a972/src/server.ts

Importing i18n module won’t ensure it‘s configured when used.

To get that straight: you want to use i18n for command line only. Not for req/res cycle of a webserver?

kaaax0815 commented 3 years ago

I use my webserver only to show static text nothing translatet, so no i do Not want to use i18n for req/res cycle