Post by David WilkinsonI have to say that this thread seems to be confusing outputting to a
console window with formatting strings.
True. Unicode and the Windows console is a tricky business.
And my post was more like a answer to "use wcout" (Vivek)
and "use CString::Format" (joe).
None of them is a solution, because the problem is the console, not the API.
As a side-note CString::Format does not even use Unicode, it is a generic
API.
Post by David WilkinsonWhat is better about any of these three as opposed to the others?
Personally, I see no reason to choose non-portable code in a pure
console application, so I would always go the iostream route. I do not
have the book "Exceptional C++ style"; what does it have to say here? I
can see that tostringstream might lose on efficiency (usually not an
issue), but why is it worse for internationalization?
I know how generic text/API works :-) (http://www.mihai-
nita.net/20050306b.shtml)
=======================================================
I will add CString::FormatMessage, and boost::format, then I will do the same
thing with all 5 APIs:
- load a string from resources (or message catalog, or whatever, I am using a
"generic" thing, GetTranslation)
- replace parameters
- display it
You tell me which gives the bigger mess.
The sting should have two parameters, something like this:
"You should click Ok to %s %d files"
and let's assume this needs to change into Yoda-speak to something like this:
"FILES %d TO %s CLICK OK YOU SHOULD"
And there are real languages that require such reordering.
=== iostream ===
string action = "delete";
int ncount = 12;
cout << "You should click Ok to " << action << " " << ncount << " files";
To localize:
IDS_CLICKOK = "You should click Ok to "
IDS_ACTION_DELETE = "delete"
IDS_ACTION_FILES = "files"
string s1, s2, s3, s4;
GetTranslation( s1, IDS_CLICKOK );
GetTranslation( s2, IDS_ACTION_DELETE );
GetTranslation( s3, IDS_ACTION_FILES );
cout << s1 << s2 << " " << %d << s3;
But in Japanese you should have no spaces, so this is wrong.
And in Yoda-speak the order changes completely, so this is wrong again.
And the translator should translate bits and pieces of a sentence, quite a
mess.
And how ugly and difficult to read if you need some special output (like
printing the number in hex, or controlling precision), because then you also
have to output control flags. Plus that the flags are not under the control
of the localizer (for instance currenty values need 2 decimals in most
locales, but 3 digits are used for most Arab countries).
=== printf ===
printf( "You should click Ok to %s %d files", action, ncount );
Much more readable.
To localize:
IDS_CLICKTOACT = "You should click Ok to %s %d files"
IDS_ACTION_DELETE = "delete"
string msg, action;
GetTranslation( msg, IDS_CLICKTOACT );
GetTranslation( action, IDS_ACTION_DELETE );
printf( msg, action, ncount );
In Japanese you should have no spaces, and the translator can remove them.
In Yoda-speak the order changes completely, so this is still wrong.
And the translator should translate almost full sentence, good.
And is easy read and control the format because the control flags are part of
the string.
=== CString::Format ===
It is pretty much like printf, just loading the localized string is a bit
easier:
CString msg;
CString action( IDS_ACTION_DELETE );
msg.Format( IDS_CLICKTOACT, action, ncount );
Same drawbacks otherwise.
=== CString::FormatMessage ===
Ideal!
To localize:
IDS_CLICKTOACT = "You should click Ok to %1!s! %2!d! files"
IDS_ACTION_DELETE = "delete"
CString msg;
CString action( IDS_ACTION_DELETE );
msg.FormatMessage( IDS_CLICKTOACT, action, ncount );
In Japanese you should have no spaces, and the translator can remove them.
And the translator should translate almost full sentence, good.
And is easy read and control the format because the control flags are part of
the string.
But this solves the Yoda-speak:
IDS_CLICKTOACT = "FILES %2!d! TO %1!s! CLICK OK YOU SHOULD"
IDS_ACTION_DELETE = "DELETE"
This works with the same code as above. Nice!
=== boost::format ===
Second best after CString::FormatMessage, especially if you have to be cross-
platform (and if you don't care about the funny % between parameters :-)
IDS_CLICKTOACT = "FILES %2!d! TO %1!s! CLICK OK YOU SHOULD"
IDS_ACTION_DELETE = "DELETE"
string msg, action;
GetTranslation( msg, IDS_CLICKTOACT );
GetTranslation( action, IDS_ACTION_DELETE );
cout << boost::format( msg % action % ncount );
In Japanese you should have no spaces, and the translator can remove them.
In Yoda-speak the order changes completely, possible, so is ok.
And the translator should translate almost full sentence, good.
And is easy read, but not to control the format, because the control flags
are stil hard-coded.
=======================================================
Sutter compares sprintf, snprintf, stringstream, strstream,
boost::lexical_cast (he does not care about platform api :-)
No clue why he ignores boost::format.
His analisys takes 16 pages, in two parts, so you can imagine it is quite
serious. He looks at ease of use, code clarity, standard or not (C90, C99,
C++03, C++0x), efficient, length-safe, type-safe, usable with templates.
And he does not even look at internationalization!
His conclusion:
- to convert a value a to a string => boost::lexical_cast
- simple formatting => stringstream, strstream
"the code will be more verbose and harder to grasp"
- for more complex formatting => snprintf
- never sprintf
=======================================================
Post by David WilkinsonReferring to your other
post, what does backward compatibility have to do with this?
The console is used to emulate the behavior of the old DOS prompt.
And that was not Unicode, and used the OEM code page.
This is why the actual console uses OEM code page as default, even in the
latest Windows versions.
And since I don't work at MS, I am not sure what is the real reason for
proper Unicode support not being added to the console: "backward
compatibility" and "who cares about console anymore".
--
Mihai Nita [Microsoft MVP, Windows - SDK]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email