I suggest you ...

Support String Interpolation in Resources Project Items

I think that it would greatly simplify internationalization efforts if Resources project items would support the notion of interpolation of values. This would build on the existing support for String resources by adding metadata to the RESX file format to describe the names and types of the expected values for the argument holes. The generated designer class would use that metadata to construct methods which would accept those values as arguments and use composite formatting to build the final string.

For example, an interpolated String resource could be declared as follows:

Name: Greetings
Value: "Hello {name}!"

When this String resource is selected the Properties window would display a single property with the name "name" and allow the developer to specify the .NET type, defaulting to System.String. Both the format string and the expected argument metadata would be encoded into the RESX file. This could be done by either extending the RESX format (assuming that the tools can ignore elements that they don't expect) or by encoding a helper type which would represent the format string and the argument metadata combined.

The ResXFileCodeGenerator would then be extended to interpret these String resources with additional metadata in order to generate methods instead of the properties that are generated currently. For example, for the simple example above the generated method would be:

internal static string Greetings(global::System.String name)
{
return String.Format(ResourceManager.GetString("Greetings", resourceCulture), name);
}

Then consumers of the Resources class can simply call the method passing the required arguments in order to return a localized runtime-interpolated string:

string greetings = Resources.Greetings("World");

38 votes
Vote
Sign in
(thinking…)
Sign in with: facebook google
Signed in as (Sign out)
You have left! (?) (thinking…)
Halo_Four shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →

3 comments

Sign in
(thinking…)
Sign in with: facebook google
Signed in as (Sign out)
Submitting...
  • Anonymous commented  ·   ·  Flag as inappropriate

    Not sure it's possible because {name} is taken from the context of the interpolation.
    To make it usable, the .resx file should be translated into a method call with parameters I guess.

  • Halo_Four commented  ·   ·  Flag as inappropriate

    Thinking about the pluralization problem further it would probably be better to drive the entire resource selection from one of the specified integral values rather than trying to fill specific arguments. I still think that this should be done silently to the consuming developer so that they don't have to write any additional code, so to the outside it would appear as a single resource regardless of how many potential string resources may be in play, which may change depending on the locale.

    For example, in en-US:

    [0] - "Completely out of products!"
    [1] - "There is one product remaining."
    [*] - "There are {count} products remaining."

    And in ru-RU:

    [0] - "Продано!"
    [1] - "Только один продукт налево."
    [2] - "Есть {count} продукты оставшиеся."
    [3] - "Есть {count} продукты оставшиеся."
    [4] - "Есть {count} продукты оставшиеся."
    [*] - "Есть {count} продуктов осталось."

    The generated method would be something like the following:

    internal static string RemainingProducts(global::System.Int32 count)
    {
    PluralFormDictionary pluralForms = (PluralFormDictionary) ResourceManager.GetObject("RemainingProducts$pluralized", resourceCulture);
    return String.Format(pluralForms.GetString(count, resourceCulture), count);
    }

    The consumer would then just call the following:

    string remainingLabel = Resources.RemainingProducts(count);

    Apologies if I totally botched the Russian translations. They are from Google Translate.

  • Halo_Four commented  ·   ·  Flag as inappropriate

    Additionally I believe that it would be very useful to support the notion of pluralized forms within the interpolated resource String. This would be accomplished by associating an argument hole of type System.String with another argument hole of one of the integral types (e.g. System.Int32) and then allowing the developer to specify a mapping of integral values with different representations of that String. The mapping would be encoded into the RESX file using a specially-encoded name in order to prevent direct editing in the designer as well as generated code members.

    For example, take the following String resource:

    Name: ProductsRemaining
    Value: "There are {count} {product} remaining."

    The metadata for the count argument hole would be set to System.Int32 and the metadata for the product argument hole would be set to System.String. The product argument hole would then be set to a plural form associated with the count argument hole. In the designer the developer can then add the following forms through the Collection editor of the properties window:

    [1] - "product"
    [*] - "products"

    This would specify that if the value passed to the count argument is 1 then the plural form of the products argument hole would be "product", otherwise the plural form of the products argument hole would be "products."

    The generated method for this resource String would appear something like the following:

    internal static string RemainingProducts(global::System.Int32 count)
    {
    PluralFormDictionary products = (PluralFormDictionary) ResourceManager.GetObject("RemainingProducts$products", resourceCulture);
    return String.Format(ResourceManager.GetString("RemainingProducts", resourceCulture), count, products[count]);
    }

    Then consumers of this resource can use it simply as follows:

    string remainingLabel = Resources.RemainingProducts(count);

    When internationalizing both the format string and the plural forms would have to be easily changed, including adding new integral mappings for plural forms. For example, in Russian the plural forms might be as follows:

    [1] - "продукт"
    [2] - "продукты"
    [3] - "продукты"
    [4] - "продукты"
    [*] - "продуктов"

    Granted I understand that this might not be a great example as the "remaining" portion of this message may also be affected by the quantity which is an inherently tricky aspect of internationalization in general.

Feedback and Knowledge Base