C# 6.0
string interpolation
localization
programming
.NET

C6.0 string interpolation localization

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

C# 6.0 string interpolation ($"Hello {name}") produces a culture-invariant string by default, which causes localization problems. Numbers, dates, and currencies get formatted using CultureInfo.CurrentCulture for the embedded expressions, but there is no built-in way to swap the interpolated string's template for a translated version. To localize properly, you need FormattableString to control culture formatting, and a separate localization system (resource files, IStringLocalizer) for the translated text itself.

String Interpolation Basics

csharp
1string name = "Alice";
2int age = 30;
3decimal price = 1234.56m;
4
5// C# 6.0+ interpolated string
6string message = $"Name: {name}, Age: {age}, Price: {price:C}";
7// "Name: Alice, Age: 30, Price: $1,234.56" (en-US)

The $"" syntax is syntactic sugar for string.Format():

csharp
// These are equivalent:
string a = $"Hello {name}, you are {age}";
string b = string.Format("Hello {0}, you are {1}", name, age);

The Localization Problem

Interpolated strings format numbers and dates using the current thread culture, but the template itself is hardcoded:

csharp
1decimal price = 1234.56m;
2DateTime date = new DateTime(2025, 3, 15);
3
4// On a German system (de-DE):
5Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
6string msg = $"Price: {price:C}, Date: {date:d}";
7// "Price: 1.234,56 €, Date: 15.03.2025"
8
9// On a US system (en-US):
10Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
11string msg = $"Price: {price:C}, Date: {date:d}";
12// "Price: $1,234.56, Date: 3/15/2025"

The number and date formatting respects culture, but "Price: " and "Date: " remain in English. For full localization, you need to translate the template too.

FormattableString for Culture Control

When you assign an interpolated string to FormattableString, C# preserves the format and arguments separately:

csharp
1FormattableString fs = $"Price: {price:C}, Date: {date:d}";
2
3// Format with a specific culture
4string german = fs.ToString(CultureInfo.GetCultureInfo("de-DE"));
5// "Price: 1.234,56 €, Date: 15.03.2025"
6
7string us = fs.ToString(CultureInfo.GetCultureInfo("en-US"));
8// "Price: $1,234.56, Date: 3/15/2025"
9
10// Always use invariant culture (for logging, serialization)
11string invariant = FormattableString.Invariant($"Value: {price}");
12// "Value: 1234.56" (always dot decimal separator)

FormattableString.Invariant()

For data that must never be culture-dependent (API calls, file paths, SQL):

csharp
1decimal lat = 48.8566m;
2decimal lon = 2.3522m;
3
4// WRONG — on de-DE, produces "48,8566" with comma
5string url = $"https://api.example.com/geo?lat={lat}&lon={lon}";
6
7// CORRECT — always uses dot separator
8string url = FormattableString.Invariant(
9    $"https://api.example.com/geo?lat={lat}&lon={lon}");
10// "https://api.example.com/geo?lat=48.8566&lon=2.3522"

Localization with Resource Files

For translating the text template, use .resx resource files:

xml
1<!-- Resources/Messages.resx (English, default) -->
2<data name="PriceMessage" xml:space="preserve">
3  <value>The price is {0:C} as of {1:d}</value>
4</data>
5
6<!-- Resources/Messages.de.resx (German) -->
7<data name="PriceMessage" xml:space="preserve">
8  <value>Der Preis beträgt {0:C} am {1:d}</value>
9</data>
csharp
1// Use string.Format with the localized template
2decimal price = 1234.56m;
3DateTime date = DateTime.Now;
4
5string template = Resources.Messages.PriceMessage;
6string message = string.Format(
7    CultureInfo.CurrentCulture,
8    template,
9    price,
10    date
11);
12// German: "Der Preis beträgt 1.234,56 € am 15.03.2025"
13// English: "The price is $1,234.56 as of 3/15/2025"

Note: You cannot use $"" interpolation with resource strings because the template must come from the resource file at runtime, not compile time.

ASP.NET Core IStringLocalizer

csharp
1// In ASP.NET Core
2public class HomeController : Controller
3{
4    private readonly IStringLocalizer<HomeController> _localizer;
5
6    public HomeController(IStringLocalizer<HomeController> localizer)
7    {
8        _localizer = localizer;
9    }
10
11    public IActionResult Index()
12    {
13        decimal price = 1234.56m;
14        // Template comes from resource file, arguments are formatted per culture
15        string message = _localizer["PriceMessage", price];
16        return View(model: message);
17    }
18}

Custom Extension Method

Create a helper for culture-aware interpolation:

csharp
1public static class StringExtensions
2{
3    public static string ToString(
4        this FormattableString formattable,
5        string cultureName)
6    {
7        return formattable.ToString(
8            CultureInfo.GetCultureInfo(cultureName));
9    }
10}
11
12// Usage
13FormattableString msg = $"Total: {amount:C}";
14string german = msg.ToString("de-DE");
15string french = msg.ToString("fr-FR");

C# 10+ Interpolated String Handlers

C# 10 introduced custom interpolated string handlers for performance. They also enable custom culture handling:

csharp
// The compiler converts $"..." to handler calls
// For logging frameworks, this avoids string allocation when logging is disabled
LoggerExtensions.LogInformation(logger, $"Processing order {orderId}");

Common Pitfalls

  • Assuming $"" respects thread culture for text: Only the format specifiers (:C, :d, :N) use culture. The literal text in the template is always the compile-time string.
  • Using $"" for URLs and SQL: Culture-dependent decimal separators break URLs and SQL queries. Always use FormattableString.Invariant() for machine-readable output.
  • Confusing string and FormattableString: string msg = $"..." immediately formats to string. To preserve format+arguments, use FormattableString msg = $"...".
  • Resource files with wrong format indices: If the resource string uses {0} and {1}, you must pass arguments in the same order with string.Format. There is no compile-time checking like $"" provides.
  • Mixed interpolation and concatenation: $"Hello " + $"{name}" creates two separate strings and concatenates. Use $"Hello {name}" in one expression for clarity and performance.

Summary

  • $"..." formats embedded values using CultureInfo.CurrentCulture but the template text is always hardcoded
  • Use FormattableString to format with a specific culture: fs.ToString(culture)
  • Use FormattableString.Invariant() for machine-readable output (URLs, APIs, SQL)
  • For full localization, use resource files (.resx) with string.Format() or IStringLocalizer
  • String interpolation cannot replace the template at runtime — use string.Format with localized templates instead

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.