-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Please remove onError parameter from format/formatToParts #58
Comments
Currently, it is used in HandleMessageFormatError(ctx, error). in 1.5.5.1 ResolveFunction ( ctx, source, resArg, annotation ) 1.5.6 FormatMarkupPart ( ctx, markup ) 1.5.10 ResolveValue ( ctx, value ) 1.3.3 Intl.MessageFormat.prototype.format ( [ values [ , onError ] ] ) 1.3.4 Intl.MessageFormat.prototype.formatToParts ( [ values [ , onError ] ] ) 1.5.4.1 SelectPattern ( ctx ) Why don't we just throw some kind of Error in these cases instead? |
The immediate reason for the
At a slightly deeper level, the reason for this approach is to ensure that localization will always provide some result to a user, rather than just failing. MessageFormat is different from the other Intl formatters in that it relies not only on testable CLDR data, but also on user-provided data, i.e. the message. For locales beyond the source locale used by the original developer, that data is the result of translation work, and translation may introduce bugs that can cause a runtime error. Sure, validation can catch many such bugs, but no system is perfect. In other words, an error thrown by e.g. NumberFormat or DateTimeFormat is actionable by a developer, and rarely seen in production use, but an error thrown by MessageFormat is often not visible to a developer or even caught by CI systems, and much more likely to end up happening at production runtime when it's not actionable. Throwing from MessageFormat would effectively introduce an expectation that all of its users have a full test suite that they run for all locales when any of its contents change. I do not think this is a reasonable expectation, as I'm not sure that anything like that is currently done by anyone. |
I do not understand about this, you argue MessageFormat is different because " is often not visible to a developer or even caught by CI systems, and much more likely to end up happening at production runtime when it's not actionable." If that is truely not actionable, then why do you need an onError parameter at all? In that case, the developer cannot take any action anyway so the onError could only be a noop, right? Currently the HandleMessageFormatError will pass completionRecord.[[Value]] to onError if one is provided. So from my understand, there are two kind of conditions Case A: The error is not actionable Therefore, for Case A, the completionRecord.[[Value]] is not needed to pass to onError and an onError is not need to be registered, because it is not actionable even if the onError receive the completionRecord.[[Value]] and can do nothing (since it is not actionable, mean no action can be taken to make thing better) Now, for Case B, I think that is the part that the onError and completionRecord.[[Value]] is useful, right? so in that case, your current design requires
Why it cannot be
I understand, sometime MessageFormat.format() may encounter error internally and need to still return a string but with informaiton about what were fallbacked during the format() operation. I wonder, could that be done via
@sffc |
I don't have an official position yet, but it seems that |
Replying to @FrankYFTang:
Continuing a bit from my previous comment, the two main use cases that the
So while a thrown error isn't necessarily actionable to the user, reporting it to a developer could make it actionable by allowing them to fix it later.
In case it's not clear in the spec, in all the cases where So
In theory, that approach could work. An additional method I don't think this would be better or clearer for users.
Note that additional information like the message source data or an identifier for the message would also probably need to be included when reporting/logging errors. Replying to @sffc
In theory, this approach could also work. We might need two different errors, to cover errors encountered during both |
This topic was also covered during the 2023-10-25 incubator call where @sffc also participated, and I recall @ljharb having some thoughts on attaching user-defined data to errors, though that may have been limited to values that are later read by spec internals. |
Given the context provided by @eemeli above, I too strongly feel that the If we go the "always throw" route then the obvious downside in my opinion would be that due to the nature of MessageFormat as @eemeli mentioned, a developer would practically speaking be forced to wrap all usage of If we go the "never throw" route it will be really difficult for developers to be able to debug or report errors. Furthermore it'll be a waste of effort since the parser and formatter already knew about the errors which instead of getting reported got discarded and then the developer had to put in substantial effort to go through the result and find those out again, at times losing context that would be vital to be able to debug it. Because of both of these outcomes, I don't understand the benefit of trying to create a dichotomy here, since we need to come to a single solution that caters to the most common use-cases. This is addressed really well by the |
What are the pros/cons of an alternative where instead of returning a bare string, we always return an object with the bare string as a "value" property (or something), and then that object gives us a place to put an |
The main cost is that we would need to create that object for every formatting call, and that the result of a So it would add a cost to every successful formatting call in order to deal with exceptional cases, and make this API different from all the other ones. To some extent it'd be a bit of a return to the kind of API this proposal had before #22, where the "formatted" output still needs a bit of processing to be used, albeit of course less than with the |
ICU APIs have been moving in the direction of returning an upgraded type from .format that can be converted to a string but also has extra metadata. We use .formatToString when the function directly returns a string. I'd be okay with .format returning an upgraded type, maybe even one with a valueOf that returns a string, or introducing a new function like formatFull that returns the upgraded object. |
TG2 discussion: https://github.com/tc39/ecma402/blob/main/meetings/notes-2024-05-23.md#please-remove-onerror-parameter-from-formatformattoparts We want to seek feedback from TG1 about error handling code style. |
I added this to the agenda: tc39/agendas#1608 Some additional context: this had previously been discussed in part in an Incubator Call, but that call saw fairly low attendance and there are clearly more questions that need to be answered. I'll put together some slides based on the options outlined in last week's TG2 discussion and share them ahead of plenary. |
We used some i18n APIs and are hoping to move over to messageFormat when its ready. So we've always favoured fallback text showing with a mechanism to report issues to be resolved later on. For us the
This would work but feels like it would lead to more boilerplate per mf.format() call than one would like. We would still need a try/catch, then in the catch There's some interesting ideas around objects with the fallbacks as keys, but returning the message string is also more consistent with |
Options from this thread: (1) Some additional options that were suggested at TG1 plenary: (5) Configure error handling behavior in the constructor (@patrick-soquet) Conclusions from plenary:
|
To expand on the suggestion. Inspired by Acorn's const errors = [];
const myString = format(data, {
errors
});
if (errors.length) {
// ...
} When an array literal is passed we can be sure that there is no re-entrancy. Making control flow easy to see. If no array is passed then The return value is "simply" a string |
Thought on option 6: If the errors get added during formatting, it would involve calling const outputMetadata = {};
const myString = format(data, outputMetadata);
if (outputMetadata.errors.length) { // or `.errors?.length` ?
// ...
} |
We should also discuss exactly how the property gets set. I realized that if |
Yeah, I don't think it would actually call There could be re-entrance if the array is a proxy or has setters but if the code is passing an array literal then it can be sure there is no userland code that will be triggered. So it's analysable at the call site. |
Currently, the format/formatToParts function
1.3.3 Intl.MessageFormat.prototype.format ( [ values [ , onError ] ] )
and
1.3.4 Intl.MessageFormat.prototype.formatToParts ( [ values [ , onError ] ] )
has a new onError parameters and introduce a new requirement for format function in ECMA402.
https://tc39.es/proposal-intl-messageformat/#sec-intl.messageformat.prototype.format
I am very concern about this and I do not think this functionality is useful. Could we remove this functionality from this proposal and just let the format/formatToParts throw ?
How would this functionality be useful in real life programming? How would that user code looks like? Why this functionality (handling the missing data) cannot be done in the user land?
The text was updated successfully, but these errors were encountered: