Koka / gettext-rs

GNU Gettext FFI binding for Rust
51 stars 25 forks source link

Provide macros which perform formatting after calling `gettext` #86

Open Minoru opened 2 years ago

Minoru commented 2 years ago

We have macros that do something like format!(gettext(…), …), but at runtime (format! expects a string literal, so we can't use that). Our macros supported {} and {n}, and there was a PR to add escaping ({{ and }}). That PR uncovered a problem: if the format string is invalid, what should our macros return? How should invalid formats be handled (see #85)?

To find out what user's expectations are, I looked at how people use gettext-rs functions:

I also looked at how others solve this problem:

So it looks like the expected solution would be returning Result. Some ideas from @delight-aug:

From the perspective of the user of macros, with Result, I could call auto-bug-reporting and display a message to users, that something went wrong. Or I could just unwrap it all the time and let my program crash because of some string. Also, it doesn't look good when you want to include a phrase in GtkLabel, but you need to match it first (or unwrap it in place). Maybe a good decision is to let users specify an action to run on a broken string? So they set up a closure near textdomain bindings. And by default just swallow things...

This, plus the lack of clear winner among the "runtime formattting" crates, makes me think we should postpone this work, and gather more ideas of what users actually need.

If you'd use a macro that does something like format!(gettext(…), …), please comment below and explain:

mks-h commented 2 years ago

I think the first thing to do if a translated string has broken formatting is to use the English (default) string. But what to do if its formating is broken, too?

zecakeh commented 2 years ago

I'm working on Fractal, a GTK app.

From my point of view, a sane fallback using the default string seems like a good default, although providing a custom closure would be a more generic solution.

But what to do if its formating is broken, too?

The macro should probably check that the formatting of the string is good and fail cargo check if it doesn't.

From a translator point of view, I believe that named format specifiers are a must have. A specifier with a good name is easier to understand. Also, the translator might need to change the variables' order and it's not doable without it. I do not think that complex specifiers are a must-have.

We are rewriting our app from scratch because of several big changes we needed to make. Our legacy code uses custom i18n methods based on an old version of this crate, to allow us to use named variables. They might be of help. Of course the syntax is not as friendly as a macro would be, and we'd like to avoid having a custom implementation.

mks-h commented 2 years ago

The macro should probably check that the formatting of the string is good and fail cargo check if it doesn't.

That's a good idea we missed. This crate is just a bindings library for gettext, so I thought we had to format the String that actual gettext returns at runtime. But I think, idealistically, nothing stops us from using the str provided to macro at compile-time to verify that the default string isn't broken (to at least have a working fallback string).

That being said, I'm struggling to think about how to implement it better. Just in case, Is there anything that allows writing custom compile-time checks for macros?

and we'd like to avoid having a custom implementation

And I'll be delighted to be the one to write the shared implementation!

zecakeh commented 2 years ago

That being said, I'm struggling to think about how to implement it better. Just in case, Is there anything that allows writing custom compile-time checks for macros?

With procedural macros, if it panics, it gives a compile-time error. So you can just use panic!("Error message") for custom errors.

mks-h commented 2 years ago

I have looked at procedural macros, and it seems absolutely possible to create the type of macros you suggested. I'll start working on it after 9th December, the deadline for my coursework. However, I can't say whether it will be implemented in this crate — that's up for @Minoru to decide.