Discussion:
I need to LoadString() programmatically based on ID not Value
(too old to reply)
Cadwell
2009-06-23 22:08:01 UTC
Permalink
I am localizing a application that has over 40 projects.

Currently in our application we have some global strings that are accessed
by all the projects. To facilitate localization I need to transfer these
strings into a .rc file.

The problem is that I don't want to have copies of these strings in each
projects .rc file. I would like to place the strings in one .rc file and have
them accessible to the other projects. To accomplish this I have created a
singleton COM object that retrieves strings from the rc file. The other
projects have access to this COM object and that is how the global strings
are shared.

Well that is what I would like to do. Short of passing in the actual string
nIDs to the COM object I can't figure out how to get the strings out of the
.rc file. Ideally I would like the client call to look like this.

CString sTranslated =
pIGlobalString->GetString("IDS_SOME_STRING");

So that means that pIGlobalString::GetString needs to convert a BSTR with
the resource ID to the resource VALUE for LoadString().

I am open to suggestions or alternate ideas. It seems like this might be
possible using FindResource and EnumResourceNames, but I am not sure how to
go about using those as they are fairly cryptic.
David Lowndes
2009-06-23 22:31:34 UTC
Permalink
Post by Cadwell
I am open to suggestions or alternate ideas.
Just build a resource only DLL with the strings in it and then you can
use LoadLibrary to get the instance handle of the DLL and use
CString::LoadString.

Dave
Cadwell
2009-06-23 22:43:01 UTC
Permalink
So the clients will be calling LoadLibrary, CString::LoadString? How will the
IDS_STRING_NAME be resolved?
Post by David Lowndes
Post by Cadwell
I am open to suggestions or alternate ideas.
Just build a resource only DLL with the strings in it and then you can
use LoadLibrary to get the instance handle of the DLL and use
CString::LoadString.
Dave
David Lowndes
2009-06-23 23:37:45 UTC
Permalink
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!

Dave
Tom Serface
2009-06-24 04:48:51 UTC
Permalink
One handy trick is to reserve a range of numbers to use in the string
resource to have sequential errors then do something like:

CString cs;
cs.LoadString(IDS_FIRSTERROR+nError);

To get the appropriate string.

Tom
Post by David Lowndes
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!
Dave
Cadwell
2009-06-24 14:40:01 UTC
Permalink
I didn't want to do that for code readability reasons. I think I am either
going to have provide a translation table or bypass the resource file
altogether and use a .xml file. I think our translation tool (LingoBit) will
work on a xml file as well as a resource.
Post by David Lowndes
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!
Dave
Tom Serface
2009-06-24 18:07:54 UTC
Permalink
I often wish MFC had the option to use RESX files like .NET has. They are
much easier to work with for strings and simple resources.

The option of having separate files (XML or whatever) is possible, but it
moves outside of the MFC paradigm and just becomes another thing not managed
by Visual Studio. You can have multiple string tables or even external .RC
files that are included based on some set of defines so you may want to
continue down that path first.

Tom
Post by Cadwell
I didn't want to do that for code readability reasons. I think I am either
going to have provide a translation table or bypass the resource file
altogether and use a .xml file. I think our translation tool (LingoBit) will
work on a xml file as well as a resource.
Post by David Lowndes
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!
Dave
Cadwell
2009-06-25 15:21:01 UTC
Permalink
I came up with a "hybrid" solution.

I have a enumeration defined in the .idl that looks something like this...

#include "resource.h"
typedef enum
{
GLOBAL_STRING_SOMETHING = IDS_STRING_SOMETHING,
}GlobalStingEnum;

Then the COM object method signature to get the strings looks like this
STDMETHODIMP CGlobalString::GetRcString(GlobalStringEnum gs, BSTR *pbstrValue)

The IDS_STRING_SOMETHING is added to the String Table using the resource
editor. So I do have a mapping function, but it is referencing the string IDs
and not the Values. I was surprised to see that even though I am including
"resource.h" in the idl file the IDS defines are not being exported only the
GLOBAL_STRING enums are. This means that the clients only know about the
GLOBAL_STRING enums which is what I was looking for. It is a bit if a pain to
keep the enumeration in sync with the string table, but it is doable.
Post by Tom Serface
I often wish MFC had the option to use RESX files like .NET has. They are
much easier to work with for strings and simple resources.
The option of having separate files (XML or whatever) is possible, but it
moves outside of the MFC paradigm and just becomes another thing not managed
by Visual Studio. You can have multiple string tables or even external .RC
files that are included based on some set of defines so you may want to
continue down that path first.
Tom
Post by Cadwell
I didn't want to do that for code readability reasons. I think I am either
going to have provide a translation table or bypass the resource file
altogether and use a .xml file. I think our translation tool (LingoBit) will
work on a xml file as well as a resource.
Post by David Lowndes
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!
Dave
Joseph M. Newcomer
2009-06-27 19:29:04 UTC
Permalink
And why can't the XML file be put into the resource segment?
joe
Post by Tom Serface
I often wish MFC had the option to use RESX files like .NET has. They are
much easier to work with for strings and simple resources.
The option of having separate files (XML or whatever) is possible, but it
moves outside of the MFC paradigm and just becomes another thing not managed
by Visual Studio. You can have multiple string tables or even external .RC
files that are included based on some set of defines so you may want to
continue down that path first.
Tom
Post by Cadwell
I didn't want to do that for code readability reasons. I think I am either
going to have provide a translation table or bypass the resource file
altogether and use a .xml file. I think our translation tool (LingoBit) will
work on a xml file as well as a resource.
Post by David Lowndes
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!
Dave
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer
2009-06-27 19:28:22 UTC
Permalink
Note that if you LoadLibrary, you can use the form of CString::LoadString that takes the
DLL instance handle as one of the arguments (I forget the order).

There is no particular reason you would need other than the string ID. The resource.h
file that you use to create the library would be imported by the clients of your DLL. Note
that it does *not* have to be called "resource.h", for example, it could be called
"stringlibrary.h" if you want.

Consider also using a .mc file and the mc compiler to create FormatMessage-compatible
strings, much better for localization to languages, and one of the outputs of the .mc
compiler is a .h file that gives all the names.
joe
Post by David Lowndes
Post by Cadwell
So the clients will be calling LoadLibrary, CString::LoadString?
Yes
Post by Cadwell
How will the
IDS_STRING_NAME be resolved?
If you want to convert a text string into a number you'd have to
provide your own conversion table - why not just use the ID number,
it'll be faster!
Dave
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Drew
2009-06-23 22:31:36 UTC
Permalink
Why not put the common string resources into an .rc2 and include that in
each projects .rc file?

