How to include Variables in Localized Strings? - c#

I'm trying to display a message to the user along the lines of:
"User 5 could not be added"
But how can I add variables to a string that is being placed in a .resx file? I've trying searching for things like "Variables in Localization" "Globalization with Variables" etc, but came up dry.
If I weren't localizing I would write:
Console.Write("User " + userNum + " could not be added");
How can this be accomplished with resources?

You can't do this directly.
What you can do is place a token - a specific string that can be replaced with string.Replace with the value of the variable.
A good candidate for this would be the built in string formatting:
Console.Write(string.Format("User {0} could not be added", userNum));
Assuming userNum has the value 5, the result would be:
User 5 could not be added
You can localize this string with the composite format specifiers.

In teams where I've done internationalization, we generally also created a resource for the format string, something like USER_COULD_NOT_BE_ADDED_FORMAT, and called String.Format (or your environment's equivalent) by passing that resource's value as the format pattern.
Then you'll do Console.Write(String.Format(resourceManager.GetString("USER_COULD_NOT_BE_ADDED_FORMAT"), userNum));
Most localizers either have training in the format strings used by the system they are localizing, or they are provided with guidance in the localization kit that you provide them. So this is not, for example, as high a barrier as making them modify code directly.
You generally need to add a loc comment to the resource ID to explain the positional parameters.

Use Composite Formatting like so:
Console.Write("User {0} could not be added", userNum);
This way you would localize "User {0} could not be added".

you can do that its simple
new lets see how
String.Format(Resource_en.PhoneNumberForEmployeeAlreadyExist,letterForm.EmployeeName[i])
this will gave me dynamic message every time
by the way I'm useing ResXManager

I would use string.Format
http://msdn.microsoft.com/en-us/library/system.string.format.aspx
Console.Write(string.Format("User {0} could not be added", userNum));

Related

Localization with string.Format

