macro_prototype icon indicating copy to clipboard operation
macro_prototype copied to clipboard

How would I do Intl.message in this system?

Open alanknight-wk opened this issue 4 years ago • 3 comments

One of the long-standing irritations in Intl is the redundant information that has to be passed. e.g. a simple call ooks like

String foo(String a, String b) => Intl.message('text with $a and $b', desc: 'arbitrary example', name: 'foo', args: [a, b], examples: const {'a': 'thing one', 'b': 'thing two'});

This is processed in two phases.

  • There's a build(-ish) time extraction that parses the text and extracts the information for translators. The description and examples are only used at this time, and even the main text is only used at this time for production purposes. In development it's also used before any translations are available.
  • At runtime the name is required as a lookup key for the translation text to use, and the args are required so that they can be interpolated into the translated text. This information is already available in the function definition, but not at run-time.

With transformers, developers could omit name and args and have them filled in automatically. It would be nice to be able to do the same with macros. So it seems like

  • An additional annotation for each message? Or is it possible to run a class-level or library level macro that would find all the messages within it.
  • More significantly, it's not clear to me how I could provide additional arguments into a method body. I haven't tried to implement it, just looked over the documentation, but I don't see a mechanism, and I don't see a mechanical rewrite that would fix it. About the only thing I can see is if you rewrote the whole statement into an annotation (can these anootations have parameters or a declaration that would be disregarded, e.g. @intlMessage(['a', 'b'], r'text with $a and $b', 'arbitrary example', {'a': 'thing one', 'b': thing two'} or
   @intlMessage
   String _payNoAttentionTofoo(String a, String b) => Intl.message('text with $a and $b', desc: 'arbitrary example', examples: const {'a': 'thing one', 'b': 'thing two'});

which would then produce foo. Neither of these seems great.

alanknight-wk avatar Jul 19 '21 23:07 alanknight-wk

I think the way you would need to do this in the current system is with the annotation, yes. I would opt for a variant of the first solution probably, something like this:

@IntlMessage(r'text with $a and $b', 'arbitrary example', {'a': 'thing one', 'b': 'thing two'})
external String foo(String a, String b);

We are considering allowing statement level macros also, which would be a bit nicer, possibly allowing something like this:

String foo(String a, String b) =>
  intlMessage('text with $a and $b', 'arbitrary example', {'a': 'thing one', 'b': thing two'});

The advantage would be allowing you to use the actual string interpolation in the user code instead of a weird raw string.

This intlMessage thing looks like a function but would actually be a macro, and it would be able to effectively replace itself with any expression it likes, and its arguments would be provided as code blocks, which it could effectively splat into that resulting expression however it chooses. It would also be to introspect on the parent scope (function or method in this case) in order to generate the code (in this case the name and args).

jakemac53 avatar Jul 21 '21 16:07 jakemac53

Statement level macros would fix a lot of the limitations I see with the current approach. Glad it's being considered!

TimWhiting avatar Jul 21 '21 16:07 TimWhiting

Yes, the statement level macros does sound very promising. And more like what "macro" means in other systems.

alanknight-wk avatar Jul 21 '21 18:07 alanknight-wk