views:

101

answers:

2

I have this formatted string that I am having a translator work on.

ENGLISH

"Check out the %1$@ %2$@ in %3$@: %4$@" = "Check out the %1$@ %2$@ in %3$@: %4$@"

GERMAN TRANSLATION

"Check out the %1$@ %2$@ in %3$@: %4$@" = "Hör Dir mal %2$@ in %3$@ an: %4$@";

These are passed to a [NSString stringWithFormat:] call:

//////////////////////////////////////
// Share Over Twitter
NSString *frmt = NSLocalizedString(@"Check out the %1$@ %2$@ in %3$@: %4$@", @"The default tweet for sharing sounds. Use %1$@ for where the sound type (Sound, mix, playlist) will be, %2$@ for where the audio name will be, %3$@ for the app name, and %3$@ for where the sound link will be.");
NSString *urlString = [NSString stringWithFormat:@"sounds/%@", SoundSoundID(audio)];
NSString *url = ([audio audioType] == UAAudioTypeSound ? UrlFor(urlString) : APP_SHORTLINK);
NSString *msg = [NSString stringWithFormat:
                 frmt,
                 [[Audio titleForAudioType:[audio audioType]] lowercaseString],
                 [NSString stringWithFormat:@"\"%@\"", AudioName(audio)],
                 APP_NAME, 
                 url];
returnString = msg;

With the desired and actual outcome of:

ENGLISH

desired: "Check out the sound "This Sound Name" in My App Name: link_to_sound"
actual:  "Check out the sound "This Sound Name" in My App Name: link_to_sound"

GERMAN

desired: "Hör Dir mal "This Sound Name" in My App Name an: link_to_sound"
actual:  "Hör Dir mal sound in "This Sound Name" an: My App Name"



THE PROBLEM The problem is that I was under the assumption that by using numbered variable in the -[NSString stringWithFormat:], I could do things like this, where the %1$@ variable is completely omitted. If you notice, the German translation of the format string does not use the first argument (%1$@) at all but it ("sound") still appears in the output string.

What am I doing wrong?

A: 

Looks like a bug to me. I think you should file a bug.

CFString's formatting engine is independent from fprintf's so there could be some differences. For instance,

printf("a %3$s\n", "b", "c", "d"); // prints "a d"
NSLog(@"a %3$s\n", "b", "c", "d"); // prints "a b"

You need to supply all previous specifiers because the width of an argument doesn't need to be fixed, e.g

printf("%2$llx %1$llx\n", 1LL, 2LL); // prints "2 1"
printf("%2$llx\n", 1LL, 2LL);        // prints "200000000" !!
NSLog(@"%2$llx %1$llx\n", 1LL, 2LL); // prints "2 1"
NSLog(@"%2$llx\n", 1LL, 2LL);        // prints "1"

iPhone OS's printf skips 4 bytes on 1 missing specifier, and CFString's formatter skips 0 bytes.


The solutions are:

  1. Rearrange your indices, e.g.

    "Check out the %4$@ %1$@ in %2$@: %3$@"
    "Hör Dir mal %1$@ in %2$@ an: %3$@";
    

    or

  2. use the format

    [@"%1$10p%2$10p%3$10p%4$10p" stringByAppendingString:frmt]
    

    to force all arguments to be used, and then chop out the first 40 characters with -substringFromIndex:, or

  3. Convert all ObjC objects into C strings (char*) and use snprintf.

  4. Write your own formatting engine.
KennyTM
I was hoping to avoid these workarounds and sending the files back to the translators. booooo
coneybeare
Can you explain what number 2 is doing?
coneybeare
I have tried to implement a 5th option, here: http://stackoverflow.com/questions/2946649/nsstringwithformat-swizzled-to-allow-missing-format-numbered-args which is essentially like number 4, but not so much of my own engine as an override
coneybeare
@coneybeare: Option 2 forces the formatting mechanism to parse at least 4 arguments (so that it knows that there are four there), however, don't do this, the IEEE Std 1003.1 specifically says you cannot mix numbered and unnumbered arguments.
dreamlax
+1  A: 

This is not a bug. Numbered arguments are not part of the C standard, but part of IEEE Std 1003.1, which says the following (emphasis mine):

The format can contain either numbered argument conversion specifications (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications (that is, % and * ), but not both. The only exception to this is that %% can be mixed with the "%n$" form. The results of mixing numbered and unnumbered argument specifications in a format string are undefined. When numbered argument specifications are used, specifying the Nth argument requires that all the leading arguments, from the first to the (N-1)th, are specified in the format string.
dreamlax
it appears what I was doing wrong is making an assumption about the omission of numbered variables.
coneybeare