Drew
Post by Cadwell
I am localizing a application that has over 40 projects.
Currently in our application we have some global strings that are accessed
by all the projects. To facilitate localization I need to transfer these
strings into a .rc file.
The problem is that I don't want to have copies of these strings in each
projects .rc file. I would like to place the strings in one .rc file and have
them accessible to the other projects. To accomplish this I have created a
singleton COM object that retrieves strings from the rc file. The other
projects have access to this COM object and that is how the global strings
are shared.
Well that is what I would like to do. Short of passing in the actual string
nIDs to the COM object I can't figure out how to get the strings out of the
.rc file. Ideally I would like the client call to look like this.
CString sTranslated =
pIGlobalString->GetString("IDS_SOME_STRING");
So that means that pIGlobalString::GetString needs to convert a BSTR with
the resource ID to the resource VALUE for LoadString().
I am open to suggestions or alternate ideas. It seems like this might be
possible using FindResource and EnumResourceNames, but I am not sure how to
go about using those as they are fairly cryptic.
Cadwell
2009-06-23 22:40:01 UTC
Permalink
Then the strings will essentially be cloned into every project. This will
require redundant translation.
Post by Drew
Why not put the common string resources into an .rc2 and include that in
each projects .rc file?
Drew
David Ching
2009-06-23 23:14:08 UTC
Permalink
Post by Cadwell
I am localizing a application that has over 40 projects.
Currently in our application we have some global strings that are accessed
by all the projects. To facilitate localization I need to transfer these
strings into a .rc file.
The problem is that I don't want to have copies of these strings in each
projects .rc file. I would like to place the strings in one .rc file and have
them accessible to the other projects. To accomplish this I have created a
singleton COM object that retrieves strings from the rc file. The other
projects have access to this COM object and that is how the global strings
are shared.
Well that is what I would like to do. Short of passing in the actual string
nIDs to the COM object I can't figure out how to get the strings out of the
.rc file. Ideally I would like the client call to look like this.
CString sTranslated =
pIGlobalString->GetString("IDS_SOME_STRING");
So that means that pIGlobalString::GetString needs to convert a BSTR with
the resource ID to the resource VALUE for LoadString().
I am open to suggestions or alternate ideas. It seems like this might be
possible using FindResource and EnumResourceNames, but I am not sure how to
go about using those as they are fairly cryptic.
I implemented a system like this which took a string instead of a UINT to
lookup the string resource, but I did not use LoadString at all, I placed
the strings in an XML file, e.g.


<xml>
<strings>
<string id="IDS_SOME_STRING">Some string</string>
</strings>
</xml>

Then used readily available XML parser to lookup the requested
IDS_SOME_STRING.


-- David
Cadwell
2009-06-24 14:41:01 UTC
Permalink
Thank you David, that might work. I will have to see how well our translation
tool (LingoBit) deals with .xml files.
Post by David Ching
I implemented a system like this which took a string instead of a UINT to
lookup the string resource, but I did not use LoadString at all, I placed
the strings in an XML file, e.g.
<xml>
<strings>
<string id="IDS_SOME_STRING">Some string</string>
</strings>
</xml>
Then used readily available XML parser to lookup the requested
IDS_SOME_STRING.
-- David
Goran
2009-06-24 06:53:24 UTC
Permalink
Well, using a COM object to me certainly looks like a major overkill.
What's wrong with just building a resource DLL and using that?
(LoadLibrary, temp AfxSetResourceHandle, LoadString)

E.g.

CString GetSharedString(UINT nIDS)
{
HMODULE hOldResourceHandle = AfxGetResourceHandle();
ON_BLOCK_EXIT(AfxSetResourceHandle, hOldResourceHandle);
AfxSetResourceHandle
(GetLangDllHModuleSomehowProbablyByUsingLoadLibrary());
CString Result;
if (!Result.LoadString(nIDS))
{
ASSERT(FALSE);
return _T("STUPID PROGRAMMER PASSED A BAD nIDS");
// Or something more appropriate, e.g. "???"
}
return Result;
// Original AfxResourceHandle restored by the mighty ScopeGuard
here.
}

This more or less requires that shared resource DLL and any project
that uses it share ResourceDLLDefines.h, and that file contains IDS-s
of strings in the *.dll. Easy-peasy, no?

Goran.
Joseph M. Newcomer
2009-06-27 19:30:15 UTC
Permalink
You don't even need the AfxSetResourceHandle; CString::LoadString now has a form that
takes the module handle of a resource DLL!
joe
Post by Goran
Well, using a COM object to me certainly looks like a major overkill.
What's wrong with just building a resource DLL and using that?
(LoadLibrary, temp AfxSetResourceHandle, LoadString)
E.g.
CString GetSharedString(UINT nIDS)
{
HMODULE hOldResourceHandle = AfxGetResourceHandle();
ON_BLOCK_EXIT(AfxSetResourceHandle, hOldResourceHandle);
AfxSetResourceHandle
(GetLangDllHModuleSomehowProbablyByUsingLoadLibrary());
CString Result;
if (!Result.LoadString(nIDS))
{
ASSERT(FALSE);
return _T("STUPID PROGRAMMER PASSED A BAD nIDS");
// Or something more appropriate, e.g. "???"
}
return Result;
// Original AfxResourceHandle restored by the mighty ScopeGuard
here.
}
This more or less requires that shared resource DLL and any project
that uses it share ResourceDLLDefines.h, and that file contains IDS-s
of strings in the *.dll. Easy-peasy, no?
Goran.
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Mihai N.
2009-06-26 08:08:07 UTC
Permalink
Post by Cadwell
I am open to suggestions or alternate ideas. It seems like this might be
possible using FindResource and EnumResourceNames, but I am not sure how to
go about using those as they are fairly cryptic.
In my experience a numeric string ID with resource-only DLL works best.
You don't have any conflicts
- because the numeric ID is really the resource-dll-handle + ID
- you can aboid naming conflicts by addopting a naming convention

So SHARED_IDS_PRINT is the stuff un the resource dll, IDS_PRINT can be your
local stuff. (or G_ or GLOBAL_ or whatever you want)

You can just provide the dll and a header file.

String IDs are slower, and cannot be checked a compile time.
With numeric IDs, if I spell a string wrongly in code I get a compile time
error. With strings everything compiles well, and explodes 5 months later.
Same for XMLs and any other string-id based idea.
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
David Lowndes
2009-06-26 10:29:55 UTC
Permalink
Post by Mihai N.
String IDs are slower, and cannot be checked a compile time.
With numeric IDs, if I spell a string wrongly in code I get a compile time
error. With strings everything compiles well, and explodes 5 months later.
Same for XMLs and any other string-id based idea.
It's always good to hear that someone else thinks the same way as me.
:)

Strings are fundamentally evil for computers. ;)

Dave
Tom Serface
2009-06-26 18:36:28 UTC
Permalink
It would be nice if strings were checked at compile time, but it's not
likley that's going happen. I only use numbers nad build my own string
equivs if I need them.

