Fog Creek Software
Discussion Board




Arguing about strings and resource files

Hi People.

At my company we have a serious argument about how to solve the following problem:

At various points in the code (C++) we want to return Error Messages to the user.

The error messages that go out to the client need to be easly translatable to various languages. The proposed solution is resource files.

The problem is that the strings need to be customizable to the exact circumstance. That means that if I have to throw out "File Not Found" I actually want to throw out "File somefile.dat Not Found". That means that the strings need to be parameterized some way.

Does anybody know about a good solution to this problem? I know I'm not the first ones to run up agains it and I have no desire to reinvent the wheel.

Or

Or Peles
Monday, April 08, 2002

We've always used format strings in the resource strings, such as

"File %s not found."

You may want to consider post-translation testing to make sure that no extra format strings were inserted by the translator, which could cause program errors.

B
Monday, April 08, 2002

Put "File %s Not Found" in the resource file, and use the resource string as a sprintf parameter. This works for most strings (it fails when you have a complicated string with more than one parameter, where the sequence in which the parameters should be inserted in the string varies from one natural language to another).

Christopher Wells
Monday, April 08, 2002

Wow, talk about fast replies. I'm impressed.

Actually, Putting something like "File %s not found" and using something like sprintf is the solution we came up with, but I don't like it for the following reasons:

1. It's difficult to maintain, since every change in the resource string requires that you go through the code and change things everywhere.
2. In case of string-parameter list mismatches, which are obviously not detectable in compile time, you're in trouble.

I'd also like your opinion on this - If I don't need internationalization (ugly word), should I still use a resource file or just Hard-Code the strings?

Or

Or Peles
Monday, April 08, 2002

FWIW, you shouldn't use %s because in some circumstances if you have a string which takes two parameters, the translated text won't necessarily have them in the same order.  (I.e. you won't know that the first %s in the english string is actually the second %s in the french string).

Try something like %1, %2, etc., so that when the strings are translated the order can be conserved.

Michael H. Pryor
Monday, April 08, 2002

Try using named parameters instead of positional parameters; it's generally almost
always a more maintainable way of doing things.

e.g: SOMECODE = "File %fname% not found"

and

Report(SOMECODE, "fname", "file.data", 0)

Then use variable length argument lists to
suck out the data.

D. Holloway
Monday, April 08, 2002

> I'd also like your opinion on this - If I don't need internationalization (ugly word), should I still use a resource file or just Hard-Code the strings?

I hard-code them.

Anything (including error messages) which are designed for the end-user to see (and perhaps fix) are translated.

Anything (assertion failures) which are designed for the developer to see are not translated (have internationalized boiler-plate saying "Call tech. support, and send them the following error data").

Christopher Wells
Monday, April 08, 2002

> 1. It's difficult to maintain, since every change in the resource string requires that you go through the code and change things everywhere.

Minimize the number of error message you give to the end-user. <g> It's a serious problem, e.g. when you have 15 resource files maintained by 15 translators each with their own schedule.

2. In case of string-parameter list mismatches, which are obviously not detectable in compile time, you're in trouble.

Right, big trouble, sprintf isn't not at all type-safe, and so can crash the application ... especially if your regression test cases don't cover all error messages. Roll your own parameter string parser/replacer, or wrap it in a Structured Exception Handler.

Christopher Wells
Monday, April 08, 2002

<I'd also like your opinion on this - If I don't need internationalization (ugly word), should I still use a resource file or just Hard-Code the strings?
>

If you *do* need internationalization, you have to be careful with parameterizing strings. Say you have a string
"Failed to do %s action %i times", so the code would load the string and then
sprintf (somebuf, errorstring, lpszAction, iNumberOfTimes)
... but this might not work in a language that wanted to list the number of times first. (This is not a good example, but then I'm not taking any time to think about it - I'm at work).
But you get the gist.

Unfortunately, I do not have a good general solution to this problem, despite years of searching.

skautsch
Monday, April 08, 2002

>FWIW, you shouldn't use %s because in some >circumstances if you have a string which takes two >parameters, the translated text won't necessarily have >them in the same order. (I.e. you won't know that the >first %s in the english string is actually the second %s in >the french string).

Delphi's format strings has this nifty additional feature called an index specifier, so that the format string can reorder the arguments as appropriate. I guess there isn't such a thing in the C RTL.

B
Monday, April 08, 2002

If this is on the Windows platform, take a look at the FormatMessage api. This is basically an sprintf on steroids, but the big difference is that is suports positional substitutions (%1, %2) etc. and pays attention to locales.

Basically, it does everything you need for just this circumstance.

Chris Tavares
Monday, April 08, 2002

> Delphi's format strings has this nifty additional feature called an index specifier, so that the format string can reorder the arguments as appropriate. I guess there isn't such a thing in the C RTL.

Maybe look at the Win32 API function called FormatMessage().

Christopher Wells
Monday, April 08, 2002

probably a little cumbersome, but xml can come in really handy here, have a resource file like:
<ErrorMessages>
<Error errorID="1" errorLang="USENG" />
<ErrorMessage>You have done a bad thing please contact tech support <ErrorMessage/>
<Error>
<Error errorID="1" errorLang="Piglatin" />
<ErrorMessage>ouya avhay oneday aya adbay ingthay easeplay ontactcay echtay upportsay <ErrorMessage/>
<Error>
<Error errorID="2" errorLang="English..." />
</ErrorMessages>

At initialization create a doublekey map and you are worry free my man
email me if you have any questions

Daniel SHchyokin
Monday, April 08, 2002

** NEVER HARD CODE THE STRINGS **

The project I am working on has hard coded strings everywhere. Not that we have to internationalize the product, there is no end of headache in removing the hard coded strings and putting them in a resource file.

The end solution we have is to use a resource file and to use positional formatting. eg: "File %1 not found."

You might also consider using a text file for the strings, like Java does, if your product is for more than just Windows.

** NEVER HARD CODE THE STRINGS **

James Ladd
Monday, April 08, 2002

Search google for "GNU gettext" - you'll find a mature tool, and many war stories about it. It manages to have reasonably easy to maintain translations and at the same time keeps code readable by using a default string as an index into the translation file.

Not perfect, but works, and highly portable across both programming languages and platforms.

Ori Berger
Monday, April 08, 2002

I started implementing this same type of thing and started 'resourcing' error messages like :

The file '%1' cannot be opened because %2

then the routine I use to raise errors would tack on insert two arguments :

Arg(0) = "C:\Foo.txt" and Arg(1) = "the file doesn't exist"

and resolve them as follows :

1) i iterate through the arguments, starting with 0
2) build a string token like "%" + i, giving %1 in the first iteration.
3) That token is replaced throughout the error string

This solves the problem of tokens being in a different order between languages.

This does require some coordination, but it makes sense once you bang your head on your desk a few times ;-)

Richard Childress
Tuesday, April 09, 2002

Sorry that should be :

1) i iterate through the arguments, starting with 0
2) build a string token like "%" + (i + 1), giving %1 in the first iteration.
3) That token is replaced throughout the error string

Richard Childress
Tuesday, April 09, 2002

If you use MS Visual C++ with MFC, use CString::FormatMessage. Otherwise, check if ::FormatMessage is what you need.

Hans-Georg Ulrich
Tuesday, April 09, 2002

I just want to raise my hand in favor of the xml solution outlined above. We use a similar system for generating our error messages and it saves us an incredible amount of time in finding and fixing little changes that need to be made.

I just wish that we had used this type of xml solution for all of the strings in our software (not just the error messages).

Benji Smith
Wednesday, April 10, 2002

*  Recent Topics

*  Fog Creek Home