How to convert boolean to localized string - c#

There is any way to convert a boolean value to a localized string. I have tried:
var x = true;
var culture = new CultureInfo("en-US")
x.ToString(culture) // returns True
culture = new CultureInfo("pt-BR")
x.ToString(culture) // returns True, expected Verdadeiro
Or should I start typing the switch now to end before 2020?

Well, start typing because it's documented behaviour :)
Boolean.ToString(IFormatProvider)
Remarks
The provider parameter is reserved. It does not participate in the
execution of this method. This means that the
Boolean.ToString(IFormatProvider) method, unlike most methods with a
provider parameter, does not reflect culture-specific settings.

As #Michal pointed out, this is the documented behavior.
If your system supports many languages, you must have some sort of i18 support. Use that to convert a boolean value to string. You can add an extension method like so:
public string ToLocalizedString(this bool b)
{
return ...i18n version of true or false...
}

Related

Translate "True" to "Verdadero" in .NET

As documented in the Boolean.ToString(IFormatProvider) method doc, the IFormatProvider provider does not impact the constant "True/False" output.
Now, is there a way to however translate the "True" to "Verdadero"?
public static void Main(string[] args)
{
//Your code goes here
Console.WriteLine("Hello, world!");
System.Globalization.CultureInfo ci = new CultureInfo("es-ES");
Console.WriteLine(true.ToString(ci));
}
// Hello, world!
// True
There isn't a library-based solution to your question, nor should there be. The reason is that a string representation of System.Boolean is unlikely to be useful for anything but the most trivial of localization. Note that is not the case for floating-point numbers where a culture-specific . or , can be applied when formatting. Dates (System.DateTime) have some localization support from the operating system itself, so .NET is able to build on that; this is not the case for System.Boolean.
Usually, there will be other words in addition to just "True" (or "False"); those words will have to be translated too. And, depending on the language and those other words, you might not be able to do simple string concatenation: string message = baseMessage + b.ToString();
Instead, you should store your strings in resource files and retrieve the right one.
bool b = ...;
string message = b ? Properties.Resources.TrueMessage : Properties.Resources.FalseMessage;
See How to use localization in C# for more details.
As per the docs, Boolean.ToString(IFormatProvider) will not reflect culture specific strings.
However, one workaround could be to create an extension method on the Boolean object:
public static class BoolExtensions
{
public static string ToSpanishString(this bool val)
{
return val ? "Verdadero" : "Falso";
}
}
You can achieve this in the following way:
bool test = true;
Console.WriteLine(test ? "Verdadero" : "Equivocado");
The first value is always the truthy one, the second is the falsy.

Why does TypeConverter not obey Cultures?

I have a CSV Reader, so I have a generic casting method. It it, I do this:
try
{
TypeConverter converter = TypeDescriptor.GetConverter(type);
if (converter != null)
{
result = converter.ConvertFromString(null, culture, value);
return true;
}
result = type.GetDefault();
return true;
}
catch
{
result = type.GetDefault();
return false;
}
However if I pass:
type: int
value: "123.024"
culture: de-DE
The converter always fails and returns 0, instead of correctly treating . as a thousands separator.
Anyone know how to get it to work?
Because the Int32Converter calls Int32.Parse internally, and Int32.Parse does not support thousands separators in the string passed to it.
More specifically, Int32.Parse format described below:
The s parameter contains a number of the form:
[ws][sign]digits[ws]
Items in square brackets ([ and ]) are optional. The following table
describes each element. Element Description
ws Optional white space.
sign An optional sign
digits A sequence of digits ranging from 0 to
9.
The s parameter is interpreted using the NumberStyles.Integer style. In addition to decimal digits, only leading and trailing spaces
together with a leading sign are allowed. To explicitly define the
style elements that can be present in s, use either the
Int32.Parse(String, NumberStyles) or the
Int32.Parse(String, NumberStyles, IFormatProvider) method.
The s parameter is parsed using the formatting information in a NumberFormatInfo object initialized for the current system culture.
For more information, see CurrentInfo. To parse a string using the
formatting information of some other culture, use the
Int32.Parse(String, NumberStyles, IFormatProvider) method.
If you were calling Int32.Parse directly, you could just call the overload that accepts a NumberStyles enum and create a composite value with the flags you want. E.g:
Int32.Parse(value, NumberStyles.Integer | NumberStyles.AllowThousands);
However, neither GetConverter() nor Int32Converter have any means of overriding the default NumberStyles of Int32.Parse, so you will either need a special case for ints or you will have to ensure that the strings passed to this function do not contain thousands separators.

TypeDescriptor does not recongnise date