Tom
Post by David Lowndes
Post by Mihai N.
String IDs are slower, and cannot be checked a compile time.
With numeric IDs, if I spell a string wrongly in code I get a compile time
error. With strings everything compiles well, and explodes 5 months later.
Same for XMLs and any other string-id based idea.
It's always good to hear that someone else thinks the same way as me.
:)
Strings are fundamentally evil for computers. ;)
Dave
David Ching
2009-06-26 18:23:59 UTC
Permalink
Post by Mihai N.
Post by Cadwell
I am open to suggestions or alternate ideas. It seems like this might be
possible using FindResource and EnumResourceNames, but I am not sure how to
go about using those as they are fairly cryptic.
In my experience a numeric string ID with resource-only DLL works best.
You don't have any conflicts
- because the numeric ID is really the resource-dll-handle + ID
- you can aboid naming conflicts by addopting a naming convention
So SHARED_IDS_PRINT is the stuff un the resource dll, IDS_PRINT can be your
local stuff. (or G_ or GLOBAL_ or whatever you want)
You can just provide the dll and a header file.
I don't like the overhead of maintaining the numeric constants in the header
file. Even though resource.h is somewhat managed by the IDE, it does not do
things like renumber the id's to remove holes caused by deleting unused
strings, etc.
Post by Mihai N.
String IDs are slower,
Not necessarily... a good hash table/map/dictionary (whatever you call it)
causes performance to be quite good. Maybe not as good as an integer id,
but good enough.
Post by Mihai N.
and cannot be checked a compile time.
With numeric IDs, if I spell a string wrongly in code I get a compile time
error. With strings everything compiles well, and explodes 5 months later.
Same for XMLs and any other string-id based idea.
True, although this does not cause many problems in real life due to the
English file also using the string constants, and if there was a
misspelling, it would be caught in the English as well. Of course the
misspelling could be introduced by a localizer in the localized file only,
but as the localizer is focused on the value of the string and not the id, I
don't think the id is in great danger of being accidentally edited.
Besides, any localizer tool could take care of this automatically by not
making the id editable.

So I find not needing to maintain resource ID's a big win. Further, I am
finding the approach used by Qt of using the tr() macro to embed the literal
string directly in the source code to be refreshing indeed. The programmer
is entirely freed of managing strings in any resource; this is done by the
pre-compiler. You had previously said this approach causes issues as the
context is not apparent to localizers, and sometimes strings get
accidentally merged. The tr() macro allows you to specify a string for the
context, as well as full-blown comments to the localizer, to keep these
things straight.

-- David
Mihai N.
2009-06-27 16:09:42 UTC
Permalink
Post by David Ching
Even though resource.h is somewhat managed by the IDE, it does not do
things like renumber the id's to remove holes caused by deleting unused
strings, etc.
But there is no problem with the holes.
If that bothers you, a Perl script can take care of that in seconds.
On the other side, strings IDs are not managed *at all* by the IDE
Post by David Ching
True, although this does not cause many problems in real life due to the
English file also using the string constants, and if there was a
misspelling, it would be caught in the English as well.
Not of there is a weird error that you rarely see.
Post by David Ching
So I find not needing to maintain resource ID's a big win.
But you have to. You still have to be carefull to add/remove stuff in two
places.
I really don't understand what "maintenance" is needed for numeric IDs.
Post by David Ching
The tr() macro allows you to specify a string for the
context, as well as full-blown comments to the localizer, to keep these
things straight.
A feature that many developers don't use.
Plus, a proper string for context will end up being almost an ID
(how do you make sure that the "Scan" button that scannes the disk is
not merged with the "Scan" button to scan a page? Or that the "New"
button to create a new file (masculin in most latin languages) is not
the same as the "New" button to create a new page (usually feminin))

You might say "these are isolated cases", but when you get a pretty
big software and you translate it into 20-30 languages, then it is
almost sure you will see this problems.

Strings should never-ever be merged. You will have to speak the 30
target languages to be sure (and you know nothing about language 31
that the sales guys will ask for next year).

Many developers don't believe this.
But unfortunately I know only two ways to convince:
1. work for at lease one year in localization "for real"
(not have your product localized, but work in a l10n company)
2. trust someone who knows
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
Giovanni Dicanio
2009-06-27 16:34:12 UTC
Permalink
Post by Mihai N.
(how do you make sure that the "Scan" button that scannes the disk is
not merged with the "Scan" button to scan a page? Or that the "New"
button to create a new file (masculin in most latin languages) is not
the same as the "New" button to create a new page (usually feminin))
This is the point that convinced me that unique numeric IDs are the way to
go (even if in source code, reading an English string looks more pretty than
reading something like IDS_...).
For example, the English word "open" can be translated in Italian in several
different ways, e.g. "aprire", "aperto", "apra"...

Giovanni
Joseph M. Newcomer
2009-06-27 19:32:27 UTC
Permalink
In the case of FormatMessage, the need to change word order for languages like German
disappears because you can use %n for various values of n.
joe

On Sat, 27 Jun 2009 18:34:12 +0200, "Giovanni Dicanio"
Post by Giovanni Dicanio
Post by Mihai N.
(how do you make sure that the "Scan" button that scannes the disk is
not merged with the "Scan" button to scan a page? Or that the "New"
button to create a new file (masculin in most latin languages) is not
the same as the "New" button to create a new page (usually feminin))
This is the point that convinced me that unique numeric IDs are the way to
go (even if in source code, reading an English string looks more pretty than
reading something like IDS_...).
For example, the English word "open" can be translated in Italian in several
different ways, e.g. "aprire", "aperto", "apra"...
Giovanni
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Tom Serface
2009-06-28 14:19:16 UTC
Permalink
I think this is one place where they got the IDE interface right. It's easy
enough to go into the dialog and delete unused IDs as well. You just have
to be careful because, depending on the use, the IDE may be reporting it
wrong. I typically use a combination of the Includes dialog and a search of
my files to do my cleanup every project cycle.

Tom
Post by Giovanni Dicanio
Post by Mihai N.
(how do you make sure that the "Scan" button that scannes the disk is
not merged with the "Scan" button to scan a page? Or that the "New"
button to create a new file (masculin in most latin languages) is not
the same as the "New" button to create a new page (usually feminin))
This is the point that convinced me that unique numeric IDs are the way to
go (even if in source code, reading an English string looks more pretty
than reading something like IDS_...).
For example, the English word "open" can be translated in Italian in
several different ways, e.g. "aprire", "aperto", "apra"...
Giovanni
Joseph M. Newcomer
2009-06-28 17:21:02 UTC
Permalink
In my first internationalized app, I built sentences out of individual phrases. This did
not work well in German; the embarrassing thing is that it is the only other language I
know, and 20 years ago (when I did this) I could do a lot better in it than I can today. I
should have known better.

I should use FormatMessage more, but I almost never use more than one formatting request
per string unless it is something like "%d (0x%08x") or "%s (%d)" so the issue never
arises.

I believe that EVERY instance of a message should be a UNIQUE string, not only for the
IDS_ value but for the text, even if all that changes is an internal error code string.
This means that if a customer reports an error message, and my client calls me, I can find
the EXACT place in my source that issued that message in under five minutes, and therefore
can usually figure out exactly what had gone wrong. Often it is customer error ("You mean
we *shouldn't* have deleted that file with the .cfg extension?") but I know PRECISELY what
went wrong! Strings are cheap. My time is expensive. My clients' reputations for
responding quickly to problems is valuable. There is no reason to "optimize" the
STRINGTABLE and compromise either situation.

In using MESSAGETABLE, deleting messages can be dangerous. In my course, I show the +1
syntax with one of those red circle/slash symbols and say "This is a really, really,
REALLY bad idea because you can't delete strings without changing the numbering, which
then destroys any legacy messages in the Event Log". In addition, I suggest that if a
message becomes deprecated, the text should be changed:

Pre-deprecation: "Please do not press that button again"

Post-deprecation: "(Obsolete with release 2.1.456) Please do not press that button again"

This way, anyone doing data mining of old Event Logs sees that the messages once had
meaning. MESSAGETABLE space is cheap. Getting a tech support call for "The unidentified
message code 666" is expensive.
Post by Tom Serface
I think this is one place where they got the IDE interface right. It's easy
enough to go into the dialog and delete unused IDs as well. You just have
to be careful because, depending on the use, the IDE may be reporting it
wrong. I typically use a combination of the Includes dialog and a search of
my files to do my cleanup every project cycle.
Tom
Post by Giovanni Dicanio
Post by Mihai N.
(how do you make sure that the "Scan" button that scannes the disk is
not merged with the "Scan" button to scan a page? Or that the "New"
button to create a new file (masculin in most latin languages) is not
the same as the "New" button to create a new page (usually feminin))
This is the point that convinced me that unique numeric IDs are the way to
go (even if in source code, reading an English string looks more pretty
than reading something like IDS_...).
For example, the English word "open" can be translated in Italian in
several different ways, e.g. "aprire", "aperto", "apra"...
Giovanni
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Mihai N.
2009-07-11 08:35:48 UTC
Permalink
Post by Joseph M. Newcomer
I believe that EVERY instance of a message should be a UNIQUE string, not only for the
IDS_ value but for the text, even if all that changes is an internal error code string.
This means that if a customer reports an error message, and my client calls me, I can find
the EXACT place in my source that issued that message in under five minutes, and therefore
can usually figure out exactly what had gone wrong.
One of the things I recomens is to have a numeric code for all errors that
can end up being reported in a tech-support call:

Something like "Error 943: bla-bla-bla" or "Bla-bla-bla (Error code
'errFileReadOnly')"

If the messages are for deep errors that developers think of "messages for
me, to understand what the heck is going on" not messages that can help
the user fix the problem, then I recomend something else.
Instead of something like (ok, made up crap):
"Corrupted hash table with garbled heap in tree balancing routine"
Go with
"Internal error %d. Please call tech support."
User friendly, programmer friendly, and saves localization costs
(imagine translating one generic message instead of 300 generic ones)


But this is *only* for stuff that is intended for tech support anyway.
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
Joseph M. Newcomer
2009-07-11 21:03:55 UTC
Permalink
See below...
Post by Joseph M. Newcomer
Post by Joseph M. Newcomer
I believe that EVERY instance of a message should be a UNIQUE string, not
only for the
Post by Joseph M. Newcomer
IDS_ value but for the text, even if all that changes is an internal error
code string.
Post by Joseph M. Newcomer
This means that if a customer reports an error message, and my client calls
me, I can find
Post by Joseph M. Newcomer
the EXACT place in my source that issued that message in under five
minutes, and therefore
Post by Joseph M. Newcomer
can usually figure out exactly what had gone wrong.
One of the things I recomens is to have a numeric code for all errors that
Something like "Error 943: bla-bla-bla" or "Bla-bla-bla (Error code
'errFileReadOnly')"
If the messages are for deep errors that developers think of "messages for
me, to understand what the heck is going on" not messages that can help
the user fix the problem, then I recomend something else.
"Corrupted hash table with garbled heap in tree balancing routine"
Go with
"Internal error %d. Please call tech support."
User friendly, programmer friendly, and saves localization costs
(imagine translating one generic message instead of 300 generic ones)
****
I agree. Most of my internal error messages that would be seen by the end user are
actually very technology-neutral. You don't *really* want to explain some bizarre
internal implementation detail. However, note that every site in my program that can
generate such an error has a *different* error code. Sometimes I provide supplemental
information:

Internal error %d. Please call tech support.
Supplemental information:
Error detail: "gamma"
Error source: "alpha"
Error response: "beta"

where for "alpha", "beta" and "gamma" I subsitute some meaningful string, such as a file
name or subroutine name or integer code or some other code that might be useful to provide
tech support to help them identify the problem, or to relay the problem to me. In many
cases, common subroutines take some ID as an extra parameter which essentially tells me
who called the subroutine. (Some people will childishly say "But that's inefficient!" as
if the extra 175 picoseconds required to push that parameter actually matter in the
slightest! Compared to the cost of tech support, this doesn't matter at all!) Otherwise,
you end up with useless error messages such as VS produces, "You have a memory leak
allocated by strcore.cpp". Big whoop. Why should I care that CString::CString leaks
storage? I want to know WHO CALLED IT!
joe
Post by Joseph M. Newcomer
But this is *only* for stuff that is intended for tech support anyway.
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Mihai N.
2009-07-12 04:13:27 UTC
Permalink
I subsitute some meaningful string, such as a file name or
subroutine name or integer code or some other code that might
be useful to provide tech support to help them identify the problem,
or to relay the problem to me.
Agree with you.
Macros like __FILE__ and __LINE__ come in very handy.
__FUNCDNAME__, __FUNCSIG__, __FUNCTION__ are also nice VS extensions,
but are not portable.
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
Joseph M. Newcomer
2009-07-12 05:17:34 UTC
Permalink
Yes, but portability of a Windows app to a non-Microsoft compiler is of zero interest to a
lot of us.

int SomeFunction(CString s, int d)
{
DebugLog(_T("%s=>") _T(__FUNCTION__) _T("(%s, %d)"), Enter(), (LPCTSTR)FirstParameter,
SecondParameter);
...
DebugLog(_T("%s<=") _T(__FUNCTION__) _T("= %d"), Leave(), retval);
return retval;
}

where Enter() is (in sketch form)

static int IndentLevel = 0;

CString Enter()
{
IndentLevel++;
return CString(_T(' '), 2 * IndentLevel);
}

CString Leave()
{
IndentLevel--;
return CString(_T(' '), 2 * (IndentLevel + 1));
}

Then I can do

#define ENTRY_STRING(fmt) _T("%s=>") _T(__FUNCTION__) fmt, Enter()
#define EXIT_STRING(fmt) _T("%s<=") _T(__FUNCTION__) fmt, Leave()

so I can write

DebugLong(ENTRY_STRING(_T("(%s, %d)"), (LPCTSTR)FirstParameter, SecondParameter);
DebugLog(EXIT_STRING(_T("%d")), retval);

When I really need to trace logic in the code, this forms the basis.
joe
Post by Mihai N.
I subsitute some meaningful string, such as a file name or
subroutine name or integer code or some other code that might
be useful to provide tech support to help them identify the problem,
or to relay the problem to me.
Agree with you.
Macros like __FILE__ and __LINE__ come in very handy.
__FUNCDNAME__, __FUNCSIG__, __FUNCTION__ are also nice VS extensions,
but are not portable.
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

David Ching
2009-06-29 02:26:03 UTC
Permalink
Post by Tom Serface
I think this is one place where they got the IDE interface right. It's
easy enough to go into the dialog and delete unused IDs as well. You just
have to be careful because, depending on the use, the IDE may be reporting
it wrong. I typically use a combination of the Includes dialog and a
search of my files to do my cleanup every project cycle.
Yeah, it's manageable, but just like everything MFC, is too manual! :-)

-- David
Tom Serface
2009-06-29 15:09:11 UTC
Permalink
I think you're getting spoiled :o)

Tom
Post by David Ching
Post by Tom Serface
I think this is one place where they got the IDE interface right. It's
easy enough to go into the dialog and delete unused IDs as well. You just
have to be careful because, depending on the use, the IDE may be reporting
it wrong. I typically use a combination of the Includes dialog and a
search of my files to do my cleanup every project cycle.
Yeah, it's manageable, but just like everything MFC, is too manual! :-)
-- David
David Ching
2009-06-29 18:56:22 UTC
Permalink
Post by Tom Serface
I think you're getting spoiled :o)
Yup. :-) It's like having 90% of .NET without the heaviness.

Cheers,
David
David Ching
2009-06-29 02:24:53 UTC
Permalink
Post by Mihai N.
On the other side, strings IDs are not managed *at all* by the IDE
...
Post by Mihai N.
Post by David Ching
True, although this does not cause many problems in real life due to the
English file also using the string constants, and if there was a
misspelling, it would be caught in the English as well.
Not of there is a weird error that you rarely see.
Yes, I can understand that. String ID's have downsides, but so do numeric
ID's.
Post by Mihai N.
I really don't understand what "maintenance" is needed for numeric IDs.
Essentially, any and all required manipulation of resource.h. Who hasn't
spent at least some time in resource.h?
Post by Mihai N.
Post by David Ching
The tr() macro allows you to specify a string for the
context, as well as full-blown comments to the localizer, to keep these
things straight.
A feature that many developers don't use.
With respect, developers who don't use the context are the same developers
who would attempt to re-use the same resource ID for another context. So
the problem is the same, it is no more pervasive the Qt way than with
traditional resource id way.

I will admit that I've reused a few ID's in my time...
Post by Mihai N.
Plus, a proper string for context will end up being almost an ID
(how do you make sure that the "Scan" button that scannes the disk is
not merged with the "Scan" button to scan a page? Or that the "New"
button to create a new file (masculin in most latin languages) is not
the same as the "New" button to create a new page (usually feminin))
Here Qt actually gives you more room by having a special comment you can use
to discuss such things with the translator. No such comment space is
provided in a traditional .rc file stringtable. And the comment goes right
next to the usage of the string (in the source code) and not in some other
file, so you don't have to manipulate separate .rc files during development.
Post by Mihai N.
Strings should never-ever be merged. You will have to speak the 30
target languages to be sure (and you know nothing about language 31
that the sales guys will ask for next year).
Perhaps not, but the same merging occurs when using .rc files as well. It's
an educational thing, not a superiority of .rc files. And you are probably
right that strings should "never-ever" be merged, but then why do
translation memory programs make it so easy to reuse these strings? A
bragging feature of them is that they will search the database for
pre-existing words and phrases and re-use them for new strings....
Post by Mihai N.
You might say "these are isolated cases", but when you get a pretty
big software and you translate it into 20-30 languages, then it is
almost sure you will see this problems.
...
Post by Mihai N.
Many developers don't believe this.
1. work for at lease one year in localization "for real"
(not have your product localized, but work in a l10n company)
2. trust someone who knows
Your word is absolutely correct for one scenario. But its important to use
the right tool for the job. What is essential for Adobe Reader (millions of
users, many languages, huge software) does not necessarily serve smaller
projects with fewer users and less languages. You have to see the big
picture. A product's success is not dependent only on how well it is
localized. Things like fast development, stability, speed, and
deployability (which are not necessarily trademarks of Adobe Reader, no
offense) are also critical. So if a localization scheme is less accurate
(but is not necessarily so) and is more productive to use, it is not
necessarily a bad thing. I've successfully used .h/.rc files, string ID XML
files, and am about to deploy a Qt app, and for my needs I have to say that
the Qt app provides beautiful localization tools and lets me keep strings in
my code, as any developer would prefer.

Oh, and Qt does not require the silly _T() macro either! So embedded
strings are truly simple again. (Yet strings are full UTF-16).

-- David
Tom Serface
2009-06-29 15:08:42 UTC
Permalink
That's a really nice feature...
Post by David Ching
Here Qt actually gives you more room by having a special comment you can
use to discuss such things with the translator. No such comment space is
provided in a traditional .rc file stringtable. And the comment goes
right next to the usage of the string (in the source code) and not in some
other file, so you don't have to manipulate separate .rc files during
development.
Mihai N.
2009-06-30 05:16:23 UTC
Permalink
Post by David Ching
Post by Mihai N.
I really don't understand what "maintenance" is needed for numeric IDs.
Essentially, any and all required manipulation of resource.h. Who hasn't
spent at least some time in resource.h?
Only if you want to. It is never *needed*.
You have a #define for something never used. So what?
It does not take any memory.
Post by David Ching
With respect, developers who don't use the context are the same developers
who would attempt to re-use the same resource ID for another context. So
the problem is the same, it is no more pervasive the Qt way than with
traditional resource id way.
Reuse is not possible in .rc files at least in dialogs.
Yes, you can reuse strings in string tables (still bad), but at least the
dialogs are "clean"
Post by David Ching
Here Qt actually gives you more room by having a special comment you can
use to discuss such things with the translator.
...
Post by David Ching
And the comment goes right
next to the usage of the string (in the source code) and not in some other
file, so you don't have to manipulate separate .rc files during development.
I am not familiar with that.
Is that comment extracted automatically? Remember that the translators
don't dig into source files.

A drawback of embeded strings: no special characters (thing smart quites,
mdash, ndash, copyright and registration). If you use them, you are not
cross-platform anymore.
Post by David Ching
No such comment space is
provided in a traditional .rc file stringtable.
That is indeed a bad thing.
Post by David Ching
Perhaps not, but the same merging occurs when using .rc files as well.
Not in dialogs. At least that.
Post by David Ching
why do
translation memory programs make it so easy to reuse these strings? A
bragging feature of them is that they will search the database for
pre-existing words and phrases and re-use them for new strings....
They don't reuse them automatically, they suggest it to the translator
(like intellisense). It is a human who knows the language making the
final decision. So it increases productivity without sacrificing
quality.
Post by David Ching
What is essential for Adobe Reader (millions of
users, many languages, huge software) does not necessarily serve smaller
projects with fewer users and less languages.
I am not talking about Acrobat (which does not use .rc files to begin with,
because it is cross-platform). If you search for my posts you will see
that my position on string IDs, strings embeded in sources, and
strings reuse, did not change from before I worked for Adobe.
The only good reason to not use the standard resources of the framework
is the need to be cross-platform. And even then numeric IDs and strings
outside the code are the better option.
Post by David Ching
A product's success is not dependent only on how well it is
localized. Things like fast development, stability, speed, and
deployability ... are also critical.
None of them incompatible with numeric IDs.
Post by David Ching
So if a localization scheme is less accurate
(but is not necessarily so) and is more productive to use, it is not
necessarily a bad thing.
Yes, it is. Crappy UI language for the end-user influences the perception
about your software. The end-users don't care about your productivity.
Post by David Ching
keep strings in my code, as any developer would prefer.
Make that "any developer would WRONGLY prefer" and I am fine.
Post by David Ching
(Yet strings are full UTF-16).
Really? So the cpp files are UTF-16 in Qt? gcc can compile that?
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
Giovanni Dicanio
2009-06-30 09:17:24 UTC
Permalink
Post by Mihai N.
Post by David Ching
(Yet strings are full UTF-16).
Really? So the cpp files are UTF-16 in Qt? gcc can compile that?
I can be wrong, but it seems to me that, if something like:

QString message = "Some message";

is written, QString class converts (at run-time) its passed string literal
from ASCII to Unicode (to store it as Unicode UTF-16 internally).

(But David is the guru of QT here.)

Giovanni
Mihai N.
2009-07-01 06:58:17 UTC
Permalink
Post by Giovanni Dicanio
QString message = "Some message";
is written, QString class converts (at run-time) its passed string literal
from ASCII to Unicode (to store it as Unicode UTF-16 internally).
But that still means that the source file is ASCII (or iso-8859-1),
anyway, something else than UTF-16.

Which means you will have troubles using smart quotes or
copyright/trademark/registration or n/mdashes in your English strings.

So nineties ... :-)
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
Giovanni Dicanio
2009-07-01 08:51:27 UTC
Permalink
Post by Mihai N.
Post by Giovanni Dicanio
QString message = "Some message";
is written, QString class converts (at run-time) its passed string literal
from ASCII to Unicode (to store it as Unicode UTF-16 internally).
But that still means that the source file is ASCII (or iso-8859-1),
anyway, something else than UTF-16.
Which means you will have troubles using smart quotes or
copyright/trademark/registration or n/mdashes in your English strings.
I don't know for sure.
In general, I used to think that C/C++ source codes should be ASCII only,
and special characters should be put in some external files like resources.
This to avoid confusions with several different encodings.
(For example, when I wrote code and commented it in Italian, I used to avoid
characters like "é ò" etc. and used "e' o' " instead in the comments.)
And if special "non-ASCII" characters should be put in string literals, I
would use the numeric escapes (e.g. "\x....").
I think that this ASCII-only policy in C/C++ source code is good and safe.

I'm not sure if in C# we can safely use Unicode in source files, or even use
Unicode in identifiers...
Post by Mihai N.
So nineties ... :-)
:-)

(You kind of remind me of our Joe when he reads malloc() and says: "so
seventies!" :-)

Giovanni
Joseph M. Newcomer
2009-07-01 15:38:45 UTC
Permalink
At least in comments and strings, accented characters are legitimate. You just can't use
them in identifiers, which is terribly ethnocentric. The reason that most languages use
only the English 7-bit subset, and a subset of that, was that other languages used
different characters in certain positions; for example, in the German code, "[\]{|}" would
have printed as "ÄÖÜäöü", so C was a wild departure (but did not require any conformance
to any kind of standard, because it was just a little in-house language for Bell Labs
research). Algol-60 used a subset, and because most of the language designers spoke
fluent English, there was a ready agreement that reserved words (procedure, do, if, then,
else, for, step, until, and so on) could be in English. Instead of the =/==, the choice
was :=/=, but symbols like &, |, {, }, [ and ] were carefully NOT used because they had
incompatible renderings in many languages (and { } [ ] did not even exist in most computer
fonts of the era). Likewise, accented characters were dismissed because most computer
fonts did not support them at all, and different computers had different representations
of fonts so your punched card deck (was there any other program representation? Yes,
punched paper tape. Files? What are those?) might say grün on one computer at gr:n or
gr}n or gr]n on another, and on a third a "card reader error, invalid card column" might
be the hardware response. That legacy is still with us, even in the Unicode era!

Note that in the IBM character sets, you could have "+()#" or "&%¤=" as the glyphs shown,
so you could write a FORTRAN statement that appeared as

A = (B + C) * 7
or
A # %B & C¤ * 7

and it would depend on what keypunch you sat down at as to what you saw on your cards, and
the printers usually gave the second ("commercial character set") rendering instead of the
first ("scientific character set") rendering.

Note that this meant that a scientific output could not talk about "%" as a symbol. So we
got used to seeing

CA 20.0(
PA 0.2(
NA 11.1(
MN 2.2(
CO 1.3(
GA 0.1(
AL 4.7(
AR 0.0(

(and that's calcium, palladium, sodium, manganese, cobalt, gallium, aluminum and argon, in
case you were wondering what state is called "NA") Sometimes what we saw varied because
scientific jobs were intermingled with commercial jobs; sometimes the operator might
change the printer font (which involved opening the printer, removing the existing print
chain, and replacing it with the other print chain), and sometimes not. In other shops,
with other printers, alternate character sets were not usually available, and the
commercial set was chosen for the printer.
joe

On Wed, 1 Jul 2009 10:51:27 +0200, "Giovanni Dicanio"
Post by Giovanni Dicanio
Post by Mihai N.
Post by Giovanni Dicanio
QString message = "Some message";
is written, QString class converts (at run-time) its passed string literal
from ASCII to Unicode (to store it as Unicode UTF-16 internally).
But that still means that the source file is ASCII (or iso-8859-1),
anyway, something else than UTF-16.
Which means you will have troubles using smart quotes or
copyright/trademark/registration or n/mdashes in your English strings.
I don't know for sure.
In general, I used to think that C/C++ source codes should be ASCII only,
and special characters should be put in some external files like resources.
This to avoid confusions with several different encodings.
(For example, when I wrote code and commented it in Italian, I used to avoid
characters like "é ò" etc. and used "e' o' " instead in the comments.)
And if special "non-ASCII" characters should be put in string literals, I
would use the numeric escapes (e.g. "\x....").
I think that this ASCII-only policy in C/C++ source code is good and safe.
I'm not sure if in C# we can safely use Unicode in source files, or even use
Unicode in identifiers...
Post by Mihai N.
So nineties ... :-)
:-)
(You kind of remind me of our Joe when he reads malloc() and says: "so
seventies!" :-)
Giovanni
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer
2009-06-30 14:51:21 UTC
Permalink
See below...
Post by Mihai N.
Post by David Ching
Post by Mihai N.
I really don't understand what "maintenance" is needed for numeric IDs.
Essentially, any and all required manipulation of resource.h. Who hasn't
spent at least some time in resource.h?
Only if you want to. It is never *needed*.
You have a #define for something never used. So what?
It does not take any memory.
****
One of the very unfortunate misfeatures that *requires* the hand-editing of resource.h is
how radio button IDs are assigned. The editor needs a primitive that allows you to select
a group of radio buttons and say "Reassign all the control IDs for this group" so they are
a dense set of integers. And it is also important that a given resource ID, if used for
radio buttons, should *never, ever* be reused in another dialog. Of course, I learned
this nearly 20 years ago, so for me it is reflexive to be able to hand-edit resource.h to
add a new button long after the radio group was created, but it is astonishing how many
people *don't* understand how to do this.

When I'm in there, I'll get rid of the typos that resulted in unused symbols, such as
IDC_MEIDUM (should have been IDC_MEDIUM) and such, but that's just a side effect of being
in there at all. I hardly ever look at it, and only rarely do any hand-editing other than
for radio button IDs.
****
Post by Mihai N.
Post by David Ching
With respect, developers who don't use the context are the same developers
who would attempt to re-use the same resource ID for another context. So
the problem is the same, it is no more pervasive the Qt way than with
traditional resource id way.
Reuse is not possible in .rc files at least in dialogs.
Yes, you can reuse strings in string tables (still bad), but at least the
dialogs are "clean"
****
Modular reuse would be nice, but overall it happens so rarely that copy-and-paste usually
suffices. Sadly, the new VS doesn't allow me to open one project and do a "copy" and go
to another project and do a "paste" (VS has its own internal pseudo-clipboard for dialogs;
apparently the VS creators hadn't yet received word that Windows actually has a mechanism
that does this, called, surprisingly "the clipboard")
****
Post by Mihai N.
Post by David Ching
Here Qt actually gives you more room by having a special comment you can
use to discuss such things with the translator.
...
Post by David Ching
And the comment goes right
next to the usage of the string (in the source code) and not in some other
file, so you don't have to manipulate separate .rc files during development.
I am not familiar with that.
Is that comment extracted automatically? Remember that the translators
don't dig into source files.
****
If I'm really serious about sharing resources, I put them in DLLs. A common multi-project
dialog, for example, has its own DLL. I know there is a school of thought that says that
creating DLLs is one of those deep and fundamentally Bad Ideas that should never be used,
because then you have to distribute multiple files, but I somehow missed why having
multiple files really is such a Bad Idea.
****
Post by Mihai N.
A drawback of embeded strings: no special characters (thing smart quites,
mdash, ndash, copyright and registration). If you use them, you are not
cross-platform anymore.
****
Actually, you are not cross-font on the *same* platform any longer, but that's yet a
different discussion. And I have never understood why linux and the Mac have ignored
international standards such as ISO-8859-1.
****
Post by Mihai N.
Post by David Ching
No such comment space is
provided in a traditional .rc file stringtable.
That is indeed a bad thing.
****
The whole precompiled-to-.aps file hack was created because computers were slow. They
aren't any longer, and this Bad Idea should silently disappear; nobody would notice or
care, and then we could put comments in, just like in other source files. Or they could
modify the .aps translation to preserve comments; it isn't like it is Rocket Science.
****
Post by Mihai N.
Post by David Ching
Perhaps not, but the same merging occurs when using .rc files as well.
Not in dialogs. At least that.
Post by David Ching
why do
translation memory programs make it so easy to reuse these strings? A
bragging feature of them is that they will search the database for
pre-existing words and phrases and re-use them for new strings....
They don't reuse them automatically, they suggest it to the translator
(like intellisense). It is a human who knows the language making the
final decision. So it increases productivity without sacrificing
quality.
****
Apparently our European distributor hired a couple non-technical types to translate
strings, and got some very creative translations that were either incomprehensible or
downright hysterically funny. We just send the English-language executable to him, and he
hires the translators and runs a program across the executable that translates English
strings to a variety of languages. I heard about this indirectly, reported it to the
client, who reported it to the translator, who didn't understand that there was anything
wrong. So he found a Swede who was a programmer, who spoke English well, and got the
correct translation. I no longer remember the word or phrase as it read, but BabelFish
would probably have done as good a job (www.babelfish.com). This is one of the reasons I
*never* use the same message twice in my programs. In context, the same sentence can be
informative in one case and misleading in another. Even in English. Especially under
maintenance, where the message can evolve to explain better in one context what really
happened, and then be really confusing when it tries to give the same explanation in a
different context. So it is not at all uncommon to find two unique IDS_ strings in my
STRINGTABLE that say the same words. Today. But maybe they won't, tomorrow.
****
Post by Mihai N.
Post by David Ching
What is essential for Adobe Reader (millions of
users, many languages, huge software) does not necessarily serve smaller
projects with fewer users and less languages.
I am not talking about Acrobat (which does not use .rc files to begin with,
because it is cross-platform). If you search for my posts you will see
that my position on string IDs, strings embeded in sources, and
strings reuse, did not change from before I worked for Adobe.
The only good reason to not use the standard resources of the framework
is the need to be cross-platform. And even then numeric IDs and strings
outside the code are the better option.
****
In 1987, I wrote a program that created the equivalent of a STRINGTABLE/MESSAGETABLE. It
was a database (in dBASE III) that had string IDs, and when it was run, it created a text
file of messages; the equivalent of resource.h gave the index of the line on which it
placed the message. It also sorted the lines alphabetically and created a TeX file which
when included in the documentation created the appendix which explained every message.
****
Post by Mihai N.
Post by David Ching
A product's success is not dependent only on how well it is
localized. Things like fast development, stability, speed, and
deployability ... are also critical.
None of them incompatible with numeric IDs.
****
I fail to see why numeric IDs are remotely an issue. See my previous description; I was
using numeric IDs in 1987, and there was nothing wrong with them then, and I fail to see
why there has ever been a problem. If the rc and mc compilers are not suitable, WRITE
YOUR OWN. You can probably write a PERL or PYTHON script to do this in a couple days! Or
generate the output from an Access database. Or whatever.
****
Post by Mihai N.
Post by David Ching
So if a localization scheme is less accurate
(but is not necessarily so) and is more productive to use, it is not
necessarily a bad thing.
Yes, it is. Crappy UI language for the end-user influences the perception
about your software. The end-users don't care about your productivity.
Post by David Ching
keep strings in my code, as any developer would prefer.
Make that "any developer would WRONGLY prefer" and I am fine.
****
Why "any developer would prefer"? What is so magical about strings? Does it matter if I
say "IDS_FILE_OPEN_FAILURE" or IDS_FILE_OPEN_FAILURE? It's just a couple quote marks!

I would *not* prefer to use strings. They don't buy that much, and are clumsy to use. In
the Locale Explorer, I stored the help text as embedded .rtf files with string names, and
it was fine when I had no tabs in the app, and is horrid now that I have a whole lot more.
It is one of the features I regret in the design. It Seemed Like A Good Idea At The Time.
It wasn't.
****
Post by Mihai N.
Post by David Ching
(Yet strings are full UTF-16).
Really? So the cpp files are UTF-16 in Qt? gcc can compile that?
****
Probably as well as VS can. \xNNNN syntax works. Unreadable, but works.
joe
****
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Mihai N.
2009-07-11 08:26:16 UTC
Permalink
Post by Joseph M. Newcomer
Post by Mihai N.
Really? So the cpp files are UTF-16 in Qt? gcc can compile that?
Probably as well as VS can. \xNNNN syntax works. Unreadable, but works.
Actually, \x is wrong, because that means "bytes in whatever code
page the program happens to run". It is not Unicode.

\uXXXX will work for L"bla", but then that's UTF-16 in VC and UTF-32 in gcc.

And then there is wchar_t str[] = { 0xXXXX, .... }, with the same problem as
above. And not quite localizable :-)

All of them as unreadable, of course.

Compare:

message=L"Copyright \u00A9 1999 Bla \u2013 Click \x201cMore\u201d for extra
info\u2026"
(unreadable, and how the heck is a developer to know all the Unicode values,
withouth using an external app, which is exactly the argument against a
using resources in a separate files)

message=L"Copyright (c) 1999 Bla -- Click \"More\" for extra info..."
(readable and nice for the developer, but looking dated and unprofesional
to the end user)
--
Mihai Nita [Microsoft MVP, Visual C++]
http://www.mihai-nita.net
------------------------------------------
Replace _year_ with _ to get the real email
Joseph M. Newcomer
2009-07-11 20:56:30 UTC
Permalink
I had not known about the \u syntax. Clearly I need to be paying more attention to these
things.

What amazes me is that Microsoft *still* generates (c) in the default About box, instead
of using the © symbol. It's like, well, if it was good enough for Windows 16 1.0 under
MS-DOS, it must obviously *still* be the Right Thing for a Unicode application generated
in 2009 (where did I misplace those intervening 20+ years of platform evolution?)
joe
Post by Mihai N.
Post by Joseph M. Newcomer
Post by Mihai N.
Really? So the cpp files are UTF-16 in Qt? gcc can compile that?
Probably as well as VS can. \xNNNN syntax works. Unreadable, but works.
Actually, \x is wrong, because that means "bytes in whatever code
page the program happens to run". It is not Unicode.
\uXXXX will work for L"bla", but then that's UTF-16 in VC and UTF-32 in gcc.
And then there is wchar_t str[] = { 0xXXXX, .... }, with the same problem as
above. And not quite localizable :-)
All of them as unreadable, of course.
message=L"Copyright \u00A9 1999 Bla \u2013 Click \x201cMore\u201d for extra
info\u2026"
(unreadable, and how the heck is a developer to know all the Unicode values,
withouth using an external app, which is exactly the argument against a
using resources in a separate files)
message=L"Copyright (c) 1999 Bla -- Click \"More\" for extra info..."
(readable and nice for the developer, but looking dated and unprofesional
to the end user)
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer
2009-06-27 19:31:30 UTC
Permalink
So use a .mc file and the mc compiler...moves the whole problem outside the resource
editor domain.
joe
Post by David Ching
Post by Mihai N.
Post by Cadwell
I am open to suggestions or alternate ideas. It seems like this might be
possible using FindResource and EnumResourceNames, but I am not sure how to
go about using those as they are fairly cryptic.
In my experience a numeric string ID with resource-only DLL works best.
You don't have any conflicts
- because the numeric ID is really the resource-dll-handle + ID
- you can aboid naming conflicts by addopting a naming convention
So SHARED_IDS_PRINT is the stuff un the resource dll, IDS_PRINT can be your
local stuff. (or G_ or GLOBAL_ or whatever you want)
You can just provide the dll and a header file.
I don't like the overhead of maintaining the numeric constants in the header
file. Even though resource.h is somewhat managed by the IDE, it does not do
things like renumber the id's to remove holes caused by deleting unused
strings, etc.
Post by Mihai N.
String IDs are slower,
Not necessarily... a good hash table/map/dictionary (whatever you call it)
causes performance to be quite good. Maybe not as good as an integer id,
but good enough.
Post by Mihai N.
and cannot be checked a compile time.
With numeric IDs, if I spell a string wrongly in code I get a compile time
error. With strings everything compiles well, and explodes 5 months later.
Same for XMLs and any other string-id based idea.
True, although this does not cause many problems in real life due to the
English file also using the string constants, and if there was a
misspelling, it would be caught in the English as well. Of course the
misspelling could be introduced by a localizer in the localized file only,
but as the localizer is focused on the value of the string and not the id, I
don't think the id is in great danger of being accidentally edited.
Besides, any localizer tool could take care of this automatically by not
making the id editable.
So I find not needing to maintain resource ID's a big win. Further, I am
finding the approach used by Qt of using the tr() macro to embed the literal
string directly in the source code to be refreshing indeed. The programmer
is entirely freed of managing strings in any resource; this is done by the
pre-compiler. You had previously said this approach causes issues as the
context is not apparent to localizers, and sometimes strings get
accidentally merged. The tr() macro allows you to specify a string for the
context, as well as full-blown comments to the localizer, to keep these
things straight.
-- David
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Loading...