> On 28 Mar 2018, at 13:12, Christophe Fergeau <cferg...@redhat.com> wrote:
> 
>> Do you have something against requiring string constants in English for our 
>> error messages?
>> 
>> Is it so difficult to understand that if I have:
>> 
>>      WriteError(“can’t write”, “header”, filename)
>>      WriteError(“can’t write”, “boson”, filename)
>> 
>> I can localize all the combinations “can’t write header”, “can’t write 
>> boson” and ignore the filename in the localization process, whereas as soon 
>> as I have:
> 
> You apparently suggest translating "can't write",
> "boson" and "header" separately, and to concatenate these translated
> bits together to get a translated "can't write boson" or "can't write
> header" translated string. I'm afraid this does not work like this, you
> need the full string to do the translation "can't write boson", "can't
> write header", and translate each separately.

Funny, I vaguely remember we had this discussion very recently. But not on 
list. So here we go again…

Consider a FileWriteError. In my proposal, the actual prototype has a C 
constant message, an C constant operation, and a variable part related to 
errno. For the sake of example, let me add an hypothetical filename. So let’s 
say we have:

- “write failed” and “write interrupted” as possible values for message
- “format”, “frame” and “header” as possible values for operation
- anything as errno (dynamic)
- anything as filename (dynamic)

At the moment, all filenames we get have the same lifetime as the program (e.g. 
they come from argv[n] or compile-time constants. But let’s imagine, for the 
sake of clarifying the previous discussion, that the filename can actually be 
constructed dynamically for example by concatenating a directory and some user 
input. In that case, the proper interface for FileWriteError would be:

        FileWriteError(const char *message, const char *operation, int errno, 
std::string filename);

Yes, you read that right. There would be an std::string here for the dynamic 
filename. Because that part is dynamic, and its lifetime cannot be managed 
statically, so there is a good reason to pay the price.

The complete English message we might want can be something like : “write 
failed writing header for file ‘Hello.txt’: no space left on device (28)”.

In my proposal, that message is built in format_message with something like:

        int written = snprintf(buffer, size, "%s writing %s for file ‘%s'", 
what(), operation, filename.c_str());
        return append_strerror(buffer, size, written); 

A first rough translation in French would translate pieces individually. That’s 
what Christophe says I’m suggesting. I’m not, but for the sake of the argument, 
let’s explore that. So we would have:

- “l’écriture a échoué” et “l’écriture a été interrompue” for individually 
translated messages
- “le format”, “une image” et “l’en tête” for individually translated operations
- “%s pendant %s pour le fichier ‘%s’” as a translation for the format string.

The translation could be achieved using:
        int written = snprintf(buffer, size, _("%s writing %s for file ‘%s’”), 
_(what()), _(operation), filename.c_str());
        return append_strerror(buffer, size, written); 

This naive approach yields “l’écriture a été interrompue pendant le format pour 
le fichier ‘Hello.txt'”, which is understandable, but sounds a bit strange. It 
would be better to instead generate “L’écriture du format a été interrompue 
pour le fichier ‘Hello.txt’”; which involves word reordering and grammatical 
pairing (e.g. contractions, male/female, etc). In order to do that, you can 
take a more subtle approach where instead of translating pieces individually, 
you translate the complete message, but still carefully ignore the parts that 
you know are variable. So you could do instead:

        snprintf(translated, sizeof(translated), “%s writing %s for file 
‘%%s’”, what(), operation);
        int written = snprintf(buffer, size, _(translated), filename.c_str());
        return append_strerror(buffer, size, written); 

In that case, obviously, you need to provide more translations:
- “write failed writing header for file ‘%s'” -> “l’écriture de l’en-tête du 
fichier ‘%s’ a échoué"
- “write failed writing frame for file ‘%s'” -> “l’écriture d’une image du 
fichier ‘%s' a échoué"
- “write interrupted writing frame for file ‘%s'” -> “l’écriture d’une image du 
fichier ‘%s’ a été interrompue”
… etc

What matters, however, is that you still have a finite number of combinations, 
because you have a finite number of input strings. And that guarantee is only 
valid as long as you can tell which parts are constant and which parts are 
variable.

What matters even more is that if in the interface of my Error-derived classes, 
I have not carefully separated the constant and the variable parts, I cannot 
make an exhaustive list of all possible translations. Therefore, it is 
important for the interface of Error to “strongly suggest” constant strings 
where I want constant strings. By the mean of a “const char *”. Where I expect 
a dynamic string, I use std::string (or const std::string & or whatever floats 
you boat).


> 
>>      WriteError(“can’t write header” + filename)
>> 
>> it suddenly becomes impossible to do localization?
> 
> so WriteError("can't write header" + filename) is actually *better* from
> a translation point of view, as you'd mark "can't write header" for
> translation and be done with it.

This would not work in German, where you’d want to have the file name somewhere 
in the middle, as in “Beim Schreiben der Kopfzeile für die Datei ‘Hello.txt' 
ist ein Fehler aufgetreten”…
        
Also, in our code, “header” is actually coming from a different part, which was 
the reason for introducing the “operation” argument. The simplification you are 
talking about really comes from having ignored that detail in my example. If 
you don’t ignore it, then the alternative is:

        WriteError( “can’t write “ + operation + “ for “ + filename);

and your “better” argument, which was already slashed in microscopic fragments 
by the German counter example, becomes all the more difficult to reassemble.


https://www.smashingmagazine.com/2012/07/12-commandments-software-localization/ 
#2: “Never concatenate strings”


Regards
Christophe Christophe Christophe Christophe (since I have to repeat myself 
quite a lot, I thought I’d do it in the signature too ;-)

> 
> Christophe

_______________________________________________
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/spice-devel

Reply via email to