Globalization API Feedback - moar!
Similar approach was proposed (with locale as a top object, others under it) and I have nothing against it, but there are some issues with your approach:
(code == localeID)
-
An implementation may support NumberFormat for localeID x, but not support DateFormat for x (it would need to do a fallback to less specific one, or default). That's why we have supportedLocaleOf method on each object.
-
How do you convey status of option/locale resolution to the developer? Which options were resolved and to what value (say I ask for 'islamic' calendar, but we only have 'gregory' for a given locale). In our current proposal we expose resolvedOptions accessor on i.e. DateTimeFormat object instance that has 'calendar' property, so a developer can decide what to do.
-
This approach would require internal caching of collator/dateformatter/numberformatter objects.
-
новембар 2011. 12.09, Nicholas C. Zakas <standards at nczconsulting.com>је написао/ла:
This API is very JavaScript and very easily understood.
Great work Nicholas and Norbert
On 11/23/2011 12:57 PM, Nebojša Ćirić wrote:
Similar approach was proposed (with locale as a top object, others under it) and I have nothing against it, but there are some issues with your approach:
(code == localeID)
Sorry for being unclear - I didn't intend for this to be a complete alternate proposal, just a starting point. There are definitely still issues that would have to be resolved.
- An implementation may support NumberFormat for localeID x, but not support DateFormat for x (it would need to do a fallback to less specific one, or default). That's why we have supportedLocaleOf method on each object.
So what you're saying is that there needs to be some way to feature detect support for number and date formats separately. That could be handled in any number of ways. One that pops to mind would be isDateFormatSupported()/isNumberFormatSupported() as an instance method.
- How do you convey status of option/locale resolution to the developer? Which options were resolved and to what value (say I ask for 'islamic' calendar, but we only have 'gregory' for a given locale). In our current proposal we expose resolvedOptions accessor on i.e. DateTimeFormat object instance that has 'calendar' property, so a developer can decide what to do.
Thanks, I was having trouble understanding what resolvedOptions was used for. Could the use case be handled by having a similar object on a Locale instance? It seems like you could include options for available calendars and anything else that developers could query against, such as:
var locale = new Locale();
if (locale.supportedOptions.islamicCalendar){
//foo
}
You could also go a more traditional direction (at least in terms of DOM objects), by doing something like:
Locale.CALENDAR_ISLAMIC = 1;
Locale.CALENDAR_GREGORIAN = 2;
var locale = new Locale();
locale.isSupported(Locale.CALENDAR_ISLAMIC);
I think feature detection is an easily solved problem if everything else is in place.
- This approach would require internal caching of collator/dateformatter/numberformatter objects.
That's an implementation detail. I'm more interested in defining an usable and relatively intuitive API before worrying about optimization.
- новембар 2011. 14.32, Nicholas C. Zakas <standards at nczconsulting.com>је написао/ла:
On 11/23/2011 12:57 PM, Nebojša Ćirić wrote:
Similar approach was proposed (with locale as a top object, others under it) and I have nothing against it, but there are some issues with your approach:
(code == localeID)
Sorry for being unclear - I didn't intend for this to be a complete alternate proposal, just a starting point. There are definitely still issues that would have to be resolved.
I just feel we are going in circles sometimes :). I am surprised Norbert agreed with this approach - I think he was against top level Locale object.
- An implementation may support NumberFormat for localeID x, but not support DateFormat for x (it would need to do a fallback to less specific one, or default). That's why we have supportedLocaleOf method on each object.
So what you're saying is that there needs to be some way to feature detect support for number and date formats separately. That could be handled in any number of ways. One that pops to mind would be isDateFormatSupported()/isNumberFormatSupported() as an instance method.
That would probably work. We could add more methods in the future - say one that tells you closest locale to the current one that does support service in question.
- How do you convey status of option/locale resolution to the developer?
Which options were resolved and to what value (say I ask for 'islamic' calendar, but we only have 'gregory' for a given locale). In our current proposal we expose resolvedOptions accessor on i.e. DateTimeFormat object instance that has 'calendar' property, so a developer can decide what to do.
Thanks, I was having trouble understanding what resolvedOptions was used for. Could the use case be handled by having a similar object on a Locale instance? It seems like you could include options for available calendars and anything else that developers could query against, such as:
var locale = new Locale(); if (locale.supportedOptions.islamicCalendar){ //foo }
You could also go a more traditional direction (at least in terms of DOM objects), by doing something like:
Locale.CALENDAR_ISLAMIC = 1; Locale.CALENDAR_GREGORIAN = 2; var locale = new Locale(); locale.isSupported(Locale.CALENDAR_ISLAMIC);
I think feature detection is an easily solved problem if everything else is in place.
Sometimes options can influence each other. For example:
- Ask for th locale (Thai)
- There are two calendars available - buddhist and gregory
- There are two numbering systems available - thai and latin
but only buddhist + thai and gregory + latin combinations are supported.
If you ask locale.isSupported('calendar': 'buddhist') you'll get true. If you ask locale.isSupported('numberingSystem', 'latin') you'll get true again. If you try to format date using that combination (thai + latin) you'll get something you didn't expect.
I would propose sligthly different isSupported method:
locale.returnSupported(serviceName, options), where serviceName is one of 'dateFormat', 'numberFormat', 'collator', options object contains requested settings (calendar, numbering system, collation options...) and method returns the object with supported features for a given service.
- This approach would require internal caching of collator/dateformatter/numberformatter objects.
That's an implementation detail. I'm more interested in defining an usable and relatively intuitive API before worrying about optimization.
I agree, it's implementation detail, but you will need to pass format parameter to each call of .format() method. I don't think that's a big problem. Also I would move format and option parameters to the last position (can be optional), so that user can specify only value and rely on defaults.
I didn't agree with this approach, and Nicholas didn't claim that I did :-)
I'm very glad though that Nicholas is taking the time to provide feedback, come up with his own ideas, and discuss them with us. In the end, the Globalization API can only be successful if people like him are comfortable using the API in their projects, and explain it to others so that they're comfortable doing so. Right now, the feedback from him, Rick, several TC39 members, and others indicates significant discomfort, so we have some work to do. Some of that work may be changes to the API, but some may also be better explanation of how to use the API, directly from applications or in higher-level libraries.
More after Thanksgiving.
Norbert
Again, my apologies - I didn't mean to imply that Norbert agreed with any of this, just that a few ideas have been more solidified in my mind after speaking with him.
As a web developer who has built large-scale web sites that have been internationalized to dozens of countries, my main purpose in contributing to this discussion is to provide feedback on what I would have liked to see in such an API to make it useful to me.
The current proposal doesn't feel very JavaScript-like, and so I've been trying to offer alternatives that make it more JavaScript-like and, therefore, more likely to be used by more developers. I'm a bit concerned that design decisions seem to have been guided by considering the most complex use cases instead of the most common.
It is my opinion (and I can only speak for myself) that a single object to encompass would represent a better API for JavaScript than adding a namespace, which hasn't been done to this point, and several new types, all of which just do one thing. That's a very Java-like approach, and I think JavaScript deserves better.
As I told Norbert, I'm very happy to lend my experience and insights to this process. I realize I may end up bringing things up that you all have discussed before - but considering that you did have a single Locale object at one point, I'd like to claim "great minds" think alike and continue discussing it. :)
Happy Thanksgiving.
It's my fault - I read "discussed with Norbert" as if you and Norbert agreed on this approach.
We are essentially choosing beween two approaches:
- Create an object, query its properties, use object methods (original approach)
- Create Locale object, call various methods like isSupportedXXX, if service is supported use formatYYY or compare
They are very similar in what the user has to do (query capabilites, call formatters) to get the final result, so I don't think we would lose any functionality picking any of those.
With 1. we might end up having lots of isSupportedDate, isSupportedNumber... after couple of iterations of adding new features (like segmentation, calendars, spell check...), but if we follow the same naming style I don't see a problem with that.
As for the namespace issue, I don't see much difference between Locale and Globalization. We discussed Modules on the other thread and came up with:
Object.system.load('@g11n', callback()) {}
or synchronous call
var global = Object.system.load('@g11n') { return Globalization; }
This would become module global import '@g11n' in the future. This approach eliminates the need of finding proper name for the namespace (and possibly for Locale()?).
Thank you for helping out. Your proposal is exactly what we need at this point - to help us refine our work and make it palatable to TC39 members :).
- новембар 2011. 16.47, Nicholas C. Zakas <standards at nczconsulting.com>је написао/ла:
- новембар 2011. 11.27, Nicholas C. Zakas <standards at nczconsulting.com>је написао/ла:
On 11/28/2011 9:15 AM, Nebojša Ćirić wrote:
It's my fault - I read "discussed with Norbert" as if you and Norbert agreed on this approach.
No worries - email is hard. :)
We are essentially choosing beween two approaches:
- Create an object, query its properties, use object methods (original approach)
- Create Locale object, call various methods like isSupportedXXX, if service is supported use formatYYY or compare
They are very similar in what the user has to do (query capabilites, call formatters) to get the final result, so I don't think we would lose any functionality picking any of those.
With 1. we might end up having lots of isSupportedDate, isSupportedNumber... after couple of iterations of adding new features (like segmentation, calendars, spell check...), but if we follow the same naming style I don't see a problem with that.
Another approach would be to have a single isSupported() method with constants that you pass in. That limits the overall API size while still allowing developers to query capabilities. The DOM has something similar, though I think the globalization use case is much more appropriate: www.w3.org/TR/DOM-Level-3-Core/core.html#DOMFeatures
What would be harder for a third party developer to extend:
isSupported('x') or isSupportedX()
This in case they wanted to provide a service which native implementation doesn't support (yet).
Some feedback on the API. This is a bit of stream-of-consciousness response, but figured it would be better to get it out than to delay & clean it up.
The internationalization issues that people may not be used to are:
- *Big data requirements. *A collation sequence for Chinese, for example, is quite large.
- *Changes over time. *There are improvements all the time. In CLDR, for example, there is an increase in data by typically 30-50% each year. These can be additions of data for the less-well supported languages (say, Uzbek), or fixes in data.
- *Changes between platforms. *The collation for German on an iPhone (which uses ICU) may differ from one for German on Windows 8, yet both can be completely satisfactory for German users. That may be because the characters that differ (say punctuation) have no fixed user expectations among Germans, or it may be that there are well known acceptable alternatives (phonebook vs dictionary sorting).
- *Variants. *Implementations typically support a main language (such as French or Uzbek), with deltas for some set of variants (Canadian French, Belgian French, ... Cyrillic Uzbek, Arabic Uzbek,...). But the exact set depends on the implementation (and version). Especially in the case of variants, one service (eg collation) might have no difference between a variant and the principal language, while another service (date formatting) might have a significant difference.
- *Best Fit. *For the majority of implementations, it is far better to return a "best match" than wrong language. So if the request is for French (Canadian) collation, and the best available is French Belgian, then it is best to return that (rather than some system default, like Japanese). However, the caller may need to know exactly what the fallback was, in case some actions do need to be taken.
- *Initialization overhead. *For many of the i18n services, but
especially collation, there is a need for individual comparisons to be as
fast as possible. The actual mechanics of how to do this across languages
are far more complicated than most people realize, so typically you build a
service object that allows you to do the fastest job for the given set of
options. When no more operations will be done with the service object, it
can be tossed. That way the caller can determine the appropriate time to
jettison the object. Think of it, if you will, like how a file system
works. Typically you do something like
- file = open(name);
- while (true) {handle(file.readByte());}
- file.close(); An alternative would be not having open/close calls, and depending on the OS caching which files are open or not.
- while(true) {handle(readByte(name));} But that would be very cumbersome to support in practice.
Locale.isLocaleSupported(code)
For these reasons, isLocaleSupported doesn't really work right. There can be different levels of support for different services, and even for the same service I can get back a result which isn't precisely what I asked for, but is sufficient for my web application. For example, if I were to create a given service (a collator for example) for a given locale (say German for Austria), with a given set of parameters (such as phonebook order), I might not get an exact match for what I requested: I could get a collator for German for Germany with phonebook order, or a collator for German for Austria with dictionary order. That's why the current API returns not only the service, but also the set of parameters for the best fit match.
It would have to be something like
resultingOptions = Locale.getCollationSupport(options)
or
resultingOptions = Locale.getSupport("collation", options)
That would have to go through the logic for figuring out the best match for the options that you would use when creating the service, but then just not create the actual service.
locale.compare("foo", "bar");
This can be done, but means that for performance, internally there will be a service object for collation that needs to be cached and managed. Or maybe performance isn't a concern in an EcmaScript context.
Mark — Il meglio è l’inimico del bene — * * * [plus.google.com/114199149796022210033] *
Re: “Changes” I’d like to also include “Changes between users”. Some users may prefer variants that aren’t normal for their language for whatever reason. So a system that is nominally based on ICU or another consistent behavior could still show variations if users are allowed to select their own preferences.
-Shawn
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Mark Davis ? Sent: Monday, November 28, 2011 1:13 PM To: Nebojša Ćirić Cc: es-discuss at mozilla.org Subject: Re: Globalization API Feedback - moar!
Some feedback on the API. This is a bit of stream-of-consciousness response, but figured it would be better to get it out than to delay & clean it up.
The internationalization issues that people may not be used to are:
-
Big data requirements. A collation sequence for Chinese, for example, is quite large.
-
Changes over time. There are improvements all the time. In CLDR, for example, there is an increase in data by typically 30-50% each year. These can be additions of data for the less-well supported languages (say, Uzbek), or fixes in data.
-
Changes between platforms. The collation for German on an iPhone (which uses ICU) may differ from one for German on Windows 8, yet both can be completely satisfactory for German users. That may be because the characters that differ (say punctuation) have no fixed user expectations among Germans, or it may be that there are well known acceptable alternatives (phonebook vs dictionary sorting).
-
Variants. Implementations typically support a main language (such as French or Uzbek), with deltas for some set of variants (Canadian French, Belgian French, ... Cyrillic Uzbek, Arabic Uzbek,...). But the exact set depends on the implementation (and version). Especially in the case of variants, one service (eg collation) might have no difference between a variant and the principal language, while another service (date formatting) might have a significant difference.
-
Best Fit. For the majority of implementations, it is far better to return a "best match" than wrong language. So if the request is for French (Canadian) collation, and the best available is French Belgian, then it is best to return that (rather than some system default, like Japanese). However, the caller may need to know exactly what the fallback was, in case some actions do need to be taken.
-
Initialization overhead. For many of the i18n services, but especially collation, there is a need for individual comparisons to be as fast as possible. The actual mechanics of how to do this across languages are far more complicated than most people realize, so typically you build a service object that allows you to do the fastest job for the given set of options. When no more operations will be done with the service object, it can be tossed. That way the caller can determine the appropriate time to jettison the object. Think of it, if you will, like how a file system works. Typically you do something like
-
file = open(name);
-
while (true) {handle(file.readByte());}
-
file.close(); An alternative would be not having open/close calls, and depending on the OS caching which files are open or not.
-
while(true) {handle(readByte(name));} But that would be very cumbersome to support in practice. Locale.isLocaleSupported(code)
For these reasons, isLocaleSupported doesn't really work right. There can be different levels of support for different services, and even for the same service I can get back a result which isn't precisely what I asked for, but is sufficient for my web application. For example, if I were to create a given service (a collator for example) for a given locale (say German for Austria), with a given set of parameters (such as phonebook order), I might not get an exact match for what I requested: I could get a collator for German for Germany with phonebook order, or a collator for German for Austria with dictionary order. That's why the current API returns not only the service, but also the set of parameters for the best fit match.
It would have to be something like
resultingOptions = Locale.getCollationSupport(options) or resultingOptions = Locale.getSupport("collation", options)
That would have to go through the logic for figuring out the best match for the options that you would use when creating the service, but then just not create the actual service.
locale.compare("foo", "bar");
This can be done, but means that for performance, internally there will be a service object for collation that needs to be cached and managed. Or maybe performance isn't a concern in an EcmaScript context.
Mark — Il meglio è l’inimico del bene —
[plus.google.com/114199149796022210033]
On Mon, Nov 28, 2011 at 17:15, Nebojša Ćirić <cira at google.com<mailto:cira at google.com>> wrote:
It's my fault - I read "discussed with Norbert" as if you and Norbert agreed on this approach.
We are essentially choosing beween two approaches:
- Create an object, query its properties, use object methods (original approach)
- Create Locale object, call various methods like isSupportedXXX, if service is supported use formatYYY or compare
They are very similar in what the user has to do (query capabilites, call formatters) to get the final result, so I don't think we would lose any functionality picking any of those.
With 1. we might end up having lots of isSupportedDate, isSupportedNumber... after couple of iterations of adding new features (like segmentation, calendars, spell check...), but if we follow the same naming style I don't see a problem with that.
As for the namespace issue, I don't see much difference between Locale and Globalization. We discussed Modules on the other thread and came up with:
Object.system.load('@g11n', callback()) {}
or synchronous call
var global = Object.system.load('@g11n') { return Globalization; }
This would become module global import '@g11n' in the future. This approach eliminates the need of finding proper name for the namespace (and possibly for Locale()?).
Thank you for helping out. Your proposal is exactly what we need at this point - to help us refine our work and make it palatable to TC39 members :).
- новембар 2011. 16.47, Nicholas C. Zakas <standards at nczconsulting.com<mailto:standards at nczconsulting.com>> је написао/ла:
Again, my apologies - I didn't mean to imply that Norbert agreed with any of this, just that a few ideas have been more solidified in my mind after speaking with him.
As a web developer who has built large-scale web sites that have been internationalized to dozens of countries, my main purpose in contributing to this discussion is to provide feedback on what I would have liked to see in such an API to make it useful to me.
The current proposal doesn't feel very JavaScript-like, and so I've been trying to offer alternatives that make it more JavaScript-like and, therefore, more likely to be used by more developers. I'm a bit concerned that design decisions seem to have been guided by considering the most complex use cases instead of the most common.
It is my opinion (and I can only speak for myself) that a single object to encompass would represent a better API for JavaScript than adding a namespace, which hasn't been done to this point, and several new types, all of which just do one thing. That's a very Java-like approach, and I think JavaScript deserves better.
As I told Norbert, I'm very happy to lend my experience and insights to this process. I realize I may end up bringing things up that you all have discussed before - but considering that you did have a single Locale object at one point, I'd like to claim "great minds" think alike and continue discussing it. :)
Happy Thanksgiving.
-Nicholas
On 11/24/2011 2:44 PM, Norbert Lindenberg wrote: I didn't agree with this approach, and Nicholas didn't claim that I did :-)
I'm very glad though that Nicholas is taking the time to provide feedback, come up with his own ideas, and discuss them with us. In the end, the Globalization API can only be successful if people like him are comfortable using the API in their projects, and explain it to others so that they're comfortable doing so. Right now, the feedback from him, Rick, several TC39 members, and others indicates significant discomfort, so we have some work to do. Some of that work may be changes to the API, but some may also be better explanation of how to use the API, directly from applications or in higher-level libraries.
More after Thanksgiving.
Norbert
On Nov 23, 2011, at 15:15 , Nebojša Ćirić wrote:
- новембар 2011. 14.32, Nicholas C. Zakas<standards at nczconsulting.com<mailto:standards at nczconsulting.com>> је написао/ла:
On 11/23/2011 12:57 PM, Nebojša Ćirić wrote: Similar approach was proposed (with locale as a top object, others under it) and I have nothing against it, but there are some issues with your approach:
(code == localeID) Sorry for being unclear - I didn't intend for this to be a complete alternate proposal, just a starting point. There are definitely still issues that would have to be resolved.
I just feel we are going in circles sometimes :). I am surprised Norbert agreed with this approach - I think he was against top level Locale object.
- An implementation may support NumberFormat for localeID x, but not support DateFormat for x (it would need to do a fallback to less specific one, or default). That's why we have supportedLocaleOf method on each object. So what you're saying is that there needs to be some way to feature detect support for number and date formats separately. That could be handled in any number of ways. One that pops to mind would be isDateFormatSupported()/isNumberFormatSupported() as an instance method.
That would probably work. We could add more methods in the future - say one that tells you closest locale to the current one that does support service in question. 2. How do you convey status of option/locale resolution to the developer? Which options were resolved and to what value (say I ask for 'islamic' calendar, but we only have 'gregory' for a given locale). In our current proposal we expose resolvedOptions accessor on i.e. DateTimeFormat object instance that has 'calendar' property, so a developer can decide what to do. Thanks, I was having trouble understanding what resolvedOptions was used for. Could the use case be handled by having a similar object on a Locale instance? It seems like you could include options for available calendars and anything else that developers could query against, such as:
var locale = new Locale();
if (locale.supportedOptions.islamicCalendar){
//foo
}
You could also go a more traditional direction (at least in terms of DOM objects), by doing something like:
Locale.CALENDAR_ISLAMIC = 1;
Locale.CALENDAR_GREGORIAN = 2;
var locale = new Locale();
locale.isSupported(Locale.CALENDAR_ISLAMIC);
I think feature detection is an easily solved problem if everything else is in place.
Sometimes options can influence each other. For example:
- Ask for th locale (Thai)
- There are two calendars available - buddhist and gregory
- There are two numbering systems available - thai and latin
but only buddhist + thai and gregory + latin combinations are supported.
If you ask locale.isSupported('calendar': 'buddhist') you'll get true. If you ask locale.isSupported('numberingSystem', 'latin') you'll get true again. If you try to format date using that combination (thai + latin) you'll get something you didn't expect.
I would propose sligthly different isSupported method:
locale.returnSupported(serviceName, options), where serviceName is one of 'dateFormat', 'numberFormat', 'collator', options object contains requested settings (calendar, numbering system, collation options...) and method returns the object with supported features for a given service. 3. This approach would require internal caching of collator/dateformatter/numberformatter objects. That's an implementation detail. I'm more interested in defining an usable and relatively intuitive API before worrying about optimization.
I agree, it's implementation detail, but you will need to pass format parameter to each call of .format() method. I don't think that's a big problem. Also I would move format and option parameters to the last position (can be optional), so that user can specify only value and rely on defaults. 23. новембар 2011. 12.09, Nicholas C. Zakas<standards at nczconsulting.com<mailto:standards at nczconsulting.com>> је написао/ла:
After meeting with Norbert to discuss the use cases and design decision rationale, I've come to a different understanding of the goals of the globalization API. Some things I learned:
- Augmenting native types with some default locale support may be dangerous. Consider the case where a single web page displays two modules with different locales. Which one wins? Therefore, "default" locale behavior for native types is impractical.
- Locale information is most frequently used for formatting numbers and dates as well as comparing strings. The locale information doesn't permeate the entire execution context.
- Developers are likely to want to define locale information once and then reuse that multiple times through a script.
Given this, I'd like to propose an alternate approach to the one currently taken in the API and also different from my initial email. It goes like this:
Have a single, top-level type called Locale defined as:
function Locale(code){
//whatever has to happen to process the code
this.code = code;
}
/* * Determine if a locale is supported. * @param code The code to check. * @return True if supported, false if not. */ Locale.isLocaleSupported = function(code){ ... };
/* * Replaces supportedLocalesOf * @param code The code to check. * @return Array of supported locales. */ Locale.getSupportedLocales = function(code){ ... };
/* * Replaces Globalization.Collator * @param a The first item. * @param b The second item. * @param options (Optional) The options to use when comparing. * @return -1 if a comes before b, 0 if they're equal, 1 otherwise */ Locale.prototype.compare = function(a, b, options){ ... };
/* * Replaces Globalization.NumberFormat * @param format A pattern format string for outputting the number. * @param value The value to format. * @return The number formatted as a string. */ Locale.prototype.formatNumber = function(format, value){ ... };
/* * Replaces Globalization.DateFormat * @param format A pattern format string for outputting the date. * @param value The date to format. * @return The number formatted as a string. */ Locale.prototype.formatDate = function(format, value){ ... };
You would then be able to create a single Locale instance and have that be used in your script. If the constructor is used without an argument, then default locale information is used:
var locale = new Locale();
If you provide a code, then that is used:
var locale = new Locale("en-us");
If you provide multiple codes, then the first supported one is used:
var locale = new Locale(["en-us", "en-gb"]);
Then, you can use that locale information for the other operations you want to do:
locale.formatDate("DMYs-short", new Date()); locale.formatNumber("##.##", 55); locale.compare("foo", "bar");
By the way, not saying this is the format pattern string that should be used, it's just for discussion.
I like having a single object to deal with instead of multiple for everything the API is trying to do. It seems a lot more intuitive than needing to manage a LocaleList that is passed into new instances of NumberFormat and DateFormat all the time (that's a bunch of housekeeping for developers).
Thoughts?
-Nicholas
On 11/21/2011 11:12 AM, Nicholas C. Zakas wrote: As promised, more verbose feedback for the Globalization API. My general feeling is that the API is overly verbose for what it's doing. I'll state my bias up front: I'm not a fan of introducing a bunch of new types to handle formatting. I'd much rather have additional methods that perform formatting on existing objects. My feedback is mostly about eliminating the new constructors - which has an added bonus of eliminating the Globalization namespace because there would be only one constructor left: Collator.
- LocaleList
I'm not sure why this type is necessary. I don't believe that locale resolution is an expensive operation, and even if it is, I'd expect the implementation to cache the results of such resolution for later use. I'd just leave this as an internal construct and instruct developers to use arrays all the time.
- supportedLocalesOf
I find this method name strange - I've read it several times and am still not sure I fully understand what it does. Perhaps "getSupportedLocales()" is a better name for this method? (I always prefer methods begin with verbs.)
- NumberFormat
Number formatting seems simple enough that it could just be added as a series of methods on Number.prototype. The three types of formatting (currency, decimal, percent) could each have their own method. Currency formatting has relatively few options to specify, so it's method can be:
/* * Formats the number as if it were currency * @param code Currency code, e.g., "EUR" * @param type (Optional) The way to format the currency code, "code", "symbol" (default), * @param locales - (Optional) Array of locales to use. */ Number.prototype.toCurrencyString = function(code, type, locales) { ... };
var num = 500; console.log(num.toCurrencyCode("EUR", "code")); //"EUR 500.00"
Decimal and percent formatting options are slightly different in that they include significant digits options. For that, I prefer to use a formatting string rather than the multitude of optional properties as currently defined (see www.exampledepot.com/egs/java.text/FormatNum.html). The formatting string indicates must-have digits as 0 and optional digits as #, allowing you to very succinctly specify how you want your number to be output. For example:
/* * Formats the number as a decimal string. * @param format Format string indicating max/min significant digits * @param locales (Optional) Array of locales to use. */ Number.prototype.toDecimalString = function(format, locales){ ... };
/* * Formats the number as a percent string. * @param format Format string indicating max/min significant digits * @param locales (Optional) Array of locales to use. */ Number.prototype.toPercentString = function(format, locales){ ... };
var num = 1234.567; console.log(numtoDecimalString("000##.##")); "01234.57"
- DateTimeFormat
As with NumberFormat, it seems like this could more succinctly be implemented as a method on Date.prototype. As its easiest:
/* * Format a date * @param options The already-defined options for DateTimeFormat * @param locales (Optional) Array of locales to use. */ Date.prototype.toFormatString = function(options, locales){ ... };
In an ideal world, I'd like to see options overloaded so it can be an options object as specified now or a formatting string. I understand that there was a sentiment against formatting strings due to their limitations and edge case errors. However, I'd like to point out that any internationalized web application is highly likely to already be using formatting strings for dates, since this is pretty much how every other language handles date formatting. That means supporting format strings in JavaScript would allow application developers to reuse the settings they already have. As it stands now, you'd need to create two different ways of formatting dates for a web app: one for your server-side language and one for your client-side language (until the day everything is running on Node.js, of course). I'd prefer my client-side code to reuse settings and configuration that the server-side code uses, otherwise I end up with two very different pieces of code doing the exact same thing, and there be dragons.
Good point: user's customizations/preferences may also change results.
Mark — Il meglio è l’inimico del bene — * * * [plus.google.com/114199149796022210033] *
I'm not sure why this discussion is drifting so far in the direction of a functional API. I thought objects, constructors, and methods are all part of ECMAScript, are widely used in the language specification, in libraries, and in applications, and developers have to understand them in order to be productive in JavaScript. So what is the advantage of isSupportDateTimeFormat(locale) or isSupported("DateTimeFormat", locale) over DateTimeFormat.isSupported(locale)?
I thought the real question is: How do we enable simpler code for the most common use cases? For example, don't require applications to create a DateTimeFormat object when it's only used once to format a date and then disposed of, or to specify a locale list to every API call when it never changes.
We should also consider what should be handled by the Globalization API, and what is better left to libraries. We tend to focus on the Globalization API in this discussion because that's what we can change directly, but I think default locale management is really better handled at a higher level. See esdiscuss/2011-November/018665
Specific comments on Nicholas proposal:
-
Extensibility is a big issue: What happens if two components within one application load two incompatible word break implementations - who wins, or is there a way to keep them separate?
-
The existing locale and parameter negotiation won't work with other services: Our current negotiation mechanism knows a lot about collators and number and date formats, and nothing about other internationalization services, and it's not designed for adaptation to such other services. This means other services have to provide their own negotiation, but that conflicts with them being part of a single Locale object.
-
Having the Locale object store the resolved locale doesn't work because the resolved locale can vary between services for the same requested locale list. It probably makes more sense for it to store the requested locale list, and use that to negotiate against each service's capabilities. That's the approach used in YUI internationalization: yuilibrary.com/yui/docs/intl
-
The Locale object as proposed doesn't seem to help the application with managing its own resources. YUI, on the other hand, provides support for this, and it seems more logical to extend the locale management in YUI to also support the services provided by the Globalization API than to have two separate mechanisms. For example, it could complement its current number formatting with a new locale sensitive function:
Y.DataType.Number.localeFormat = function (data, options) { if (Y.Lang.isNumber(data)) { var localeList = Y.config.lang; if (!Y.Lang.isArray(localeList)) { localeList = localeList.split(/[, ]/); } return data.toLocaleString(localeList, options); } else { // do whatever Y.DataType.Number.format does in this case } }
Overall I think a container like the proposed Locale object may make sense at the application level, but doesn't seem like the right approach for the Globalization API.
, Norbert
- новембар 2011. 11.27, Nicholas C. Zakas <standards at nczconsulting.com> је написао/ла:
The reason I started down this path is because the API, as currently designed, doesn't fit in with the rest of the JavaScript core language. Just to summarize some of my earlier points:
-
No part of core JavaScript uses namespaces today. Having a top-level Globalization object that doesn't nothing other than provide a hook onto which new constructors are placed is a first and doesn't seem very useful. At the very least I'd consider killing the Globalization object altogether and just let the constructors exist in the global scope.
-
The current proposal requires that developers maintain a LocaleList object and pass that into methods of NumberFormat, DateTimeFormat, and Collator (assuming they don't want to use the default). Needing to keep track of an object is a pain, but is lessened when that object does everything you need it to. That's why I suggested having a Locale object. Yes, you'd need to keep track of this object, but at least it would be one object instead of multiple objects.
-
The semantic difference between DateTimeFormat.isSupported(), Locale.isSupported(), etc. is minimal. The point I was trying to make is that the current requirements for the Globalization API don't preclude having a single object. That's simply a design decision, not a huge barrier that can be overcome.
Again, saying that "libraries will handle this" is too simple and presumptive. You're assuming that some library a) likes what you've done enough to invest in it, b) finds some advantage to this API over what it has already, and c) can integrate with what they already have. You could create an API that sits unused because libraries don't see any advantage to using it vs. what they have already.
On Nov 29, 2011, at 1:19 PM, Nicholas C. Zakas wrote:
The reason I started down this path is because the API, as currently designed, doesn't fit in with the rest of the JavaScript core language. Just to summarize some of my earlier points:
- No part of core JavaScript uses namespaces today. Having a top-level Globalization object that [does] nothing other than provide a hook onto which new constructors are placed is a first and doesn't seem very useful. At the very least I'd consider killing the Globalization object altogether and just let the constructors exist in the global scope.
Injecting new names into the global scope is risky, since all the good names are already taking by user-defined globals.
The plan we discussed a few weeks ago was to use a built-in module, which could be imported from or declared freely in new opt-in code. Unversioned JS would have to use something like the
Object.system.loadIntrinsic("@globalization")
API-sketch.
Whatever we do, it should be as future-friendly to modules as possible, and it shouldn't inject new global names into the shared scope of ES.next and unversioned scripts.
- The current proposal requires that developers maintain a LocaleList object and pass that into methods of NumberFormat, DateTimeFormat, and Collator (assuming they don't want to use the default). Needing to keep track of an object is a pain, but is lessened when that object does everything you need it to. That's why I suggested having a Locale object. Yes, you'd need to keep track of this object, but at least it would be one object instead of multiple objects.
This still seems plausible to me. It caters to Norbert's "Customer categories" 1 and 2, if I'm not mistaken.
- The semantic difference between DateTimeFormat.isSupported(), Locale.isSupported(), etc. is minimal. The point I was trying to make is that the current requirements for the Globalization API don't preclude having a single object. That's simply a design decision, not a huge barrier that can be overcome.
Mark's point about isSupported not being binary and simplex is good, independent of whether objects are grouped into a Locale or not. Again if I'm following correctly (please tell me if not).
Again, saying that "libraries will handle this" is too simple and presumptive. You're assuming that some library a) likes what you've done enough to invest in it, b) finds some advantage to this API over what it has already, and c) can integrate with what they already have. You could create an API that sits unused because libraries don't see any advantage to using it vs. what they have already.
Again I agree with you. The flip side is that, per Mark's fine message dated November 28, 2011 1:13:24 PM PST, internationalization is multi-state/best-fit and complex, with evolving big data, user preferences in the loop.
Doing a low-level built-in library and letting others built higher-level abstractions is plausible based on experience with things like canvas, WebGL, and typed arrays. We don't get to do the perfect high-level API on short notice, certainly not in committee. I'm skeptical we can even do a low-level toolkit by next spring, with two truly independent implementations interoperating convincingly.
So "libraries will handle this" is the wrong guiding principle, but "let's find the high-level sweet spot" is high-risk without more time for experimentation and user testing.
I don't have any satisfying answers, but I hope this helps.
On 11/30/2011 12:01 PM, Brendan Eich wrote:
On Nov 29, 2011, at 1:19 PM, Nicholas C. Zakas wrote:
The reason I started down this path is because the API, as currently designed, doesn't fit in with the rest of the JavaScript core language. Just to summarize some of my earlier points:
- No part of core JavaScript uses namespaces today. Having a top-level Globalization object that [does] nothing other than provide a hook onto which new constructors are placed is a first and doesn't seem very useful. At the very least I'd consider killing the Globalization object altogether and just let the constructors exist in the global scope. Injecting new names into the global scope is risky, since all the good names are already taking by user-defined globals.
The plan we discussed a few weeks ago was to use a built-in module, which could be imported from or declared freely in new opt-in code. Unversioned JS would have to use something like the
Object.system.loadIntrinsic("@globalization")
API-sketch.
Whatever we do, it should be as future-friendly to modules as possible, and it shouldn't inject new global names into the shared scope of ES.next and unversioned scripts.
To make sure I understand, are you saying that all future ES functionality won't introduce new names into the global scope? And further, all new functionality would need to be loaded via a module?
- The current proposal requires that developers maintain a LocaleList object and pass that into methods of NumberFormat, DateTimeFormat, and Collator (assuming they don't want to use the default). Needing to keep track of an object is a pain, but is lessened when that object does everything you need it to. That's why I suggested having a Locale object. Yes, you'd need to keep track of this object, but at least it would be one object instead of multiple objects. This still seems plausible to me. It caters to Norbert's "Customer categories" 1 and 2, if I'm not mistaken.
Which is plausible? The current proposal or my single-object proposal? :)
- The semantic difference between DateTimeFormat.isSupported(), Locale.isSupported(), etc. is minimal. The point I was trying to make is that the current requirements for the Globalization API don't preclude having a single object. That's simply a design decision, not a huge barrier that can be overcome. Mark's point about isSupported not being binary and simplex is good, independent of whether objects are grouped into a Locale or not. Again if I'm following correctly (please tell me if not).
Agreed, I was just brainstorming about ways to solve some of the issues being brought up. There do seem to be two categories of detection necessary for this API, one is binary (is Gregorian calendar supported) and one is best fit (what's the closest locale you can approximate).
Again, saying that "libraries will handle this" is too simple and presumptive. You're assuming that some library a) likes what you've done enough to invest in it, b) finds some advantage to this API over what it has already, and c) can integrate with what they already have. You could create an API that sits unused because libraries don't see any advantage to using it vs. what they have already. Again I agree with you. The flip side is that, per Mark's fine message dated November 28, 2011 1:13:24 PM PST, internationalization is multi-state/best-fit and complex, with evolving big data, user preferences in the loop.
Doing a low-level built-in library and letting others built higher-level abstractions is plausible based on experience with things like canvas, WebGL, and typed arrays. We don't get to do the perfect high-level API on short notice, certainly not in committee. I'm skeptical we can even do a low-level toolkit by next spring, with two truly independent implementations interoperating convincingly.
So "libraries will handle this" is the wrong guiding principle, but "let's find the high-level sweet spot" is high-risk without more time for experimentation and user testing.
I definitely wasn't trying to suggest a truly high-level abstraction, but more a mid-level API that has some low-level characteristics and some high-level characteristics. Another way to say this is that the API makes the simple case simple ("hi! I'd just like to format a price for display") while allowing more complex cases through other means. I present XMLHttpRequest as a good example of a mid-level API, where it's easy to do a basic GET request but you can still do fancy things using CORS, binary data, HTTP streaming, etc.
On Nov 30, 2011, at 12:44 PM, Nicholas C. Zakas wrote:
On 11/30/2011 12:01 PM, Brendan Eich wrote:
On Nov 29, 2011, at 1:19 PM, Nicholas C. Zakas wrote:
The reason I started down this path is because the API, as currently designed, doesn't fit in with the rest of the JavaScript core language. Just to summarize some of my earlier points:
- No part of core JavaScript uses namespaces today. Having a top-level Globalization object that [does] nothing other than provide a hook onto which new constructors are placed is a first and doesn't seem very useful. At the very least I'd consider killing the Globalization object altogether and just let the constructors exist in the global scope. Injecting new names into the global scope is risky, since all the good names are already taking by user-defined globals.
The plan we discussed a few weeks ago was to use a built-in module, which could be imported from or declared freely in new opt-in code. Unversioned JS would have to use something like the
Object.system.loadIntrinsic("@globalization")
API-sketch.
Whatever we do, it should be as future-friendly to modules as possible, and it shouldn't inject new global names into the shared scope of ES.next and unversioned scripts. To make sure I understand, are you saying that all future ES functionality won't introduce new names into the global scope?
It depends on what you mean by "scope". The shared global object (the window object in the DOM) used today by <script> elements provides shared globals, e.g. parseInt. We would like to avoid introducing new names that appear as properties of this object, which is on the scope chain for pre-Harmony script, in order to avoid collisions.
For the lexical top-level scope in Harmony opt-in code, we can indeed introduce new names in the first Harmony edition, but things get harder after that, for the same reason: user-defined lexical bindings may collide.
And further, all new functionality would need to be loaded via a module?
This is a strong "yes", without qualification so far in my view. We intend for built-in modules to be accessible to unversioned/pre-Harmony script via the heap (Object.system.load...).
There's an open question of whether we import from built-in modules via a standard prelude, per the above "we can indeed introduce new names in the first Harmony edition". My view based on experience is that, even with further opt-in via <script type=...> and |use version N| pragmas, we do not have many chances to extend the standard prelude, if there is one.
- The current proposal requires that developers maintain a LocaleList object and pass that into methods of NumberFormat, DateTimeFormat, and Collator (assuming they don't want to use the default). Needing to keep track of an object is a pain, but is lessened when that object does everything you need it to. That's why I suggested having a Locale object. Yes, you'd need to keep track of this object, but at least it would be one object instead of multiple objects. This still seems plausible to me. It caters to Norbert's "Customer categories" 1 and 2, if I'm not mistaken. Which is plausible? The current proposal or my single-object proposal? :)
Sorry, "this" referred to your Locale object proposal.
So "libraries will handle this" is the wrong guiding principle, but "let's find the high-level sweet spot" is high-risk without more time for experimentation and user testing. I definitely wasn't trying to suggest a truly high-level abstraction, but more a mid-level API that has some low-level characteristics and some high-level characteristics. Another way to say this is that the API makes the simple case simple ("hi! I'd just like to format a price for display") while allowing more complex cases through other means. I present XMLHttpRequest as a good example of a mid-level API, where it's easy to do a basic GET request but you can still do fancy things using CORS, binary data, HTTP streaming, etc.
I'd call XHR low-level in the typed array or WebGL or canvas sense (ignore API size, the issue is semantic level), but ok -- I see your point. The counter-arguments based
And further, all new functionality would need to be loaded via a module?
This is a strong "yes", without qualification so far in my view. We intend for built-in modules to be accessible to unversioned/pre-Harmony script via the heap (Object.system.load...).
This is true for ES6, but the Globalization API does not have any need to take a dependency on ES6. DOM and other host APIs regularly add new names to the global. These will need to be rationalized with modules once modules are available in engines. But I have to imagine they will continue to evolve in current form as well, including adding new global names available to non-extended JavaScript even once modules are available for consistency and developer ease.
Why should the globalization API be treated any differently? It so happens that TC39 is shepherding the development of the standard, but that need not force these APIs into a module-only API design any earlier than the rest of the browser. Given that the globalization APIs are otherwise independent of ES6, this does not seem a compelling reason to couple them to a dependency on a standard with a 1.5 year later target ratification.
Why not just pick a global name, as has been done for many new browser APIs over recent years, and then separately rationalize with modules as part of overall design of how browser and other host APIs will adopt modules?
Luke
On Nov 30, 2011, at 3:28 PM, Luke Hoban wrote:
And further, all new functionality would need to be loaded via a module?
This is a strong "yes", without qualification so far in my view. We intend for built-in modules to be accessible to unversioned/pre-Harmony script via the heap (Object.system.load...).
This is true for ES6, but the Globalization API does not have any need to take a dependency on ES6. DOM and other host APIs regularly add new names to the global.
Not "regularly" -- irregularly and with hacks like [Replaceable]. This will stop at some point, but pushing off that date just makes more work rationalizing with modules later, and risks name collision whenever the Globalization standard actually is done.
These will need to be rationalized with modules once modules are available in engines. But I have to imagine they will continue to evolve in current form as well, including adding new global names available to non-extended JavaScript even once modules are available for consistency and developer ease.
Why shouldn't we stop adding new names to the global object sooner rather than later? It's a hazardous game and there's no real winner. "Globalization" is not wanted by developers or implementors as a new global property.
Why should the globalization API be treated any differently?
Because we're working on it in the same timeframe as ES6 and we have modules in ES6.
It so happens that TC39 is shepherding the development of the standard, but that need not force these APIs into a module-only API design any earlier than the rest of the browser. Given that the globalization APIs are otherwise independent of ES6, this does not seem a compelling reason to couple them to a dependency on a standard with a 1.5 year later target ratification.
Who says the globalization work is going to be standardized (December 2013 - 1.5 years is mid-2012) by next Spring?
I doubt it, given the usual contretemps, the debate on API form and function here, the need for user testing, and the two-or-more independent implementations interoperating requirement. But let's find out. There's no need to prejudge this and push hard on injecting a new name just to be independent of ES6.
Why not just pick a global name, as has been done for many new browser APIs over recent years,
Irregularly, with collisions (even JSON), with consequent lumpy naming, incoherent name schemes, [Replaceable] hacks, etc. etc.
and then separately rationalize with modules as part of overall design of how browser and other host APIs will adopt modules?
There's no way to ratioanalize with modules without duplication -- see the recent @object thread.
And further, all new functionality would need to be loaded via a module?
This is a strong "yes", without qualification so far in my view. We intend for built-in modules to be accessible to unversioned/pre-Harmony script via the heap (Object.system.load...).
This is true for ES6, but the Globalization API does not have any need to take a dependency on ES6. DOM and other host APIs regularly add new names to the global.
Not "regularly" -- irregularly and with hacks like [Replaceable]. This will stop at some point, but pushing off that date just makes more work rationalizing with modules later, and risks name collision whenever the Globalization standard actually is done.
Between IE9 and IE10 PP4 we've already added 93 new global names, and other browsers are similar over the last few years. This includes some reasonably common names like "File", "URL", "AudioTrack", "ProgressEvent" and "applicationCache" which are just as likely to clash as "Globalization". I don't see any reason to believe this is going to stop anytime soon.
These will need to be rationalized with modules once modules are available in engines. But I have to imagine they will continue to evolve in current form as well, including adding new global names available to non-extended JavaScript even once modules are available for consistency and developer ease.
Why shouldn't we stop adding new names to the global object sooner rather than later? It's a hazardous game and there's no real winner. "Globalization" is not wanted by developers or implementors as a new global property.
It's really a question of which 'we' you are talking about. The real topic that needs to get addressed is how browser APIs are going to migrate to modules. As new "HTML5" features come along or grow, are they going to use modules or are they going to continue adding global names for non-extended code consumers? I suspect that current inertia is strongly toward the latter. I see no reason why Globalization should be handled as a special case here. We should just work toward a plan for new browser APIs, and align Globalization with that plan when the time comes.
Why should the globalization API be treated any differently?
Because we're working on it in the same timeframe as ES6 and we have modules in ES6.
But why does it matter that TC39 is working on it vs. W3C? Why make it harder to use in the near term just because it's being shepherded by ECMA?
It so happens that TC39 is shepherding the development of the standard, but that need not force these APIs into a module-only API design any earlier than the rest of the browser. Given that the globalization APIs are otherwise independent of ES6, this does not seem a compelling reason to couple them to a dependency on a standard with a 1.5 year later target ratification.
Who says the globalization work is going to be standardized (December 2013 - 1.5 years is mid-2012) by next Spring?
I doubt it, given the usual contretemps, the debate on API form and function here, the need for user testing, and the two-or-more independent implementations interoperating requirement. But let's find out. There's no need to prejudge this and push hard on injecting a new name just to be independent of ES6.
As far as I understand, the work on an i18n spec has always been independent of ES6. I don't mean to prejudge when either ES6 or the globalization work will be standardized, but we've agreed repeatedly on timelines for the work on the two documents that are at least a year apart. Sure - either could slip - but I don't see why we want to hold either one back from making progress unnecessarily. I don't see the discussion here as justifying on its own merits holding back any progress on the globalization APIs, including using a global name for the API as all other browser APIs will do in the same timeframe as this APIs developed.
Luke
On Nov 30, 2011, at 7:14 PM, Luke Hoban wrote:
And further, all new functionality would need to be loaded via a module?
This is a strong "yes", without qualification so far in my view. We intend for built-in modules to be accessible to unversioned/pre-Harmony script via the heap (Object.system.load...).
This is true for ES6, but the Globalization API does not have any need to take a dependency on ES6. DOM and other host APIs regularly add new names to the global.
Not "regularly" -- irregularly and with hacks like [Replaceable]. This will stop at some point, but pushing off that date just makes more work rationalizing with modules later, and risks name collision whenever the Globalization standard actually is done.
Between IE9 and IE10 PP4 we've already added 93 new global names, and other browsers are similar over the last few years.
s/few/many/ :-|. Playing catch-up doesn't equate to high rate of addition averaged over all browsers across the last five years.
But (more below) the pipelining among standards bodies means we'll have to get modules proven with some built-in examples first, within Ecma TC39, before we get W3C to take a dependency.
This includes some reasonably common names like "File", "URL", "AudioTrack", "ProgressEvent" and "applicationCache" which are just as likely to clash as "Globalization". I don't see any reason to believe this is going to stop anytime soon.
It will slow down (has slowed down) now that the bulk of "HTML5" (including Web API / DOM Core) is done. Anyway:
-
We had real problems with JSON, also with other names. Forcing overlong and ugly names doesn't do anyone any good.
-
The core language is not the DOM or Web APIs writ large. If we're adding modules we could take a dependency. It's not a non-starter just because of the global name hacking needed so far in lieu of modules.
Why shouldn't we stop adding new names to the global object sooner rather than later? It's a hazardous game and there's no real winner. "Globalization" is not wanted by developers or implementors as a new global property.
It's really a question of which 'we' you are talking about. The real topic that needs to get addressed is how browser APIs are going to migrate to modules. As new "HTML5" features come along or grow, are they going to use modules or are they going to continue adding global names for non-extended code consumers? I suspect that current inertia is strongly toward the latter. I see no reason why Globalization should be handled as a special case here. We should just work toward a plan for new browser APIs, and align Globalization with that plan when the time comes.
Gee, that sounds like what I was saying. Are you assuming the alignment comes after the Globalization spec is gunned through Ecma? While perhaps at the same time sandbagging the plan for new browser APIs?
Excuse my cynicism. Inertia is a fact, but why are you embracing it so hard? Getting modules prototyped will take effort and it's easy to starve that initiative by doing more API hacking, ad nauseum.
Why should the globalization API be treated any differently?
Because we're working on it in the same timeframe as ES6 and we have modules in ES6.
But why does it matter that TC39 is working on it vs. W3C? Why make it harder to use in the near term just because it's being shepherded by ECMA?
Because, for one thing, the W3C and other groups so far produce specs that reference published Ecma specs. They do not take dependencies on drafts.
Another reason is that we are all (TC39 proper and the Globalization group) meeting every two months and discussing things here, so rationalizing Globalization as a built-in module looks like a good first test case for "the plan". It avoids the inter-standards-body lawyering, latency, and culture clash problems.
Who says the globalization work is going to be standardized (December 2013 - 1.5 years is mid-2012) by next Spring?
I doubt it, given the usual contretemps, the debate on API form and function here, the need for user testing, and the two-or-more independent implementations interoperating requirement. But let's find out. There's no need to prejudge this and push hard on injecting a new name just to be independent of ES6.
As far as I understand, the work on an i18n spec has always been independent of ES6. I don't mean to prejudge when either ES6 or the globalization work will be standardized, but we've agreed repeatedly on timelines for the work on the two documents that are at least a year apart.
No. At the last TC39 meeting I dissented from the optimism about next Spring (no one mooted next December). We have not all agreed. Anyway, who cares what you or I project? No one has a crystal ball.
We're here to talk about substance, not speculate about schedule. If we have a chance to avoid yet another global name injection because we're doing exactly the same thing -- using built-in modules -- in the same spec-drafting time frame as for other ES6 additions, then we ought to consider doing likewise for Globalization.
I'm not sandbagging the Globalization work, far from it. If it is all but done and the plan to align built-in modules is not at the same roughly ready state, then we should let inertia have a last hurrah -- provided no one has already played unfairly.
Sure - either could slip - but I don't see why we want to hold either one back from making progress unnecessarily.
At this point, nothing is held back by mocking up Globalization as a built-in module. That was trivially stubbed already, from the earlier thread on this topic.
The deeper issues, which Nick Zakas and others have raised, remain the API form and function. We need to settle those instead of overdoing the inertia argument here. Window dressing as built-in module or as pseudo-module namespace object is a sideshow.
Speaking on behalf of real world web developers, the opposition to "Globalization" is that it's unnecessarily long. This is a long standing problem with APIs that are designed by people that don't have to use them everyday.
Speaking on behalf of real world web developers, the opposition to "Globalization" is that it's unnecessarily long. This is a long standing problem with APIs that are designed by people that don't have to use them everyday.
Agreed - a shorter name would be better - but the alternative being discussed here is not a shorter name - it's this tradeoff:
"Globalization" vs. "Object.System.loaded['@globalization']"
That is, the alternative here is 3 times as long as the already 'unnecessarily long' option. As Brendan noted, we still need to do the API design on the system module loader to try to streamline this - but the design space currently being explored won't lead to this being shorter than "Globalization", so the length argument by itself would seem to favor a single global name.
There was an earlier thread discussing alternatives to "Globalization", several of which are shorter and may be appropriate choices instead.
Luke
On Nov 30, 2011, at 9:08 PM, Rick Waldron wrote:
Speaking on behalf of real world web developers, the opposition to "Globalization" is that it's unnecessarily long. This is a long standing problem with APIs that are designed by people that don't have to use them everyday.
Yeah, but shorter names are likelier to collide, unless hideous (e.g., G11n). Which brings us to the built-in modules idea where the importer lexically names the "@globalization" module or imports exported bindings from it.
To get beyond the module vs. predefined-in-global-scope-namespace-object sideshow (I called it that; I agree it's not the main show, but it ain't a mater of indifference either), we could talk about how some of the additions will show up as methods in Date.prototype, e.g. This should happen without any import statement, and it's very unlikely that such built-in prototype extensions will break existing web content. Whatever formatting, collating, etc. object APIs live behind the scenes, this seems worth getting agreement on sooner rather than later.
The namespace or module contents, e.g. the DateTimeFormat constructor, need sorting out independent of the way you access the namespace object or module, too. We should get back to those API details, if any controversies remain. I'm not sure Norbert convinced everyone that localeList must be an independent parameter from options, for example.
I suspect controversies remain. Using ICU in the Google implementation is not enough to ensure that Apple will go along, and without those shiny iOS devices supporting the Globalization API, developers will probably not polyfill the whole thing. IE10 won't have a ton of market share up front, and who knows how market share ends up on phones and tablets, so pressure on Apple to implement by using the Google code may well be limited.
Really, in addition to covering the sideshow issue of module vs. object, and the more significant "main show" API form and function issues, we need to get all the players on board. The last time Ecma produced an at-most-two-vendors-really-wanted-it spec was E4X, and that was a big mess. I don't think Globalization will be that kind of mess, but the precedent is there. We need to get all the significant market players on the same page.
On Nov 30, 2011, at 9:52 PM, Luke Hoban wrote:
Speaking on behalf of real world web developers, the opposition to "Globalization" is that it's unnecessarily long. This is a long standing problem with APIs that are designed by people that don't have to use them everyday.
Agreed - a shorter name would be better - but the alternative being discussed here is not a shorter name - it's this tradeoff:
"Globalization" vs. "Object.System.loaded['@globalization']"
As I just suggested in reply to Rick, I think micro-optimizing here for brevity is not the main thing. Most developers will want the Date.prototype, etc. extensions -- easy to use and better-localizable methods.
All the full-metal OOP APIs for amortizing collator construction costs, etc., will be used less frequently, even if by some big web app properties.
Let me propose, guys:
If time for specification draft + vendors basic implementation less than time to implement fully module based API (get rid of globals at all), then another global name is a solution.
As this topic strategic targeted (IMO) I think it makes sense to draft both options — as a developer I don't care how to call needed method (using module or directly) in any case all tools will use wrappers, so basically: local = Globalization; and local = Object.System.loaded['@globalization']; are the same as much as methods API consistent.
In case I've missed point — sorry.
Anton.
On Nov 30, 2011, at 10:25 PM, Anton Yatsenko wrote:
In case I've missed point — sorry.
There are several points. How one gets the Globalization "module" is one, but the bigger points at issue involve API parameterization, overhead, and style issues.
On Nov 30, 2011, at 11:37 PM, Brendan Eich wrote:
On Nov 30, 2011, at 10:25 PM, Anton Yatsenko wrote:
In case I've missed point — sorry.
There are several points. How one gets the Globalization "module" is one, but the bigger points at issue involve API parameterization, overhead, and style issues.
Also, critically (Shawn Steele's latest post, cited in full below, hit this point) "support inference" or in general, whether and how fallback works.
I know this is related to the actual implementation, but there are some things we won't be able to avoid if we go with new (functional) API. If performance penalty is acceptable then we should proceed with the discussion.
Cases:
- Single format/comparison operation - performance should be mostly the same for both (most of the time would go into creating the support object - say ICU collator)
- Multiple format/comparison invocations - performance should be on the side of object like API, the question is just how much 2a. Both approaches have to create supporting object and process options, but object approach does that only once 2b. Functional approach would have to cache objects, which should help, but in that case it would need to generate hash keys from given options
I wrote a short benchmark (JS code only) to see how much hashing influences the performance. Please take a look at the results and the code (any optimization hints are welcome).
Benchmark: i18n.kaziprst.org/test-driver.html
Code (formatter is intentionally trivial in both cases): i18n.kaziprst.org/funct-impl.js, i18n.kaziprst.org/object-impl.js
- децембар 2011. 11.53, Brendan Eich <brendan at mozilla.com> је написао/ла:
Could we not have both object-based (to amortize construction cost) and functional (with cached object) flavors? Win-win, and lots of precedent in various libraries going back to POSIX era libc IIRC.
I was always pro that (have a cake and eat it too) approach, but some TC39 members were against having 2 ways of doing things (complicates the API). I think that functional approach could reuse the object-based implementation with addition of caching to lower the costs.
At this point we have 3 proposals:
- Tie in to toLocaleString type of methods
- Functional proposal
- Object-based proposal
I think 1. suffices for a casual user that doesn't care about what got resolved to what, and which locale or pattern fallback was used.
We could expose either 2. or 3. to an advanced user who needs more control. Now, if 1. is sufficiently functional (it's built in anyways), then I would go with exposing 3. as is more efficient in general use case.
Keeping all 3 around would be too much.
- децембар 2011. 11.36, Brendan Eich <brendan at mozilla.com> је написао/ла:
On Dec 2, 2011, at 11:56 AM, Nebojša Ćirić wrote:
I was always pro that (have a cake and eat it too) approach, but some TC39 members were against having 2 ways of doing things (complicates the API).
Hrm, I missed that. It's not "the JS way". We do not always or even generally endeavor to confine API expressiveness to one paradigm or "way of doing it".
It happens that the clunky standard library (ECMA-262 Clause 15) looks OOP-ish, like Java, with methods shared via protoypes, but there are functions too (parseInt is one). That's all old stuff that was done in haste, it shouldn't be taken as restrictive holy writ when considering new APIs.
I think that functional approach could reuse the object-based implementation with addition of caching to lower the costs.
Right!
At this point we have 3 proposals:
- Tie in to toLocaleString type of methods
- Functional proposal
- Object-based proposal
I think 1. suffices for a casual user that doesn't care about what got resolved to what, and which locale or pattern fallback was used.
We could expose either 2. or 3. to an advanced user who needs more control. Now, if 1. is sufficiently functional (it's built in anyways),
(1) is all we need in the way of functional (methodical) API, IMHO. If I recall correctly, it includes new methods, not just toLocaleString -- is that correct?
At this point we have 3 proposals:
- Tie in to toLocaleString type of methods
- Functional proposal
- Object-based proposal
I think 1. suffices for a casual user that doesn't care about what got resolved to what, and which locale or pattern fallback was used.
We could expose either 2. or 3. to an advanced user who needs more control. Now, if 1. is sufficiently functional (it's built in anyways),
(1) is all we need in the way of functional (methodical) API, IMHO. If I recall correctly, it includes new methods, not just toLocaleString -- is that correct?
Norbert's list ( strawman:globalization-integration) contains only what's currently available in ES. It is possible that in some future iteration of both standards we may have to add new built in methods (say to handle time zones, calendars, break iterators...) - but that's out of scope for the current spec.
We would need to flesh out actual mechanizm of combining (1) and (3) - importing, what to do if i18n support is not available...
I am leaning towars having (1) and (3) available, skipping (2) completely. We can't remove (1) from the language so we better improve it. The question is - should we use (2) or (3) to achieve that.
After meeting with Norbert to discuss the use cases and design decision rationale, I've come to a different understanding of the goals of the globalization API. Some things I learned:
Given this, I'd like to propose an alternate approach to the one currently taken in the API and also different from my initial email. It goes like this:
Have a single, top-level type called Locale defined as:
You would then be able to create a single Locale instance and have that be used in your script. If the constructor is used without an argument, then default locale information is used:
If you provide a code, then that is used:
If you provide multiple codes, then the first supported one is used:
Then, you can use that locale information for the other operations you want to do:
By the way, not saying this is the format pattern string that should be used, it's just for discussion.
I like having a single object to deal with instead of multiple for everything the API is trying to do. It seems a lot more intuitive than needing to manage a LocaleList that is passed into new instances of NumberFormat and DateFormat all the time (that's a bunch of housekeeping for developers).
Thoughts?