i18n API implementation issues

# Nebojša Ćirić (14 years ago)

Jungshik and I were working on v8 implementation and we have almost everything done (number formatting is not done, and some parts of collation). There are some issues I would like to discuss (I made an update to the strawman):

  1. For LocaleInfo I would like to include construction with plain string: new LocaleInfo("de") vs more verbose new LocaleInfo({'languageID': 'de'} )

One would be able to construct LocaleInfo like this: LocaleInfo(); - default LocaleInfo('sr'); LocaleInfo({}); - default LocaleInfo({'languageID': 'sr', 'regionID': 'RU'); - and all variations (missing regionID, missing languageID).

  1. Language matching - how detailed should we go in specifying language matching process?

I assume each platform has its own way of finding the best match for a user provided language ID. Should we just leave it at that?

I would like to propose one requirement:

If user specifies "de-AT-u-co-phonebk" and language matcher produces "de-DE" as closest match, we should keep the extension and produce final "de-DE-u-co-phonebk".

  1. Derive methods were introduced to simplify creation of objects by tweaking current settings. It works well when you override existing setting or when you add new one. It's not so great when you want to remove a setting. For example:

var dtf = new DateTimeFormat({'skeleton': 'MMMy'});

// Skeleton has higher priority than timeType and dateType fields so we have to remove it in order for dateType override to have any effect. var derivedDtf = dtf.derive({'skeleton': undefined, 'dateType': 'short');

The example is somewhat forced since user would probably just create new DTF in this case instead of deriving, but I wanted to show the problem with reseting the setting.

The question is - do we keep derive methods? If so, do we care about this case/my solution (since user has other ways to create the object)?

  1. Should we rename LocaleInfo.collator()/numberFormat()/*dateTimeFormat *() into LocaleInfo.createCollator()...? It makes it clear we are creating new object.

  2. I think that calendar settings for DateTimeFormat doesn't bring much value and makes implementation more complex. Overriding default calendar is rare operation, and can be easily done with -u-ca extension - one just needs to create new locale.

  3. I would rename dateSkeleton into skeleton in DateTimeFormat settings.

# Allen Wirfs-Brock (14 years ago)

On May 18, 2011, at 2:04 PM, Nebojša Ćirić wrote:

Hi all, Jungshik and I were working on v8 implementation and we have almost everything done (number formatting is not done, and some parts of collation). There are some issues I would like to discuss (I made an update to the strawman):

  1. For LocaleInfo I would like to include construction with plain string: new LocaleInfo("de") vs more verbose new LocaleInfo({'languageID': 'de'})

One would be able to construct LocaleInfo like this: LocaleInfo(); - default LocaleInfo('sr'); LocaleInfo({}); - default LocaleInfo({'languageID': 'sr', 'regionID': 'RU'); - and all variations (missing regionID, missing languageID).

you don't need quotes around the property names in your literal objects: LocaleInfo({languageID: 'sr', regionID: 'RU');

You could support all of the above, it's just a matter of some internal logic. You really have tree alternative for the argument: none, a string an object that will be treated as a possibly empty set of option properties

  1. Language matching - how detailed should we go in specifying language matching process?

I assume each platform has its own way of finding the best match for a user provided language ID. Should we just leave it at that?

I would like to propose one requirement:

If user specifies "de-AT-u-co-phonebk" and language matcher produces "de-DE" as closest match, we should keep the extension and produce final "de-DE-u-co-phonebk".

Precise specification is best for interoperability. Specify as much as you possibly can.

  1. Derive methods were introduced to simplify creation of objects by tweaking current settings. It works well when you override existing setting or when you add new one. It's not so great when you want to remove a setting. For example:

var dtf = new DateTimeFormat({'skeleton': 'MMMy'});

// Skeleton has higher priority than timeType and dateType fields so we have to remove it in order for dateType override to have any effect. var derivedDtf = dtf.derive({'skeleton': undefined, 'dateType': 'short');

The example is somewhat forced since user would probably just create new DTF in this case instead of deriving, but I wanted to show the problem with reseting the setting.

Specify undefined as the value seems fine as long as undefined as a option property value always means the same as that property being absent. Alternatively, you could accept an optional second argument to derive that is an array of property names to delete. Does this really occur enough to bother with any of this. If this scenario is rare why not just not support this style of derivation. BTW, contrary to a literal interpretation of actual language you use above, I assume that new DTF(options) and dtf.derive(options) both create a new dtf object.

The question is - do we keep derive methods? If so, do we care about this case/my solution (since user has other ways to create the object)?

If the same thing can be accomplished other ways and it isn't a common usage case then I recommend dropping it.

  1. Should we rename LocaleInfo.collator()/numberFormat()/dateTimeFormat() into LocaleInfo.createCollator()...? It makes it clear we are creating new object.

The methods on the LocaleInfo constructor or are they really LocaleInfo.prototype methods. In other words are the results derived from a specific LocaleInfo instance or is the result some sort of global value that is independent of the instances. If the latter, why are they associated with the LocaleInfo constructor?

# Nebojša Ćirić (14 years ago)
  1. Should we rename LocaleInfo.collator()/numberFormat()/* dateTimeFormat*() into LocaleInfo.createCollator()...? It makes it clear we are creating new object.

The methods on the LocaleInfo constructor or are they really LocaleInfo.prototype methods. In other words are the results derived from a specific LocaleInfo instance or is the result some sort of global value that is independent of the instances. If the latter, why are they associated with the LocaleInfo constructor?

They are prototypes:

LocaleInfo.prototype.collator()

I propose

LocaleInfo.prototype.createCollator()

since they actually create a new LocaleInfo.Collator (or DateTimeFormat or NumberFormat) objects based on that locale info.

# Allen Wirfs-Brock (14 years ago)

On May 18, 2011, at 3:35 PM, Nebojša Ćirić wrote:

  1. Should we rename LocaleInfo.collator()/numberFormat()/dateTimeFormat() into LocaleInfo.createCollator()...? It makes it clear we are creating new object.

The methods on the LocaleInfo constructor or are they really LocaleInfo.prototype methods. In other words are the results derived from a specific LocaleInfo instance or is the result some sort of global value that is independent of the instances. If the latter, why are they associated with the LocaleInfo constructor?

They are prototypes:

LocaleInfo.prototype.collator()

I propose

LocaleInfo.prototype.createCollator()

since they actually create a new LocaleInfo.Collator (or DateTimeFormat or NumberFormat) objects based on that locale info.

It could go either way and there isn't a lot of relevant precedent in the ES built-in libraries to guide this. If it is essential an characteristic that a new object is being created I personally would be inclined to put something in the name to indicate that. Personally I might use newXXX rather than createXXX. The other possibility is that you could make them be constructor functions so you would say:

 var myDtf =  new myLocale.DateTimeFormat(myOptions);

Since any native ES function can be used as a constructor (and will even do the right thing if it is carefully code with that possibility in mind) I guess that might argue for the non-imperative naming, particularly if it starts with an upper case. Then either the above or

 var myDtf =  myLocale.DateTimeFormat(myOptions);

would be equivalent. I think they both read fine.

# John Tamplin (14 years ago)

On Wed, May 18, 2011 at 6:35 PM, Nebojša Ćirić <cira at google.com> wrote:

  1. Should we rename LocaleInfo.collator()/numberFormat()/*

dateTimeFormat*() into LocaleInfo.createCollator()...? It makes it clear we are creating new object.

The methods on the LocaleInfo constructor or are they really LocaleInfo.prototype methods. In other words are the results derived from a specific LocaleInfo instance or is the result some sort of global value that is independent of the instances. If the latter, why are they associated with the LocaleInfo constructor?

They are prototypes:

LocaleInfo.prototype.collator()

I propose

LocaleInfo.prototype.createCollator()

since they actually create a new LocaleInfo.Collator (or DateTimeFormat or NumberFormat) objects based on that locale info.

Are they required to create new instance, or can they return a cached object that uses the same parameters? If the latter, then I would suggest either foo() or getFoo() rather than createFoo().

# Nebojša Ćirić (14 years ago)

John, I think you are correct. We allow for cached items, so it wouldn't be "new" object we return in that case. I guess it's better to leave names as they are now.

Allan, this is how current proposal looks like (just parts relevant to DateTimeFormat):

LocaleInfo = function(settings|string) {...} LocaleInfo.prototype.dateTimeFormat(settings) LocaleInfo.DateTimeFormat = function(settings, locale) {...} LocaleInfo.DateTimeFormat.prototype.format(Date) ...

So one can do both:

var locale = new LocaleInfo(); var dtf = locale.dateTimeFormat({skeleton: 'MMMy'}); // shorthand for one below

or

var dtf = new LocaleInfo.DateTimeFormat({skeleton: 'MMMy'}, locale});

  1. мај 2011. 16.11, John Tamplin <jat at google.com> је написао/ла:
# Allen Wirfs-Brock (14 years ago)

On May 18, 2011, at 4:33 PM, Nebojša Ćirić wrote:

John, I think you are correct. We allow for cached items, so it wouldn't be "new" object we return in that case. I guess it's better to leave names as they are now.

The the cached and potentially sharable instance immutable?

Allan, this is how current proposal looks like (just parts relevant to DateTimeFormat):

LocaleInfo = function(settings|string) {...} LocaleInfo.prototype.dateTimeFormat(settings) LocaleInfo.DateTimeFormat = function(settings, locale) {...} LocaleInfo.DateTimeFormat.prototype.format(Date) ...

So one can do both:

var locale = new LocaleInfo(); var dtf = locale.dateTimeFormat({skeleton: 'MMMy'}); // shorthand for one below

or

var dtf = new LocaleInfo.DateTimeFormat({skeleton: 'MMMy'}, locale});

I think you should only support one or the other (and I would go with the version on the prototype). By having both you effective double the surface area of this part of the API., but for what benefit? It isn't clear that the connivence in a few situations justify the cost of developers having to learn a larger API set.

Particularly if the above example is typical, I don't see why anyone would ever code the second form. You still have to have locale available and the first form is shorter.

# Nebojša Ćirić (14 years ago)
  1. мај 2011. 16.48, Allen Wirfs-Brock <allen at wirfs-brock.com> је написао/ла:

On May 18, 2011, at 4:33 PM, Nebojša Ćirić wrote:

John, I think you are correct. We allow for cached items, so it wouldn't be "new" object we return in that case. I guess it's better to leave names as they are now.

The the cached and potentially sharable instance immutable?

I think that's what we agreed at the last meeting, to prevent potential problems.

Allan, this is how current proposal looks like (just parts relevant to DateTimeFormat):

LocaleInfo = function(settings|string) {...} LocaleInfo.prototype.dateTimeFormat(settings) LocaleInfo.DateTimeFormat = function(settings, locale) {...} LocaleInfo.DateTimeFormat.prototype.format(Date) ...

So one can do both:

var locale = new LocaleInfo(); var dtf = locale.dateTimeFormat({skeleton: 'MMMy'}); // shorthand for one below

or

var dtf = new LocaleInfo.DateTimeFormat({skeleton: 'MMMy'}, locale});

I think you should only support one or the other (and I would go with the version on the prototype). By having both you effective double the surface area of this part of the API., but for what benefit? It isn't clear that the connivence in a few situations justify the cost of developers having to learn a larger API set.

Particularly if the above example is typical, I don't see why anyone would ever code the second form. You still have to have locale available and the first form is shorter.

If I remove constructor LocaleInfo.DateTimeFormat where would I put LocaleInfo.DateTimeFormat.prototype.format() method for example (I would gladly remove the duplication, but I am not sure how)? Right now I do:

LocaleInfo.prototype.dateTimeFormat(settings) { return new LocaleInfo.DateTimeFormat(settings, this); }

and

LocaleInfo.DateTimeFormat = function(settings, locale) { }

does actual work of creating the new object.

# Gillam, Richard (14 years ago)

Nebojša--

My votes, for whatever they may be worth...

  1. For LocaleInfo I would like to include construction with plain string: new LocaleInfo("de") vs more verbose new LocaleInfo({'languageID': 'de'})

One would be able to construct LocaleInfo like this: LocaleInfo(); - default LocaleInfo('sr'); LocaleInfo({}); - default LocaleInfo({'languageID': 'sr', 'regionID': 'RU'); - and all variations (missing regionID, missing languageID).

That seems appropriate to me.

  1. Language matching - how detailed should we go in specifying language matching process?

I think you should strive to define this precisely, unless it's going to put an undue burden on some subset of implementers. But I think you want to find a precedent and stay as close to it as possible. You want to avoid reinventing wheels.

  1. Derive methods were introduced to simplify creation of objects by tweaking current settings. It works well when you override existing setting or when you add new one. It's not so great when you want to remove a setting. For example:

The question is - do we keep derive methods? If so, do we care about this case/my solution (since user has other ways to create the object)?

I'm going to have to take another look at the strawman (the current version you're working from is posted somewhere, right?), but I think there's some value here and that your suggestion of using "undefined" seems reasonable.

Just the same, it'd be better if the derive() method were smart enough to know that specifying "dateType" isn't going to do anything unless it also un-defines "skeleton". Otherwise, the developer has to remember that, or discover it the hard way.

  1. Should we rename LocaleInfo.collator()/numberFormat()/dateTimeFormat() into LocaleInfo.createCollator()...? It makes it clear we are creating new object.

Yes.

  1. I think that calendar settings for DateTimeFormat doesn't bring much value and makes implementation more complex. Overriding default calendar is rare operation, and can be easily done with -u-ca extension - one just needs to create new locale.

Probably better to just use the locale, unless that's going to cause an undue burden for implementers. (From my perspective, I'm trying to implement this on top of ICU, and would prefer whatever forces me to write the least amount of code above and beyond what ICU provides.)

  1. I would rename dateSkeleton into skeleton in DateTimeFormat settings.

I don't really care.

As I said, for whatever it's worth...

--Rich Gillam Lab126

# Allen Wirfs-Brock (14 years ago)

On May 18, 2011, at 5:06 PM, Nebojša Ćirić wrote:

If I remove constructor LocaleInfo.DateTimeFormat where would I put LocaleInfo.DateTimeFormat.prototype.format() method for example (I would gladly remove the duplication, but I am not sure how)? Right now I do:

LocaleInfo.prototype.dateTimeFormat(settings) { return new LocaleInfo.DateTimeFormat(settings, this); }

and

LocaleInfo.DateTimeFormat = function(settings, locale) { }

does actual work of creating the new object.

Clearly I'm being sloppy by having first not read your entire API; but of course actually developer also probably will jump in without reading everything so may it isn't such a bad approach....

I would expect that the LocaleInfo instance you obtained (I'll call it locale) has been instantiate for some specific "locale" (in quotes I mean the abstract concept of some specific locale). In other words locale encapsulates all the knowledge that is needed to perform operations in some specific "locale" context. Part of that encapsulated knowledge is what kind of DTF to use by default for that "locale" or how to selection from a set of possible DTFs based upon requested settings. In practice you might only have one "class" that implements DTF or you might have several but that really is an implementation that should be hidden inside locale.

I would then expect to see an implementation something like this:

LocaleInfo.prototype.dateTimeFormat(settings) { return new this.__internalDTFProvider(settings, this) }

Now that I have written this, I see the more concise answer:

The concrete "class" that implements a specific DTF should be a private implementation detail. It doesn't have to have a public constructor API because the constructor is called from within the dateTimeFormat method. The instances of such private DTF constructors support the public DTF instance API. In other words that have methods such as format. The actual implementation of format would hang-off of the prototype object associated with such private DTF constructors.

Here is a third way to state it:

LocaleInfo is a constructor that has a public API that is used to create (or access) LocaleInfo objects. localeInfo instances have a public API (essentially the public properties of LocaleInfo.prototype) that give access to various kinds of objects including objects that support the DTF interface DTF objects implement the DTF public API including format The constructor(s) for DTF objects is an implementation detail and doesn't need a public API

# Nebojša Ćirić (14 years ago)

One more question wrt. private implementation.

Is it enough if I name it say LocaleInfo.DateTimeFormat__ and not include it in the public API list in the proposal?

Looking around web I found ways to make things truly private, but there was price to pay (see stackoverflow.com/questions/55611/javascript-private-methods).

  1. мај 2011. 17.55, Allen Wirfs-Brock <allen at wirfs-brock.com> је написао/ла:
# Allen Wirfs-Brock (14 years ago)

On May 18, 2011, at 6:17 PM, Nebojša Ćirić wrote:

One more question wrt. private implementation.

Is it enough if I name it say LocaleInfo.DateTimeFormat__ and not include it in the public API list in the proposal?

You can probably do anything you want in your prototype as long as it is clear what is implementation detail and what is public API. Personally, I prefer to put marker characters such as __ at the front to make them more obvious. I think you could use any of the widely known techniques in your prototype and I don't think you will need to be particularly concerned about the costs.

# Rick Waldron (14 years ago)

With all due respect, as I'm quite new to the es-discuss list, but from a developer's perspective this proposal seems more like a host object API than a native object API.

I'm trying to broaden my understanding about the qualifications and life cycle of a proposal

Thanks in advance!

# Allen Wirfs-Brock (14 years ago)

On May 19, 2011, at 10:52 AM, Rick Waldron wrote:

With all due respect, as I'm quite new to the es-discuss list, but from a developer's perspective this proposal seems more like a host object API than a native object API.

It's neither. There is a specific TC39 sub-activity to define a set of I18N APIs that could be implemented as a "library" or module. Such a library might be implemented via host object or it might be implemented using native objects.

# Rick Waldron (14 years ago)

Thank you! That is precisely the information I had hoped to glean.