Currently I am mostly doing the localization by putting key-value pairs into a Resources.resw file. So I wonder about how I should localize strings that need formatting or say strings with different grammar orders in different languages. It might be easier to understand what I mean with the examples below.
For example, just as this part in the official document for localization suggests, one language can have the date format of
string.Format("Every {0} {1}", monthName, dayNumber);
while the other uses
string.Format("Every {1} {0}", monthName, dayNumber);
In this situation, what is the best way to localize such a string?
Things/Grammars can be way more complicated than this example. The suggestion in the official document doesn't look good to me because a date can be unpredictable. Or may be you can enumerate the date, but that requires a lot of work. Or let's say we have a string that takes user input, like
string.Format("Do you want to delete {name}?", name);
In another language it might have this grammar order
string.Format("You want to delete {name} do?", name);
It is impossible to localize the whole sentence as the example suggests in the document.
The only way of avoiding situation that I can think of is not to put user input....
If you have access to the date you could use The Month ("M", "m") Format Specifier
From the documentation:
DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);
Console.WriteLine(date1.ToString("m",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays April 10
Console.WriteLine(date1.ToString("m",
CultureInfo.CreateSpecificCulture("ms-MY")));
// Displays 10 April
For string.Format("Do you want to delete {name}?", name); you can
$"Do you want to delete the following user? '{name}'";
One way I just found out is to put this key-value pair into the Resources.resw:
Key: RemoveText
Value: Do you want to delete {0}?
After you get the localized string like doing
var msg = Localize('RemoveText');
Then
var result = string.Format(msg, name)
can give you the expected result.
Basically, you need to put {0} appropriately in every language. The only flaw of this solution is that {0} should not be allowed in the user input.
If you still want {0} to appear, you can change {0} to other strings that you think are too complicated and long for user to type in, for example '{usersAreVeryUnlikelyToTypeInThisInTheirInputs}'. And then use
msg.replace('{usersAreVeryUnlikelyToTypeInThisInTheirInputs}', name)
to get the localized string.

string format custom parameter

Is there a (simple/predefined) way to use the same string.format mechanism but use names instead of numbers? I want to use the same formatting system but make it more end-user friendly. Right now my users are putting in {0} {1:Minutes} after selecting what parameters they want to use from a list but it becomes quite unreadable and confusing once you start using more then say 2 parameters.
For example want to do {Now} and {Now:Minutes} and have it replaced with DateTime.Now for {Now} and DateTime.Now.Minutes using the IFormattable for {Now:Minutes}. So that the users won't need to select items from a list anymore but rather just type/insert the names of the parameters.
In c#-6.0 you can use
string result = $"Time: {DateTime.Now}";
On previous versions see
How to replace tokens on a string template?

Varying filepaths in C#

I am creating C# Windows Form that retrieves files from shared drives as email attachments. I am trying to automate the file retrieval process, but the filepaths available to me vary according to the date. For example:
V:\....\Dec-03\filename12-3-2013.xml
J:\.....\December\filename12-4-2013
I have the filepath stored as string from a textbox, but since the path varies slightly day-to-day, I've been trying to figure out how automate this process. In the past I've used VBA code where I've concatenated method calls into the string like this
"..." & Day(Date) & "..."
(I replaced the ampersand with the plus sign of course for C#)
But this just gets me an illegal characters in path Argument exception.
I am using a check for filedate and taking a a specific filepath through a textbox. I want particular files that are being updated in monthly folders and the filename contains a date. I want to grab the ones with today's date or yesterday's date, but some have no date in the filename or directory at all. Since there isn't a lot of consistency, I would love to enter code
"+ DateTime.Now.ToString() +"
in the textbox per individual filepath as I load them via the form and have the program execute like I've done with some VBA code, but I get Illegal characters with the double quotes in the middle of a filepath. Is there some work around or will I need to create fixes for every particular pattern?
Use System.IO.Path.Combine(...) to handle chaining directories together (it takes care of extra slashes for you). In your combine, use String.Format(SomeFormatString, token1value, toke2value, etc.) to give you the name you were wanting.
C# uses + to append strings instead of & in older VB.
"My Date: " + DateTime.Now.ToString("MM/dd/yyyy")
An example of this with the String.Format I showed above would be
string.Format("My Date: {0}", DateTime.Now.ToString("MM/dd/yyyy"))
If I'm following what you're saying about Day(Date), you might try some thing like this in C#:
MyObject.SomeMethod("some string " + dateValue.ToString("ddd") + " more string data");
Where dateValue is a DateTime object and the "ddd" parameter tells the ToString method to return a three character abbreviation of the day of the week (e.g. 'Wed').
For more information on using ToString with DateTime objects to extract various parts of the date, see
http://msdn.microsoft.com/en-us/library/bb762911(v=vs.100).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2
The ToString overload of the DateTime structure will allow you to format a date as the Month name, etc.
var x = DateTime.Today.ToString("MMMM"); // December
You can include other characters in the format string as well, for example to get Dec-19 you can use:
var x = DateTime.Today.ToString("MMM-dd"); // Dec-19
TyCobb's answer covers combining the formatted date into a path using Path.Combine (which I generally recommend).
You can also use String.Format to insert a formatted value into a string, which is often easier to read and leads to fewer mistakes. For example, to generate your first example, you could use the following:
var path =
String.Format("V:....\{0:MMM-dd}\filename{0:M-d-yyyy}.xml", DateTime.Today);

Format a Resource string into another one?

What would be the best way to accomplish something like this?
Suppose I have the following pair of Resource strings.
BadRequestParameter: Potential bad request aborted before execution.
RequiredParameterConstraint: {0} parameter requires a value. {1}
And suppose I want to set {1} on the second one, to the value of BadRequestParameter. I could easily do that using string.Format. But now suppose I have lots of Resource strings like the second one, all of which include some other Resource string in them.
What would be the best way to code this? Is using string.Format repeateadly in each case really all that I can do?
Update
I'll try to explain myself better. These are the resource strings I actually have:
BadRequestParameter Potential bad request aborted before execution.
EmptyVector Vectorized requests require at least one element. {0}
OverflownVector Vectorized requests can take at most one hundred elements. {0}
RequiredParamConstraint {0} parameter requires a value. {1}
SortMinMaxConstraint {0} parameter value '{1}' does not allow Min or Max parameters in this query. {2}
SortRangeTypeConstraint Expected {0} parameter Type '{1}'. Actual: '{2}'. {3}
SortValueConstraint {0} parameter does not allow '{1}' as a value in this query. {2}
I'd like to avoid writing the string in BadRequestParameter at the end of each of those lines. Therefore, I added a format at the end of those strings. The issue now is that I'd like to somehow auto-reference {x} to BadRequestParameter, in order to avoid having to make calls like
string.Format(Error.EmptyVector, Error.BadRequestParameter);
I have lots of Resource strings like the second one, all of which include some other Resource string in them.
Instead of storing pre-made format strings ready for use, you could store raw material for building real format strings, and add code to expand them pro grammatically before use. For example, you could store strings like this:
BadRequestParameter: Potential bad request aborted before execution.
SupportNumber: (123)456-7890
CallTechSupport: You need to call technical support at {SupportNumber}.
RequiredParameterConstraint: {{0}} parameter requires a value. {BadRequestParameter} {CallTechSupport}
Of course passing these strings to string.Format as-is is not going to work. You need to parse these strings, for example with RegExps, and find all instances where you have a word between curly braces, instead of a number. You could then replace each word with its sequence number, and produce an array of parameters based on the names that you find between curly braces. In this case, you will get these two values (pseudocode):
formatString = "{{0}} parameter requires a value. {0} {1}";
// You replaced {BadRequestParameter} with {0} and {CallTechSupport} with {1}
parameters = {
"Potential bad request aborted before execution."
, "You need to call technical support at (123)456-7890."
};
Note: Of course, producing this array of parameters required recursion.
At this point, you can invoke string.Format to produce your final string:
var res = string.Format(formatString, parameters);
This returns the string that has resource strings pre-replaced for your callers:
"{0} parameter requires a value. Potential bad request aborted before execution. You need to call technical support at (123)456-7890."
The callers can now use this string for formatting, without bothering with other resource values.
Yes :-) unless you want to make a helper method that is shorter, but that would really just be for convenience sake
public static string f(string format, params object[] p)
{
return string.Format(format, p);
}
IF you treat the argument indicators {#} as wild cards then why would it make sense for you to pre-fill them inside of your resource.
I see absolutely nothing wrong with
String.Format(RequiredParamterConstraint, "something", BadRequestParameter);

Why use String.Format? [duplicate]

This question already has answers here:
String output: format or concat in C#?
(32 answers)
Closed 9 years ago.
Why would anyone use String.Format in C# and VB .NET as opposed to the concatenation operators (& in VB, and + in C#)?
What is the main difference? Why are everyone so interested in using String.Format? I am very curious.
I can see a number of reasons:
Readability
string s = string.Format("Hey, {0} it is the {1}st day of {2}. I feel {3}!", _name, _day, _month, _feeling);
vs:
string s = "Hey," + _name + " it is the " + _day + "st day of " + _month + ". I feel " + feeling + "!";
Format Specifiers
(and this includes the fact you can write custom formatters)
string s = string.Format("Invoice number: {0:0000}", _invoiceNum);
vs:
string s = "Invoice Number = " + ("0000" + _invoiceNum).Substr(..... /*can't even be bothered to type it*/)
String Template Persistence
What if I want to store string templates in the database? With string formatting:
_id _translation
1 Welcome {0} to {1}. Today is {2}.
2 You have {0} products in your basket.
3 Thank-you for your order. Your {0} will arrive in {1} working days.
vs:
_id _translation
1 Welcome
2 to
3 . Today is
4 .
5 You have
6 products in your basket.
7 Someone
8 just shoot
9 the developer.
Besides being a bit easier to read and adding a few more operators, it's also beneficial if your application is internationalized. A lot of times the variables are numbers or key words which will be in a different order for different languages. By using String.Format, your code can remain unchanged while different strings will go into resource files. So, the code would end up being
String.Format(resource.GetString("MyResourceString"), str1, str2, str3);
While your resource strings end up being
English: "blah blah {0} blah blah {1} blah {2}"
Russian: "{0} blet blet blet {2} blet {1}"
Where Russian may have different rules on how things get addressed so the order is different or sentence structure is different.
First, I find
string s = String.Format(
"Your order {0} will be delivered on {1:yyyy-MM-dd}. Your total cost is {2:C}.",
orderNumber,
orderDeliveryDate,
orderCost
);
far easier to read, write and maintain than
string s = "Your order " +
orderNumber.ToString() +
" will be delivered on " +
orderDeliveryDate.ToString("yyyy-MM-dd") +
"." +
"Your total cost is " +
orderCost.ToString("C") +
".";
Look how much more maintainable the following is
string s = String.Format(
"Year = {0:yyyy}, Month = {0:MM}, Day = {0:dd}",
date
);
over the alternative where you'd have to repeat date three times.
Second, the format specifiers that String.Format provides give you great flexibility over the output of the string in a way that is easier to read, write and maintain than just using plain old concatenation. Additionally, it's easier to get culture concerns right with String.Format.
Third, when performance does matter, String.Format will outperform concatenation. Behind the scenes it uses a StringBuilder and avoids the Schlemiel the Painter problem.
Several reasons:
String.Format() is very powerful. You can use simple format indicators (like fixed width, currency, character lengths, etc) right in the format string. You can even create your own format providers for things like expanding enums, mapping specific inputs to much more complicated outputs, or localization.
You can do some powerful things by putting format strings in configuration files.
String.Format() is often faster, as it uses a StringBuilder and an efficient state machine behind the scenes, whereas string concatenation in .Net is relatively slow. For small strings the difference is negligible, but it can be noticable as the size of the string and number of substituted values increases.
String.Format() is actually more familiar to many programmers, especially those coming from backgrounds that use variants of the old C printf() function.
Finally, don't forget StringBuilder.AppendFormat(). String.Format() actually uses this method behind the scenes*, and going to the StringBuilder directly can give you a kind of hybrid approach: explicitly use .Append() (analogous to concatenation) for some parts of a large string, and use .AppendFormat() in others.
* [edit] Original answer is now 8 years old, and I've since seen an indication this may have changed when string interpolation was added to .Net. However, I haven't gone back to the reference source to verify the change yet.
String.Format adds many options in addition to the concatenation operators, including the ability to specify the specific format of each item added into the string.
For details on what is possible, I'd recommend reading the section on MSDN titled Composite Formatting. It explains the advantage of String.Format (as well as xxx.WriteLine and other methods that support composite formatting) over normal concatenation operators.
There's interesting stuff on the performance aspects in this question
However I personally would still recommend string.Format unless performance is critical for readability reasons.
string.Format("{0}: {1}", key, value);
Is more readable than
key + ": " + value
For instance. Also provides a nice separation of concerns. Means you can have
string.Format(GetConfigValue("KeyValueFormat"), key, value);
And then changing your key value format from "{0}: {1}" to "{0} - {1}" becomes a config change rather than a code change.
string.Format also has a bunch of format provision built into it, integers, date formatting, etc.
One reason it is not preferable to write the string like 'string +"Value"+ string' is because of Localization. In cases where localization is occurring we want the localized string to be correctly formatted, which could be very different from the language being coded in.
For example we need to show the following error in different languages:
MessageBox.Show(String.Format(ErrorManager.GetError("PIDV001").Description, proposalvalue.ProposalSource)
where
'ErrorCollector.GetError("ERR001").ErrorDescription' returns a string like "Your ID {0} is not valid". This message must be localized in many languages. In that case we can't use + in C#. We need to follow string.format.

Categories