Closed Bug 889335 Opened 11 years ago Closed 10 years ago

navigator.languages

Categories

(Core :: DOM: Core & HTML, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla32
Tracking Status
relnote-firefox --- 32+

People

(Reporter: gal, Assigned: mounir)

References

Details

(Keywords: dev-doc-complete)

Attachments

(1 file, 4 obsolete files)

There is currently no way to get all accept languages. navigator.language only returns the first language. We should expose all languages via navigator.languages.
Attached patch patch (obsolete) — Splinter Review
We could expose an array of parsed language strings, but it would be rather tricky to normalize all cases, so maybe its better to punt that to content.
Sorry for the rogue assignment here, but Marcos seems to be uniquely qualified for this.
Assignee: nobody → mcaceres
Would something like that make sense?

[ArrayClass]
interface FooBarLanguages {
  // To make this work.
  readonly attribute unsigned long length;
  getter DOMString getItem(unsigned long index);

  // Returns exactly what is sent in the HTTP header.
  DOMString toString();

  // Change event handler.
  attribute Function? onchange;
};

partial interface Navigator {
  readonly attribute FooBarLanguages languages;
};

The reasons being:
- returning an array is clearly better because the most common usage will be to go through all languages from 0 to n and check if one of them is supported by the web page;
- self-containing the change event in the object is probably better than adding it to the navigator object;
- .toString() is arguably useful given that joining the array elements would do more or less the same thing.

Alternatively, we could simply have .languages returning an array of DOMString and have the change event sent to the navigator object.

What do you guys think?
OS: Mac OS X → All
Hardware: x86 → All
Seems like "DOMString toString();" can simply be replaced by "stringifier;".
What's the use case and where was this discussed?
(In reply to :Ms2ger from comment #5)
> What's the use case and where was this discussed?

The use cases are all localization solutions where we want to be able to fallback according to user specified fallback chain. See bug 288670, bug 562648, Firefox OS inability to fallback from es-CA, to es, to en-US etc.

Semi related is bug 889616 - about exposing Ecma 402 internal API for canonicalizing language tags and language negotiation algorithm.
Do these use cases require this to be exposed to the web at large, or only to chrome code or something along those lines?
For proper client side localization capabilities, it would be best for the library to have access to that, and such library may operate in client space.

Unless we have a strong reason not to, I'd suggest exposing it.

If the concern here is fingerprinting, we could do another trick, we could combine this and bug 889616 and instead expose an API for language negotiation which on input takes list of locales provided by the app and on output returns sorted list of locales that match user preference.

This would remove the fingerprinting and minimize the amount of code needed to write language fallback code.
> Unless we have a strong reason not to, I'd suggest exposing it.

Can we convince other UA vendors to do so too?
I think such an API would be useful for many applications. Currently applications that want to adjust to the language preferences of a new user have to rely on the HTTP Accept-Language header that the browser sends to the server. Applications that primarily live on the client but have access to a server can ping the server for the language information. Applications that live exclusively on the client can't do that.

Fingerprinting may be an issue - see the warning on navigator.language in the HTML5 spec:
http://www.w3.org/html/wg/drafts/html/master/webappapis.html#language-preferences
On the other hand, the preferred languages are already sent to servers as part of HTTP headers, so I don't see how an API can make the situation worse.

(In reply to Mounir Lamouri (:mounir) from comment #3)

> - returning an array is clearly better because the most common usage will be
> to go through all languages from 0 to n and check if one of them is
> supported by the web page;

An array-like object holding BCP 47 language tags in descending order of preference would be great because it can be passed directly to the ECMAScript Internationalization API (and similar APIs elsewhere). If you can require that the strings are canonicalized language tags (http://www.ecma-international.org/ecma-402/1.0/#sec-6.2.3) that would be even better (and might marginally decrease fingerprinting information).

>   // Returns exactly what is sent in the HTTP header.
>   DOMString toString();

I don't think we want that. The HTTP spec allows language tags in the Accept-Language header to come with qvalues to indicate relative preferences or even to exclude languages (http://tools.ietf.org/html/rfc2616#section-14.4). I'm not aware of anybody actually having found a good use for qvalues, so I'd suggest not exposing them at all. Any user agent that uses qvalues in its Accept-Language header should reorder or remove language tags as indicated by the qvalues and then strip the qvalues before returning the preference list. If a toString() is needed, Array.prototype.toString() provides a good default.

(In reply to Boris Zbarsky (:bz) (reading mail, but on paternity leave) from comment #10)

> Can we convince other UA vendors to do so too?

During the development of the first edition of the ECMAScript Internationalization API, Microsoft was requesting a way to obtain the user's language preferences. It didn't make sense within ECMAScript, which can be used in environments where there is no identifiable user, but I guess they'd support this interface for the browser.
(In reply to Norbert Lindenberg from comment #11)
> Fingerprinting may be an issue - see the warning on navigator.language in
> the HTML5 spec:
> http://www.w3.org/html/wg/drafts/html/master/webappapis.html#language-
> preferences
Nitpicking, we try to implement HTML spec http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#language-preferences
Man, the spec really doesn't make sense at all. If you allow the accept headers without user consent and then censor nav.language then you expose potentially another bit (does-the-browser-support-censoring-or-not), without actually reducing the fingerprinting surface.
(In reply to Zbigniew Braniecki [:gandalf] from comment #9)
> If the concern here is fingerprinting, [...]

There is no fingerprinting issues here because we already send the preferred languages in the HTTP headers so allowing them to be accessed by script is just making developers life easier and make a offline scenario simpler;

(In reply to Boris Zbarsky (:bz) (reading mail, but on paternity leave) from comment #10)
> Can we convince other UA vendors to do so too?

That's the plan.

Boris, do you think an ArrayClass interface with a change event is a good idea?
Flags: needinfo?(bzbarsky)
Putting the event on the array is pretty weird. I would rather put it on navigator.
The list actually changes while the browser is running?  That seems odd to me.
Yes, settings - content - languages is the (awkward) pref UI on desktop.
> Boris, do you think an ArrayClass interface with a change event is a good idea?

My temptation is to simply return a sequence and claim that the case of users changing the pref while the page is loaded and the page wanting to know about that is a super-edge-case we don't care about...
Flags: needinfo?(bzbarsky)
(In reply to Boris Zbarsky (:bz) (reading mail, but on paternity leave) from comment #18)
> > Boris, do you think an ArrayClass interface with a change event is a good idea?
> 
> My temptation is to simply return a sequence and claim that the case of
> users changing the pref while the page is loaded and the page wanting to
> know about that is a super-edge-case we don't care about...

It's actually not an edge case in Firefox OS. We want to be able to change the application language without needing the user to kill all the opened apps... including the homescreen, the system app, etc.

This is a common request in the web app world and that was asked multiple times wrt to app l10n which made us ask this to be added to the HTML specs:
https://www.w3.org/Bugs/Public/show_bug.cgi?id=21289

If we are worried that this event would be a privacy issue and doubt that it is needed in a more Web-y context, we could also simply expose it to privileged applications only. Though, I think we can have some solutions to prevent privacy breaches (like randomly delaying the event).
(In reply to Andreas Gal :gal from comment #15)
> Putting the event on the array is pretty weird. I would rather put it on
> navigator.

I think it makes things self-explanatory and self-contained and can make the produced code a bit nicer.
If we do want the event, using an ArrayClass interface as above makes sense to me.
We could also make navigator event target, and dispatch languagechange event there, and have
.languages as a sequence.
See also bug 780953
You'd need getLanguages() to have a sequence, but yes.
(In reply to Olli Pettay [:smaug] from comment #22)
> We could also make navigator event target, and dispatch languagechange event
> there, and have
> .languages as a sequence.
> See also bug 780953

Why splitting things up when we can keep things at one place?
(In reply to Mounir Lamouri (:mounir) from comment #19)

> It's actually not an edge case in Firefox OS. We want to be able to change
> the application language without needing the user to kill all the opened
> apps... including the homescreen, the system app, etc.
> 
> This is a common request in the web app world and that was asked multiple
> times wrt to app l10n which made us ask this to be added to the HTML specs:
> https://www.w3.org/Bugs/Public/show_bug.cgi?id=21289

Would application developers actually implement that? To do so, they'd have to keep track of all the places where they insert localized strings into user interfaces, and all places where they insert locale-sensitive data, including data that they may have obtained from a server, and all parts of the application that depend on the locale in some other fashion (e.g., left-to-right layout vs. right-to-left layout...). On the other hand, normal users rarely change their language preferences - usually once during initial setup, and then possibly later to add secondary languages when installing apps that don't support the primary language. A feature that requires changes throughout the app and is hardly ever used is unlikely to be implemented well.

On iOS, changing the language makes the screen go black for several seconds - I guess that means that the OS kills all apps. Can Firefox OS do that?
(In reply to Norbert Lindenberg from comment #25)

> Would application developers actually implement that? To do so, they'd have
> to keep track of all the places where they insert localized strings into
> user interfaces, and all places where they insert locale-sensitive data,
> including data that they may have obtained from a server, and all parts of
> the application that depend on the locale in some other fashion (e.g.,
> left-to-right layout vs. right-to-left layout...). 

We have localization libraries that provide APIs that wrap around all l10n code uses. Those APIs make language switching easy.

See https://github.com/l20n/l20n.js API

The only missing link is that it's impossible for us now to learn what fallback chain the user wants.
(In reply to Zbigniew Braniecki [:gandalf] from comment #26)

> We have localization libraries that provide APIs that wrap around all l10n
> code uses. Those APIs make language switching easy.
> 
> See https://github.com/l20n/l20n.js API

I can see how this can help update localized strings in user interfaces. I don't see how it helps update all places where applications insert locale-sensitive data, including data that they may have obtained from a server, and all parts of the application that depend on the locale in some other fashion (e.g., left-to-right layout vs. right-to-left layout...).
(In reply to Norbert Lindenberg from comment #27)

> I can see how this can help update localized strings in user interfaces. I
> don't see how it helps update all places where applications insert
> locale-sensitive data, including data that they may have obtained from a
> server, and all parts of the application that depend on the locale in some
> other fashion (e.g., left-to-right layout vs. right-to-left layout...).

Most of the APIs provided there are asynchronous wrappers. This is not really topic for this bug, so lets take it off from here, but the bottom line is - we provide APIs to make language switching easy on running code including updating all locale-sensitive data insertions and running-code translations. And yes, including rtl/ltr layouts.
After some discussions with a few folks at the Toronto work week, here is where we ended up (roughly):

Redefined Navigator to extend EventTarget. This seems fairly uncontroversial. 

Then we have two options:

## Options 1

partial interface Navigator {
  sequence<DOMString> getLanguages();
  attribute EventHandler onlanguagechange;
};

When getLanguages() is invoked, return a sequence [WebIDL] that represents the internal language list of the user agent.

When there is a change to the UA language list (e.g., the user changed their language preferences through system prefs), queue a task to fire a simple event named "languagechange" at the navigator object.

### Example usage 
(function(){
  var langList = navigator.getLanguages(); 
  navigator.addEventListener("languagechange", function(){
     //figure out what's changed
     var newLangList = navigator.getLanguages();
  });
}());

## Option 2
partial interface Navigator {
  readonly DOMString[] languages;
  attribute EventHandler onlanguagechange;
};

The languages attribute is a read only array [WebIDL] that represents the user's preferred languages ordered by most preferred. 

When getting the languages attribute, return the array that currently represents the language list for the user agent.  

Note: a new array object is created when there is a change to the internal language list, and this assigned to the attribute.  

When there is a change to the UA language list,
1. Let lang-list be a platform array object with the items being the language tags sorted by most preferred.
2. make lang-list read only as per WebIDL. 
3. queue a task to: 
 3.1 Set the value of the languages attribute to lang list.  
 3.2 Fire a simple event named "languagechange" at the navigator object. 

### Example usage 
(function(){
  var langList = navigator.languages; 
  navigator.addEventListener("languagechange", function(){
     //figure out what's changed
     var newLangList = navigator.languages;
  });
}());

## Rationale for options 

1. In options 1, using a sequence instead of a "live" array is simpler for developers to work with (won't change from under you in fun ways). In option 2, you get a different array when there is a change to the language - the "getting" behavior of this attribute is somewhat similar to how document.body works.
2. In option 1 and 2, we are not defining a new type of array (by using [ArrayClass] FooBarLanguages) reduces complexity. 
3. In option 1 and 2, not having a new Array type (FooBarLanguages) that implements or inherits from event target reduces complexity. 
4. In option 1, having certain methods available from Array on a read only array (e.g., sort, pop, push) is confusing - as they throw. In option 2, authors wanting to make use of those array methods will need to use Array.prototype.slice.call(navigator.languages).
I didn't understand the difference between sequence<DOMString> and DOMString[], sorry.

Also, it'd be good to know who participated in those conversations in TO.
Axel, apologies for not being clear and not including the participants. The difference is that when you call getLanguages() you _always_ get a new array. Also, the array is *not* read only, so you can .pop, .push, and use any method on available to Array.   

However, with navigator.language you only get a new new array if the languages list has changed. You are also restricted to what you can do (e.g., pop(), push() throw an exception). 

People around the table were Mounir, Jonas, Annevk, and a few other innocent bystanders.
I would vote for option 2 because navigator.language and navigator.languages are similar this way, but feature wise, both are fine for me.

If we can get it landed in timeframe for FxOS 2.0 we could rework settings languages panel there to facilitate language selection fallback properly :)
After talking to more people (namely Brad Lassey and Arun Ranganathan), the use cases for the Web platform itself are much more limited. I think this should be a FxOS feature only and we should move it to the window.navigator.mozApps.
Why is this only interesting to apps? Wouldn't webpages want to have the same fallback-to-users-second-language ability too?

I'm always suspicious when someone claims that apps and pages have different needs. Sometimes we give them different behavior because we have different levels of trust, but that doesn't seem to be the case here.
I agree with Jonas: this use case isn't specific to apps but to offline on the web. Mostly packaged apps do good offline because the Web has a terrible offline support but we should fix that and having this feature is part of that.

This said, I wouldn't mind having this feature being enabled for packaged apps on Firefox OS for some time so we can experiment it a bit before pushing it to the Web. But if we do that, it should stay in navigator.
(In reply to Jonas Sicking (:sicking) from comment #34)
> Why is this only interesting to apps? Wouldn't webpages want to have the
> same fallback-to-users-second-language ability too?

I'm not disagreeing about it being a useful feature. Just seems like a fairly niche use case (swapping on the fly). Even setting up the list of fall back languages is a non-trivial thing for most users. Consider - Firefox OS only allows selection of one language at a time (AFACT, from the setting menu). On Desktop, at least there is some UI. 
 
> I'm always suspicious when someone claims that apps and pages have different
> needs. Sometimes we give them different behavior because we have different
> levels of trust, but that doesn't seem to be the case here.

Agree, this is not really a trust issue.
(In reply to Mounir Lamouri (:mounir) from comment #35)
> I agree with Jonas: this use case isn't specific to apps but to offline on
> the web. Mostly packaged apps do good offline because the Web has a terrible
> offline support but we should fix that and having this feature is part of
> that.

Sure. I agree that this seems useful - but there are caveats: like if one can only select one language in FxOS, that users will actually select the languages they prefer in order, that apps will be localized to intersect with preferred languages, etc. 

Consider, although it won't be super rare that a user changes the default language, swapping preferred language order does seem like something extremely rare. 
 
> This said, I wouldn't mind having this feature being enabled for packaged
> apps on Firefox OS for some time so we can experiment it a bit before
> pushing it to the Web. But if we do that, it should stay in navigator.

I don't think we should have some things on navigator unprefixed in FxOS but require this to be behind a flag on desktop (as is the policy we are adopting). Exposing this on navigator in FxOS would make things confusing for developers, who will see a proprietary API extension in the standardized navigator space. Putting things in the navigator.mozApps space keeps things cleanly separated and immediately available in a place the is clearly proprietary.
(In reply to Marcos Caceres [:marcosc] from comment #36)
> (In reply to Jonas Sicking (:sicking) from comment #34)
> > Why is this only interesting to apps? Wouldn't webpages want to have the
> > same fallback-to-users-second-language ability too?
> 
> I'm not disagreeing about it being a useful feature. Just seems like a
> fairly niche use case (swapping on the fly). Even setting up the list of
> fall back languages is a non-trivial thing for most users. Consider -
> Firefox OS only allows selection of one language at a time (AFACT, from the
> setting menu). On Desktop, at least there is some UI. 

That's exactly the longer term vision for change in how we communicate with user about their language preference.

All major OSes are actually using a sortable lists of locales, and in the future we will want to switch Firefox Desktop, Mobile and OS to the similar model: select locales you want, and sort them.

Having navigator.languages exposed is a part of that puzzle.
People speaking minority languages should have a web that caters to their language preferences. That's by mozilla values, and part of a few email conversations these days driven by Mitchell.

The example is that if you're a Catalan speaker, you should be able to expose your language preference to the web, commonly being Catalan, Spanish, some English.

This should also work for webpages that use javascript and in-browser localization infrastructure.

This bug is about exposing a list of languages to javascript.

A user of a Catalan localization will start with a default that has a series of languages, as do many others. If they're able to adjust those defaults isn't part of the problem.

Language switching is a detail, and yes, we should send an event when that happens. Apps vs webpages don't matter much, or at all in my book.

This is really about making js-in-the-browser a good platform for localization. It needs to expose more than a single language to do so.
Axel, thanks for the great summary - I think we are all in violent agreement about this being needed and the use cases. We are now just trying to work out the best way to expose this to the platform:  

1. Initially expose it on window.navigator.mozApps and let FireFox OS pave the cowpath. 

2. Expose it on navigator, but behind a about:config flag until we get it standardized at the W3C/WHATWG.
That's the "no new prefixed DOM apis on release channels unless it's FxOS" rule?

I think I'd prefer to be able to test this on desktop and android builds, even if that'd require testers using aurora or nightly builds.
(In reply to comment #40)
> Axel, thanks for the great summary - I think we are all in violent agreement
> about this being needed and the use cases. We are now just trying to work out
> the best way to expose this to the platform:  
> 
> 1. Initially expose it on window.navigator.mozApps and let FireFox OS pave the
> cowpath. 

Why would this be exposed on mozApps?  I don't think it has anything to do with our apps API.

> 2. Expose it on navigator, but behind a about:config flag until we get it
> standardized at the W3C/WHATWG.

This seems like a good path forward, assuming that we will maintain the liberty to change the preffed off implementation according to feedback that we get from the standardization process.
(In reply to :Ehsan Akhgari (needinfo? me!) from comment #42) 
> Why would this be exposed on mozApps?  I don't think it has anything to do
> with our apps API.

True. It just seemed like a good proprietary place to hang this off. 
 
> > 2. Expose it on navigator, but behind a about:config flag until we get it
> > standardized at the W3C/WHATWG.
> 
> This seems like a good path forward, assuming that we will maintain the
> liberty to change the preffed off implementation according to feedback that
> we get from the standardization process.

Having it behind a flag allows for that (and maybe spitting out a console warning just to drive the point home).

Questions that are still unanswered: 

1. given navigator.languages, and the app's supported languages, how does the developer determine "best match"? 
Would be nice to have: 

navigator.languages.bestMatch([...array of app's supported languages...]); 

that handles all the canonicalization, etc. to find the best match.

2. should language change fire only when navigator.language changes or when navigator.languages changes?
(In reply to Marcos Caceres [:marcosc] from comment #43)
> Questions that are still unanswered: 
> 
> 1. given navigator.languages, and the app's supported languages, how does
> the developer determine "best match"? 
> Would be nice to have: 
> 
> navigator.languages.bestMatch([...array of app's supported languages...]); 
> 
> that handles all the canonicalization, etc. to find the best match.

This is covered by Ecma 402 http://ecma-international.org/ecma-402/1.0/#sec-9.2.6

While working on the second edition of Ecma 402 Mozilla will push for exposing this API (together with CanonicalizeLanguageTag)

See https://mail.mozilla.org/pipermail/es-discuss/2013-July/031771.html

> 
> 2. should language change fire only when navigator.language changes or when
> navigator.languages changes?

that's a great question. 
For langneg use case, it should fire whenever navigator.languages changes, because the langneg result of the previous negotiation may be that we're using fallback locale (third fourth in the chain) and in result of the changes on the second/third position we will now be able to go up and use more preferred locale.
(In reply to Zbigniew Braniecki [:gandalf] from comment #44)
> (In reply to Marcos Caceres [:marcosc] from comment #43)
> > Questions that are still unanswered: 
> > 
> > 1. given navigator.languages, and the app's supported languages, how does
> > the developer determine "best match"? 
> > Would be nice to have: 
> > 
> > navigator.languages.bestMatch([...array of app's supported languages...]); 
> > 
> > that handles all the canonicalization, etc. to find the best match.
> 
> This is covered by Ecma 402
> http://ecma-international.org/ecma-402/1.0/#sec-9.2.6

Yep. 

> While working on the second edition of Ecma 402 Mozilla will push for
> exposing this API (together with CanonicalizeLanguageTag)
> 
> See https://mail.mozilla.org/pipermail/es-discuss/2013-July/031771.html

Sounds good. Looking forward to hearing from Norbert Lindenberg about this (I saw his response to the list asking for support). 

As an aside, I guess this depends on implementing the first edition. Is there a bug for that that we can link this one to? 

> > 
> > 2. should language change fire only when navigator.language changes or when
> > navigator.languages changes?
> 
> that's a great question. 
> For langneg use case, it should fire whenever navigator.languages changes,
> because the langneg result of the previous negotiation may be that we're
> using fallback locale (third fourth in the chain) and in result of the
> changes on the second/third position we will now be able to go up and use
> more preferred locale.

Ok, I'm envisioning something like the following. Can you help me envision how a developer then deals with the event? - I've tried a few different things, but all ended up in a tangled mess of for loops:

var appLangs = ["en", "en-AU", "es", "jp"]; 

//Returning a list of best matches (or maybe Intl.bestMatch(aList,bList))
var bestMatches = navigator.languages.bestMatch(appLangs);

//no match, use the first of the appLangs
if(bestMatches.length === 0){
   bestMatches.push(appLangs[0]);
}

//Still provide the user the choice to pick which language of the supported ones they want
//as this might not be their computer/device, etc. 
var appLang = bestMatches[0];
pickLanguageUI(bestMatches, appLangs).then((lang) => {appLang = lang}); 

navigator.onlanguagechange = (e) => {  
    //this should be straight forward! Do we need a new event type that clearly indicates what changed? 
}
(In reply to Marcos Caceres [:marcosc] from comment #45)
> Ok, I'm envisioning something like the following. Can you help me envision
> how a developer then deals with the event? - I've tried a few different
> things, but all ended up in a tangled mess of for loops:


var appLangs = ['en', 'en-AU', 'es', 'jp'];
var defaultLang = ['de'];

navigator.addEventListener('languagechange', function() {
  var locList = Intl.prioritizeLocales(appLangs,             // [1]
                                       navigator.languages,
                                       defaultLang);
  var successFlag = null;

  while (!successFlag) {
    appLang = locList.pop();
    // here we try to use the resources for appLang          // [2]
    // if we get everything done successFlag = true;
  }
});


In case of L20n port for FxOS we currently use mozSettings [3]



[1] https://github.com/l20n/l20n.js/blob/master/lib/l20n/intl.js#L431
[2] https://github.com/l20n/l20n.js/blob/master/lib/l20n/context.js#L364
[3] https://github.com/l20n/l20n.js/blob/master/bindings/l20n/gaia.js#L144

The whole thing is as simple as the code that decides wherever we can localize using first result, or do we need to fallback to the next one.
Oh, of course defaultLang should be in appLangs, sorry for that omission.
(In reply to Marcos Caceres [:marcosc] from comment #45)

> As an aside, I guess this depends on implementing the first edition. Is
> there a bug for that that we can link this one to? 

There's meta bug 837963, and individual bugs for the different products:
- desktop Firefox: bug 853301
- Firefox for Android: bug 864843
- Firefox OS: bug 866301
(In reply to Zbigniew Braniecki [:gandalf] from comment #46)
> (In reply to Marcos Caceres [:marcosc] from comment #45)
> > Ok, I'm envisioning something like the following. Can you help me envision
> > how a developer then deals with the event? - I've tried a few different
> > things, but all ended up in a tangled mess of for loops:
> 
> 
> var appLangs = ['en', 'en-AU', 'es', 'jp'];
> var defaultLang = ['de'];
> 
> navigator.addEventListener('languagechange', function() {
>   var locList = Intl.prioritizeLocales(appLangs,             // [1]
>                                        navigator.languages,
>                                        defaultLang);

Whatever is happening above needs to happen in the Event. A developer should not require an additional library to do whatever Intl.prioritizeLocales() is doing. Will look into it.
(In reply to Marcos Caceres [:marcosc] from comment #49)

> Whatever is happening above needs to happen in the Event. A developer should
> not require an additional library to do whatever Intl.prioritizeLocales() is
> doing. Will look into it.

Language Negotiation is not trivial. It requires an algorithm.

What I call prioritizeLocales is just a input-safe version of Intl.LookupSupportedLocales which is a standard:

http://ecma-international.org/ecma-402/1.0/#sec-9.2.6

I started a discussion on es-discuss about standardizing this function as part of Ecma 402. (since language negotiation is part of i18n, not l10n).

https://mail.mozilla.org/pipermail/es-discuss/2013-July/031771.html
(In reply to Zbigniew Braniecki [:gandalf] from comment #50)
> (In reply to Marcos Caceres [:marcosc] from comment #49)
> 
> > Whatever is happening above needs to happen in the Event. A developer should
> > not require an additional library to do whatever Intl.prioritizeLocales() is
> > doing. Will look into it.
> 
> Language Negotiation is not trivial. It requires an algorithm.

Yeah, that's exactly what I'm worried about. Just getting notified that something changed in the language list is not particularly helpful without either having the ES's Intl interface available + some way of easily getting the prioritized list. Be nice to just have a method on the event:

var locList = ev.prioritizedLocales(optionalDefault);

That makes use of the LookupSupportedLocales abstract algorithm.

But if LookupSupportedLocales does end up getting exposed in ES's Intl object, then it's could be trivial enough to just combine them. Regardless, the Event should hold the old list and the new list, IMO. Otherwise, the developer needs to keep a copy of the old list unnecessarily while waiting for a language change (which is annoying, as you gotta either wrap it in another function or hold it as a global).

> What I call prioritizeLocales is just a input-safe version of
> Intl.LookupSupportedLocales which is a standard:
> 
> http://ecma-international.org/ecma-402/1.0/#sec-9.2.6
>
> I started a discussion on es-discuss about standardizing this function as
> part of Ecma 402. (since language negotiation is part of i18n, not l10n).
> 
> https://mail.mozilla.org/pipermail/es-discuss/2013-July/031771.html

Right, so are we blocked on that? Also, what's the status of implementing ecma-402?
(In reply to Marcos Caceres [:marcosc] from comment #51)
> Yeah, that's exactly what I'm worried about. Just getting notified that
> something changed in the language list is not particularly helpful without
> either having the ES's Intl interface available + some way of easily getting
> the prioritized list. Be nice to just have a method on the event:
> 
> var locList = ev.prioritizedLocales(optionalDefault);
> 
> That makes use of the LookupSupportedLocales abstract algorithm.

Well, it's not about optionalDefault. You need to know what you're negotiating against.
Two lists are:
 * user selected locales in the user sorted order
 * available locales for the given webapp

Webapp will have to provide the locales as a variable or via JSON, but in order to get user selected locales we need navigator.languages.

> But if LookupSupportedLocales does end up getting exposed in ES's Intl
> object, then it's could be trivial enough to just combine them. Regardless,
> the Event should hold the old list and the new list, IMO. Otherwise, the
> developer needs to keep a copy of the old list unnecessarily while waiting
> for a language change (which is annoying, as you gotta either wrap it in
> another function or hold it as a global).

Yeah, I agree that it would match most of the datachange APIs to show old/new combo.

> Right, so are we blocked on that?

I don't think so. The faster we can get it implemented the better, since we can use this (or other) language negotiation algorithms without waiting for them to be exposed.

> Also, what's the status of implementing ecma-402?

bug 837963.

We landed it on m-c, we're about to enable it on Firefox desktop (bug 853301)
(In reply to Zbigniew Braniecki [:gandalf] from comment #52)
> (In reply to Marcos Caceres [:marcosc] from comment #51)
> > Yeah, that's exactly what I'm worried about. Just getting notified that
> > something changed in the language list is not particularly helpful without
> > either having the ES's Intl interface available + some way of easily getting
> > the prioritized list. Be nice to just have a method on the event:
> > 
> > var locList = ev.prioritizedLocales(optionalDefault);
> > 
> > That makes use of the LookupSupportedLocales abstract algorithm.
> 
> Well, it's not about optionalDefault. You need to know what you're
> negotiating against.

Yes, sorry. I was intending optionalDefault as something to be returned if the comparison between the two lists resulted in nothing being matched... but then neglected to add the app's supported languages list, as you point out below. 

> Two lists are:
>  * user selected locales in the user sorted order
>  * available locales for the given webapp

Ok, yes. So then this needs to be on ES Intl and maybe keeping the old list might not be required. 
 
> Webapp will have to provide the locales as a variable or via JSON, but in
> order to get user selected locales we need navigator.languages.

Right.

> > But if LookupSupportedLocales does end up getting exposed in ES's Intl
> > object, then it's could be trivial enough to just combine them. Regardless,
> > the Event should hold the old list and the new list, IMO. Otherwise, the
> > developer needs to keep a copy of the old list unnecessarily while waiting
> > for a language change (which is annoying, as you gotta either wrap it in
> > another function or hold it as a global).
> 
> Yeah, I agree that it would match most of the datachange APIs to show
> old/new combo.

Ok. Let's go with that. 
 
> > Right, so are we blocked on that?
> 
> I don't think so. The faster we can get it implemented the better, since we
> can use this (or other) language negotiation algorithms without waiting for
> them to be exposed.
> 
> > Also, what's the status of implementing ecma-402?
> 
> bug 837963.
> 
> We landed it on m-c, we're about to enable it on Firefox desktop (bug 853301)

Awesomeness! I'd searched for ecma-402 in Bugzilla, which is why I didn't find it :) 

Ok, quick summary to make sure we have enough to make a spec from. 

1. navigator extends EventTarget. 
2. navigator gains languages attribute
3. navigator gains onlanguageschange EventHandler attribute 
4. Event fires when navigator.languages changes (which consequently when navigator.languages changes, that can cause navigator.language to change before the event is fired as part of a microtask)
5. LanguagesChangeEvent has oldValue and newValue attributes, that readonly arrays.
6. We continue to lobby ES-Discuss to enhance the Intl object - but in the mean time we prollyfill with Moz's localization lib. 

Did I miss anything?
Err. I mean: 

4. Event fires when navigator.languages changes (which consequently can cause navigator.language to change before the event is fired as part of a microtask)

And in 5. "that *are* that readonly arrays"

Sorry about that, got excited and didn't read over what I wrote :)
So, I thought about it a little more and we should not do 5. (.oldValue, .newValue) for now. We can add that later.
See Also: → 780953
I've posted a complete proposal over on bug 780953, comment 33.
Spec now has navigator.getLanguages() (also in workers). http://html5.org/r/8458
Summary: navigator.languages → navigator.getLanguages()
The spec now calls it navigator.languages, and it is exposed in workers and windows. A "languagechange" event is fired on the global whenever it changes. A onlanguagechange property is added to <body>
Summary: navigator.getLanguages() → navigator.languages
Marcos: do you plan to move forward with this patch anytime soon? We're ready to start moving forward with UI language fallbacking for Firefox OS and that's a necessary step for us here.

Stas: are you ok with navigator.languages using intl.accept_languages for now?
Flags: needinfo?(stas)
@gandalf, sorry for the miscommunication - I'm not working on the implementation or any patches for this feature. My role was only to make sure that this feature was added to the HTML spec. Hixie has specified it [1]. 

[1] http://www.whatwg.org/specs/web-apps/current-work/#dom-navigator-languages
Oh, you're assigned to this bug. :)

I may take it once I get to the language fallback.
Assignee: mcaceres → nobody
(In reply to Zbigniew Braniecki [:gandalf] from comment #62)
> Oh, you're assigned to this bug. :)
> 
> I may take it once I get to the language fallback.

Thanks!
Attached patch Patch with tests (obsolete) — Splinter Review
Hope this is fine :)
Assignee: nobody → mounir
Attachment #770139 - Attachment is obsolete: true
Status: NEW → ASSIGNED
Attachment #8408820 - Flags: review?(jonas)
Hmm, actually, there is something wrong with that patch. I should unregister when the nsGlobalWindow is destroyed. Or maybe use an observer instead of a callback.
Attached patch Patch with tests (obsolete) — Splinter Review
Attachment #8408820 - Attachment is obsolete: true
Attachment #8408820 - Flags: review?(jonas)
Attachment #8409209 - Flags: review?(jonas)
Attached patch Patch with tests (obsolete) — Splinter Review
I had to change my test because it was failing when e10s was enabled.

This is now passing try:
https://tbpl.mozilla.org/?tree=Try&rev=b51750c5cdac
Attachment #8409209 - Attachment is obsolete: true
Attachment #8409209 - Flags: review?(jonas)
Attachment #8409370 - Flags: review?(jonas)
Comment on attachment 8409370 [details] [diff] [review]
Patch with tests

Review of attachment 8409370 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/webidl/Navigator.webidl
@@ +51,5 @@
>  
>  [NoInterfaceObject]
>  interface NavigatorLanguage {
>    readonly attribute DOMString? language;
> +  readonly attribute any languages; // any = DOMString[] here.

[cached, frozen] readonly attribute sequence<DOMString> languages;

And then make sure to invalidate the cache as needed.
Attachment #8409370 - Flags: review?(jonas) → review-
Attached patch Patch with testsSplinter Review
Review comments applied.
Attachment #8409370 - Attachment is obsolete: true
Attachment #8415305 - Flags: review?(jonas)
Comment on attachment 8415305 [details] [diff] [review]
Patch with tests

Review of attachment 8415305 [details] [diff] [review]:
-----------------------------------------------------------------

Olli, can you review that the new onlanguagechange event has been implemented as it should?

r=me on the other bits with the below comments are fixed.

::: dom/base/Navigator.cpp
@@ +411,5 @@
> +{
> +  nsTArray<nsString> languages;
> +  GetLanguages(languages);
> +  if (languages.Length() >= 1) {
> +    aLanguage.Assign(languages[0]);

Otherwise set aLanguage to the empty string. (Sorry, crappy XPCOM conventions)

@@ +420,5 @@
>  
> +void
> +Navigator::GetLanguages(nsTArray<nsString>& aLanguages)
> +{
> +  GetAcceptLanguages(aLanguages);

Why having separate functions for GetLanguages and GetAcceptLanguages?

::: dom/base/nsGlobalWindow.cpp
@@ +1989,5 @@
>  
>    if (!mDoc->IsInitialDocument()) {
>      return false;
>    }
> +

Please don't do whitespace cleanup on this whole file in the same patch as there are functional changes.

::: dom/events/EventNameList.h
@@ +466,5 @@
>               NS_EVENT)
> +WINDOW_EVENT(languagechange,
> +             NS_LANGUAGECHANGE,
> +             EventNameType_HTMLBodyOrFramesetOnly,
> +             NS_EVENT)

Olli needs to review the language change stuff.
Attachment #8415305 - Flags: review?(bugs)
Comment on attachment 8415305 [details] [diff] [review]
Patch with tests

Not too nice API. Event fires on object Foo notifying that a property on
object Bar has changed.

This doesn't implement the languagechanged event for WorkerScope, but
looks like we're not implementing NavigatorLanguage interface there either.
Attachment #8415305 - Flags: review?(bugs) → review+
(In reply to Olli Pettay [:smaug] from comment #72)
> Comment on attachment 8415305 [details] [diff] [review]
> Patch with tests
> 
> Not too nice API. Event fires on object Foo notifying that a property on
> object Bar has changed.

Yeah, we pointed this out to Hixie - but he noted that other platform features do this too: navigator.onLine fires on the body element and adds ononline and onoffline event handlers on body. There is another feature that does this too, but can't remember which right now. We opted for consistency with the other platform features rather than "fix" this.  

> This doesn't implement the languagechanged event for WorkerScope, but
> looks like we're not implementing NavigatorLanguage interface there either.

Should probably add that for completeness at some point.
Yes, having navigator.languages and onlanguagechange support in workers might very well be important for B2G as well as for ServiceWorkers.
(In reply to Jonas Sicking (:sicking) from comment #71)
> ::: dom/base/Navigator.cpp
> @@ +411,5 @@
> > +{
> > +  nsTArray<nsString> languages;
> > +  GetLanguages(languages);
> > +  if (languages.Length() >= 1) {
> > +    aLanguage.Assign(languages[0]);
> 
> Otherwise set aLanguage to the empty string. (Sorry, crappy XPCOM
> conventions)

Done.

> @@ +420,5 @@
> >  
> > +void
> > +Navigator::GetLanguages(nsTArray<nsString>& aLanguages)
> > +{
> > +  GetAcceptLanguages(aLanguages);
> 
> Why having separate functions for GetLanguages and GetAcceptLanguages?

One is the binding function and the other returns GetAcceptLanguages(). I prefer to keep them separated. It doesn't cost anything and makes thing clear. It happens that the bindings directly call GetAcceptLanguages() but it could be different and that later function could be re-used.

> ::: dom/base/nsGlobalWindow.cpp
> @@ +1989,5 @@
> >  
> >    if (!mDoc->IsInitialDocument()) {
> >      return false;
> >    }
> > +
> 
> Please don't do whitespace cleanup on this whole file in the same patch as
> there are functional changes.

Will try to have those removed before pushing. My editor is configured to clean whitespaces without me knowing ;)
https://hg.mozilla.org/mozilla-central/rev/0b7fbc19c190
Status: ASSIGNED → RESOLVED
Closed: 10 years ago
Flags: in-testsuite+
Resolution: --- → FIXED
Target Milestone: --- → mozilla32
Please don't land directly on mozilla-central for things like this.

Also this appears untested, since it caused breakage:
https://tbpl.mozilla.org/php/getParsedLog.php?id=39195081&tree=Mozilla-Central
https://tbpl.mozilla.org/php/getParsedLog.php?id=39195467&tree=Mozilla-Central

Backout:
https://hg.mozilla.org/mozilla-central/rev/417acde736e7
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Target Milestone: mozilla32 → ---
Thanks a lot, Mounir!  Can you please send an intent to ship message to dev-platform for this as per https://wiki.mozilla.org/WebAPI/ExposureGuidelines?  Thanks!
(In reply to Ed Morley [:edmorley UTC+0] from comment #77)
> Also this appears untested, since it caused breakage:
> https://tbpl.mozilla.org/php/getParsedLog.php?id=39195081&tree=Mozilla-
> Central
> https://tbpl.mozilla.org/php/getParsedLog.php?id=39195467&tree=Mozilla-
> Central

I think you want MOZ_UTF16("intl.accept_languages") there...
try: https://tbpl.mozilla.org/?tree=Try&rev=3ac5be64c90f
m-i: https://hg.mozilla.org/integration/mozilla-inbound/rev/45ae4b138069
intent: https://groups.google.com/forum/#!topic/mozilla.dev.platform/HMJwqYrR9hQ

I think the breakage I provoked is underlying a real issue: the compile failure was only happening in debug builds and a simple `./mach build` doesn't produce a debug build. That sounds fairly unfortunate.

Also, I did sent my patch to try, two times, but the build issue appears while following a review comment. To save resources, I assume that running the tests locally to test those changes would be enough given that they touched only the code I was writing. Little I knew that my build was a release build and an obvious breakage could happen in a non-release build.
Target Milestone: --- → mozilla32
Blocks: 975020
No longer blocks: 975020
Blocks: 975020
https://hg.mozilla.org/mozilla-central/rev/45ae4b138069
Status: REOPENED → RESOLVED
Closed: 10 years ago10 years ago
Resolution: --- → FIXED
Depends on: 1012624
No longer blocks: 975020
(In reply to Zibi Braniecki [:gandalf] from comment #60)
> Stas: are you ok with navigator.languages using intl.accept_languages for
> now?

Clearing an old needinfo.  Gandalf's question was related to an even older conversation we had about user preferences wrt languages for content and UI.  However, this separation is now less relevant.  (So yes, I'm ok with this :)
Flags: needinfo?(stas)
relnote-firefox: --- → ?
Added in the release notes with the wording:
"navigator.languages property and languagechange event implemented (learn more)"
learn more pointing to https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage.languages
I'm in the progress of completing the documentation started by zyunfei. 

I have a few questions:
1. NavigatorLanguage.languages is implemented by both Navigator and WorkerNavigator. Is this attribute available to workers thanks to this bug? (That is: no more specific work needed)
2. The onlanguagechange property is defined on WindowEventHandlers. In Gecko, WEH is implemented by Window and HTMLBodyElement, meaning that this bug added this property to both these interfaces. In the HTML WHATWG spec, WEH should also implemented by HTMLIFrameElement, but this is not the case in Gecko (and therefore onlanguagechange is not available there). Is this correct? If so, is there a bug to follow to know when HTMLIFrameElement will implement WEH?

ni: Mounir for 1, ms2ger for 2 :-)
Flags: needinfo?(mounir)
Flags: needinfo?(Ms2ger)
1. No, http://w3c-test.org/workers/interfaces/WorkerUtils/navigator/001.html shows neither .language or .languages are currently supported in workers. Feel free to file that.
2. AFAICT, WEH should not be implemented by HTMLIFrameElement, but by HTMLFrameSetElement, and we do that in Gecko.
Flags: needinfo?(mounir)
Flags: needinfo?(Ms2ger)
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: