Using proper culture with C# string interpolation

Once you get used to string interpolation feature in C# it is easy to get addicted to it. However, we need to keep in mind the fact that the current system culture matters. Consider we are building a code that communicates with JavaScript – for example in Web Assembly – and needs to execute a function that takes in a decimal number as an argument:

https://gist.github.com/MartinZikmund/950f700fbe2d81f3b4580b13430907b9

As simple as our sample looks, it contains a remarkable bug. Why?

String interpolation was built as an alternative to the string.Format method. In fact, in the generated IL we can see it actually evaluates to string.Format call:

https://gist.github.com/MartinZikmund/9caa0c28edf97e77a1e589632cf86f76

The output of string.Format depends on the CultureInfo.CurrentCulture and so does string interpolation’s output. Hence, when you run this code on a PC with the en-US (American English) culture, it evaluates to:

https://gist.github.com/MartinZikmund/0bdb03a4057804265b33d82b8d20ce59

But running on a PC with cs-CZ (Czech) culture we suddenly get:

https://gist.github.com/MartinZikmund/9fe4d2a4ea6994911fbe6efd3b4742fc

The decimal comma would certainly be appropriate when displayed to the user. In our case, however, this would mean a call to the myFunction function with two arguments – 1 and 2!

We could use string.Format directly to solve this problem, as it allows us to specify a IFormatProvider to control formatting. But can we keep using an interpolated string and evaluate it to a machine-friendly representation?

Solution

As I have mentioned, interpolated string in most cases evaluates to a plain old string.Format call. However, when needed, the syntax can evaluate to an instance of System.FormattableString. This helpful class essentially wraps the interpolated string and gives read-only access to the format string, its arguments, and it also offers a static Invariant method. This method takes a single FormattableString argument (your interpolated string) and evaluates it according to the Invariant culture (CultureInfo.InvariantCulture):

https://gist.github.com/MartinZikmund/c3f47109c53684fa2cfe06299c7de424

That is ideal, as invariant culture always stays the same, so we can rest assured that the interpolated string evaluation will always result in:

https://gist.github.com/MartinZikmund/0bdb03a4057804265b33d82b8d20ce59

Tip: FormattableString.Invariant is a mouthful. If you use it often, you can utilize the using static feature, which was introduced in C# 6 and then reference the method directly as Invariant:

https://gist.github.com/MartinZikmund/5bbf53a0b95c6cef5aeb7affc03405a4

Specifying culture

You might be wondering now if there is a method that would allow us to use a specific culture to use when evaluating an interpolated string. The first attempt might be to just do the following:

https://gist.github.com/MartinZikmund/08acd275a036deab553bb89c7f56f33a

Unfortunately, this does not work as we expect. Because we are not explicitly casting the interpolated string to FormattableString, it gets directly evaluated to string.Format, so we are then just calling ToString(usCulture) on the resulting string. And string.ToString implementations are in fact just no-ops:

https://gist.github.com/MartinZikmund/67a44a8c86ef36f4653dcb92701f92a6

So we need to do a small side-step instead:

https://gist.github.com/MartinZikmund/08507123b24d807a164bbc829745aba7

By explicitly storing the interpolated string in a FormattableString variable, we can now call the FormattableString.ToString(IFormatProvider) implementation, which actually does what we need – evaluates the interpolation under the given culture.

Source code

Example source code for this blog post is available on my GitHub. It showcases all discussed options in a single .NET Core 3.0 console app:

Invariant culture sample
Invariant culture sample

Summary

Interpolated strings in C# are handy, but we have to ensure that they are evaluated correctly according to the context where the resulting string is presented. If we are displaying it to the user, the default behavior of using CultureInfo.CurrentCulture is probably most appropriate. On the other hand, in machine-to-machine communication, we should always make sure to use invariant culture using FormattableString.Invariant.

Buy me a coffeeBuy me a coffee

2 thoughts on “Using proper culture with C# string interpolation”

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.