turquoiseowl / i18n

Smart internationalization for ASP.NET
Other
556 stars 156 forks source link

Customize scheme 1 #278

Closed jeancroy closed 7 years ago

jeancroy commented 8 years ago

Basically I want to get rid of this step

4. The request contains an Accept-Language header with a language
    that matches (exactly or loosely) one of the application languages.

The context is "fr-ca" : a bunch of french speaking people in a sea of English speaking territory. Official language of business is french. A lot of computer system are in English. Like 50/50. Among other things choosing English software help to search on web for settings/feature XYZ.

Basically the language of the os/browser is disconnected from user intent. Most of our target user don't have any idea about advanced settings to adjust asked language.


Then I realize Scheme1 without step4 is almost Scheme2 + cookie. So I tried to implement cookies in DetermineDefaultLanguageFromRequest delegate. Not complex but also a failure. This is because whatever the ouput of this is hidden as being the default. I tough this was a bug until I see it documented:

The default URL Localization scheme (Scheme1) will show the language tag in the URL always; 
an alternative scheme, Scheme2, will show the language tag only if it is not the default.

So basically the feedback is stuff is glued together. Url presentation is tied to language detection source. Cookie language detection is tied to request language detection.

So it would be nice to have separate Boolean for behavior.

DetectLanguageFromCookie DetectLanguageFromHeader HideDefaultLanguageInUrl

Sheme1 could map as (true,true,false) Scheme2 map as (false,false,true)

And after setting the scheme we could refine the behavior

turquoiseowl commented 8 years ago

Yes, I'm with your analysis 100%. It should have been developed like that in the first place TBH.

I'm a bit tied up with other stuff at the moment. Are you able to have a crack at it yourself?

jeancroy commented 8 years ago

I can give it a try. I do have one question.

Here it show GetRequestUserLanguages should choose between the request and the PAL https://github.com/turquoiseowl/i18n/blob/d3b19831eca9fb35b91d8a4ceefb980bf5e17993/src/i18n/Helpers/HttpContextExtensions.cs#L269-L283

But in the body there's this comment https://github.com/turquoiseowl/i18n/blob/d3b19831eca9fb35b91d8a4ceefb980bf5e17993/src/i18n/Helpers/HttpContextExtensions.cs#L201-L205

So i'm not sure how LocalizedApplication.Current.DefaultLanguageTag ever surface as the default choice.

jeancroy commented 8 years ago

Also would need some guidance on life cycle of those behavior boolean.

Some options I can think of:

  1. Real Boolean. Resolve early. For exam listen to change of scheme.
  2. Nullable. Resolve Late. behavior = manualOption ?? getDefautlForScheme(currentScheme) .
  3. Don't bother about behavior boolean unless scheme is scheme.Custom.

Option 3 feel a bit like a patch, giving up the idea of base scheme + refine. But it help ensure we don't introduce subtle behavior change in existing installation.

turquoiseowl commented 8 years ago

There are two sets of language tags: Application Languages and User Languages. The former relates to those supported by your webapp (what's available); the latter are those requested by the browser/user-agent (what's wanted).

The LocalizedApplication.Current.DefaultLanguageTag value is relevant in terms of ApplicationLanguages and is added to that set in addition to any locales/translations you add to your app. It isn't added to User Languages.

The job of i18n is then to make a best match between those two sets of languages.

turquoiseowl commented 8 years ago

WRT booleans, how about somehting like:

    [Flags]
    public enum UrlLocalizationScheme
    {
        _DetectLanguageFromCookieFlag = 0x01,
        _DetectLanguageFromHeaderFlag = 0x02,
        _HideDefaultLanguageInUrlFlag = 0x04,

        Void = 0,
        Scheme1 = _DetectLanguageFromCookieFlag | _DetectLanguageFromHeaderFlag,
        Scheme2 = _HideDefaultLanguageInUrlFlag,
        Scheme3 = 0,
    }
turquoiseowl commented 8 years ago

Thinking about this a bit more, are you sure Scheme2 excludes Accept-Language languages from consideration? IIRC the only thing it modifies is whether or not the langtag is shown in the URL when the default app language was current. That was its original purpose anyhow.

turquoiseowl commented 8 years ago

And some more: the UrlLocalization and TextLocalization are somewhat distinct parts of i18n. Perhaps the DetectLanguageFromHeaderFlag settings etc. are not strictly related to UrlLocalization and can best be implemented as a simple booleans in the app settings or static flag properties on the LocalizedApplication class.

jeancroy commented 8 years ago

Thinking about this a bit more, are you sure Scheme2 excludes Accept-Language languages from consideration?

My default browser send: Accept-Language:en-US,en;q=0.8

The default language is i18n.LocalizedApplication.Current.DefaultLanguage = "fr";

Using Scheme 2 french is applied, Using Scheme1 english is applied.

So current behavior is scheme2 exclude Accept-Language.


As far as design perspective, I'm not sure it should be linked either. Scheme1/2 could control only display, _DetectLanguageFromCookieFlag , _DetectLanguageFromRequestFlag could be set independently

Then this package have tons of download on nugget so I'm not sure it's that good to change default

turquoiseowl commented 8 years ago

Yes, you're right: a side effect of Scheme2 is that a langtag will be derived from the URL, whether or not the URL contains a langtag. Therefore, the cookie and Accept-Language header never get a look in.

The simplest thing looks like two corresponding flags in the HttpContextExtensions.GetInferredLanguage method.

turquoiseowl commented 8 years ago

HttpContextExtensions.GetInferredLanguage then needs to fallback explicitly on LocalizedApplication.Current.DefaultLanguageTag if/when UserLanguages (i.e. headers) are disabled.

Jandev commented 7 years ago

It appears I'm stumbling across the mentioned issue also. We also want/need to control if the Headers will be parsed for a language or not.

Can you enlighten us if there has been any progress in the development? If not I'll just have to create it myself :-)

turquoiseowl commented 7 years ago

I defer to @jeancroy on this.

jeancroy commented 7 years ago

It kinda depend how universal we want to fix this. Do we agree on a minimum fix that only skip the header ?

turquoiseowl commented 7 years ago

Do we agree on a minimum fix that only skip the header ?

Yes. Single-purpose flag on LocalizedApplication maybe.

Jandev commented 7 years ago

Sounds good! I'll discuss internally if we can wait for this feature or need to develop something similar in the meantime.

Jandev commented 7 years ago

I've forked the project and made a small change concerning the Accept-Language header by creating a delegate which a developer can override if necessary. Similar to the UrlLocalizer code. Link: https://github.com/LasaulecBV/i18n/commit/4d4e6391694e8e17ddd61af3f65186359e7ed501

It's not based on the flags, like discussed over here. For now our team can work with this change I made. If you want I can create a PR (with comments and stuff added of course), but perhaps the flags might be a bit easier (for now).

turquoiseowl commented 7 years ago

Even better than a flag! I would be very pleased to receive a PR.

If you've time, a note on this feature in the README would be cool.

Jandev commented 7 years ago

Sure, no problem. I'll get to it tomorrow or the day after. In my current branch I've also upgraded the project to VS2015. Is this a problem? Otherwise I can make the changes by not converting it to VS2015 if necessary.

turquoiseowl commented 7 years ago

Yes, if we can keep the project open to VS2013 users for now that would be great. Much appreciated.