I'm having an ussue with the TypeDescriptor class.
I have a cookie which contains a date - the date is converted to a string and then back again using some helper methods.
One of my staple extension methods is used to do the conversion, however it throws a forced error because the date is not convertible back from a string.
Here's the message I output:
22/01/2015 14:29:15 could not be converted to DateTime
Looks like a DateTime to me!
The problem can be overcome by using Convert.ToDateTime(), so the code in general is ok. i also use it for dates elwhere with no problems to date.
The only difference is that I'm converting in the middle of a linq statement like this:
Set = new SortedSet<TrackedItem>(set
.Split(';')
.Select(s =>
{
var parts = s.Split(',');
return new TrackedItem(
parts[0].ConvertTo<int>(),
Convert.ToDateTime(parts[1]));
}));
Any ideas?
public static T ConvertTo<T>(this object obj, bool throwInvalid = false)
where T : IConvertible
{
// Object does not require converting.
if (obj is T) return (T)obj;
// Determine if object can be converted.
var type = typeof(T);
var converter = TypeDescriptor.GetConverter(type);
var isConvertible = converter != null && converter.IsValid(obj);
var error = string.Format("'{0}' could not be converted to type {1}", obj, type.Name);
// If no conversion is available, and defaults not allowed throw an error.
(!isConvertible && throwInvalid).ThrowTrue(error);
// If the object is convertible, convert it, else return the default(T).
return isConvertible ? (T)converter.ConvertFrom(obj) : default(T);
}
I'm guessing from your date example that you're running in the en-GB culture. Unfortunately, to draw liberally from this related q/a, IsValid always uses CultureInfo.InvariantCulture (US date format) to decide its answer. So when running in en-GB with a date such as your example, IsValid will return false; but ConvertFrom, which by default uses the current thread culture will succeed!
Interestingly, the latest docs for IsValid massively hedge the question of whether this is actually a bug:
The IsValid method is used to validate a value within the type
rather than to determine if value can be converted to the given type.
For example, IsValid can be used to determine if a given value is
valid for an enumeration type.
So really you shouldn't be using IsValid here at all - you should be doing what the same docs go on to suggest:
You can write your own WillConvertSucceed method by wrapping the
ConvertTo and ConvertFrom methods in exception blocks.
And in that method you can be sure to use the CultureInfo you actually care about.

Why String.Equals is returning false?

I have the following C# code (from a library I'm using) that tries to find a certificate comparing the thumbprint. Notice that in the following code both mycert.Thumbprint and certificateThumbprint are strings.
var certificateThumbprint = AppSettings.CertificateThumbprint;
var cert =
myStore.Certificates.OfType<X509Certificate2>().FirstOrDefault(
mycert =>
mycert.Thumbprint != null && mycert.Thumbprint.Equals(certificateThumbprint)
);
This fails to find the certificate with the thumbprint because mycert.Thumbprint.Equals(certificateThumbprint) is false even when the strings are equal. mycert.Thumbprint == certificateThumbprint also returns false, while mycert.Thumbprint.CompareTo(certificateThumbprint) returns 0.
I might be missing something obvious, but I can't figure out why the Equals method is failing. Ideas?
CompareTo ignores certain characters:
static void Main(string[] args)
{
var a = "asdas"+(char)847;//add a hidden character
var b = "asdas";
Console.WriteLine(a.Equals(b)); //false
Console.WriteLine(a.CompareTo(b)); //0
Console.WriteLine(a.Length); //6
Console.WriteLine(b.Length); //5
//watch window shows both a and b as "asdas"
}
(Here, the character added to a is U+034F, Combining Grapheme Joiner.)
So CompareTo's result is not a good indicator of a bug in Equals. The most likely reason of your problem is hidden characters. You can check the lengths to be sure.
See this for more info.
You may wish to try using an overload of String.Equals that accepts a parameter of type StringComparison.
For example:
myCert.Thumbprint.Equals(certificateThumbprint, StringComparison.[SomeEnumeration])
Where [SomeEnumeration] is replaced with one of the following enumerated constants:
- CurrentCulture
- CurrentCultureIgnoreCase
- InvariantCulture
- InvariantCultureIgnoreCase
- Ordinal
- OrdinalIgnoreCase
Reference the MSDN Documentation found here.
Sometimes when we insert data in database it stores some spaces like "question ". And when you will try to compare it with "question" it returns false. So my suggestion is: please check the value in database or use Trim() method.
In your case, please try:
mycert.Thumbprint != null && mycert.Thumbprint.trim().equals(certificateThumbprint.trim())
I think it will return true if any record will exist.

String StartsWith() issue with Danish text

Can anyone explain this behaviour?
var culture = new CultureInfo("da-DK");
Thread.CurrentThread.CurrentCulture = culture;
"daab".StartsWith("da"); //false
I know that it can be fixed by specifying StringComparison.InvariantCulture. But I'm just confused by the behavior.
I also know that "aA" and "AA" are not considered the same in a Danish case-insensitive comparision, see http://msdn.microsoft.com/en-us/library/xk2wykcz.aspx. Which explains this
String.Compare("aA", "AA", new CultureInfo("da-DK"), CompareOptions.IgnoreCase) // -1 (not equal)
Is this linked to the behavior of the first code snippet?
Here a test that illustrates the problem, daab og dåb (same word in old and modern language respectively) means baptism/christening.
public class can_handle_remnant_of_danish_language
{
[Fact]
public void daab_start_with_då()
{
var culture = new CultureInfo("da-DK"); Thread.CurrentThread.CurrentCulture = culture;
Assert.True("daab".StartsWith("då")); // Fails
}
[Fact]
public void daab_start_with_da()
{
var culture = new CultureInfo("da-DK"); Thread.CurrentThread.CurrentCulture = culture;
Assert.True("daab".StartsWith("da")); // Fails
}
[Fact]
public void daab_start_with_daa()
{
var culture = new CultureInfo("da-DK"); Thread.CurrentThread.CurrentCulture = culture;
Assert.True("daab".StartsWith("daa")); // Succeeds
}
[Fact]
public void dåb_start_with_daa()
{
var culture = new CultureInfo("da-DK"); Thread.CurrentThread.CurrentCulture = culture;
Assert.True("dåb".StartsWith("daa")); // Fails
}
[Fact]
public void dåb_start_with_da()
{
var culture = new CultureInfo("da-DK"); Thread.CurrentThread.CurrentCulture = culture;
Assert.True("dåb".StartsWith("da")); // Fails
}
[Fact]
public void dåb_start_with_då()
{
var culture = new CultureInfo("da-DK"); Thread.CurrentThread.CurrentCulture = culture;
Assert.True("dåb".StartsWith("då")); // Succeeds
}
}
All the above tests should be successfull with my understanding of the language, and im danish!
I aint got no degree in grammar though. :-)
Seems like a bug to me.
Like Nappy said, its a feature of the danish language, where "aa" and "å" is still the same. Danish got another two letters, æ and ø, but I am not sure if they can be written using two letters as well.
I think in the second example "aA" is not changed while "AA" is changed to "Å". Just to confuse things even more, "Aa" is considered equal to "AA" and "aa" only when using case-insensitive comparing.
The modern spelling of "baptism" in Danish, namely dåb, is certainly not considered to start with da, for a Danophone. If daab is supposed to be an old-fashioned spelling of dåb, it is a bit philosophical whether it starts with da or not. But for (modern) collation purposes, it does not (alphabetically, such daab goes after disk, not before).
However, if your string is not supposed to represent natural language, but is instead some kind of technical code, like hexadecimal digits, surely you do not want to use any culture-specific rules. The solution here is not to use the invariant culture. The invariant culture has (English) rules itself!
Instead, you want to use ordinal comparison.
Ordinal comparison simply compares the strings char by char, without any assumptions of what sequences are "equivalent" in some sense. (Technical remark: Each char is a UTF-16 code unit, not a "character". Ordinal comparison is ignorant of the rules of Unicode normalization.)
I think the confusion arises because, by default, some string methods use a culture-aware comparison, and other string methods use the ordinal comparison.
The following examples all use a culture-aware comparison:
"Straße".StartsWith("Strasse", StringComparison.CurrentCulture)
"Straße".Equals("Strasse", StringComparison.CurrentCulture)
"ne\u0301e".StartsWith("née", StringComparison.CurrentCulture)
"ne\u0301e".Equals("née", StringComparison.CurrentCulture)
"Straße".StartsWith("Strasse") // CurrentCulture is default for 'StartsWith'!
"ne\u0301e".StartsWith("née") // CurrentCulture is default for 'StartsWith'!
Each of the above may depend on the .NET version as well! (As an example, the first one gives true if the current culture is the invariant culture and you are under .NET Framework 4.8; but it gives false if the current culture is the invariant culture and you use .NET 6.)
But these examples use ordinal comparison:
"Straße".StartsWith("Strasse", StringComparison.Ordinal)
"Straße".Equals("Strasse", StringComparison.Ordinal)
"ne\u0301e".StartsWith("née", StringComparison.Ordinal)
"ne\u0301e".Equals("née", StringComparison.Ordinal)
"Straße".Equals("Strasse") // Ordinal is default for 'Equals'!
"ne\u0301e".Equals("née") // Ordinal is default for 'Equals'!
So remember to check what the default comparison is for the string method you use, and specify the opposite one if needed. (Or always specify the comparison, even when redundant, if you prefer.)

Categories