# String interpolation & format specifiers

> Build a string from literal text and embedded expressions with $"…{expr}…". A hole may carry a .NET format specifier after a colon ({amount:F2}), applied via IFormattable in the current culture — exactly C#.

<!-- id: function-string-interpolation · area: function · stability: stable · html: https://osysharp.com/reference/function/string-interpolation/ -->

## Summary        {#summary}
`$"…{expr}…"` builds a string from literal text and embedded expressions. A hole may carry a .NET **format
specifier** after a colon — `{amount:F2}` — applied via `IFormattable` in the current culture, exactly like
C#'s `string.Format`.

## Signature      {#signature}
```osy syntax
$"literal {expr} more {expr:format} text"
```

## Description    {#description}
- Each `{expr}` hole is converted to its string form and concatenated with the surrounding literal text.
- A `:format` after the expression applies a .NET format string to a formattable value (numbers, dates):
  `{total:F2}` → two decimals, `{n:N0}` → thousands separators, `{ratio:P1}` → a percent, `{when:yyyy-MM-dd}`
  → a date. A hole with no specifier writes the value as its `ToString()` does.
- Every hole formats in the CURRENT culture, as C#'s do: the viewer's `[PreferredCulture]`, else `app.DefaultCulture`,
  else the invariant culture — never the host machine's, so the text is the same on every server and in every browser.
  Under `app.DefaultCulture = "sv-SE"`, `$"{1234.5m}"` is `1234,5`. Write
  `string.Format(CultureInfo.InvariantCulture, "{0}", x)` for text a machine reads ([Culture formatting — ToString(format, culture)](https://osysharp.com/reference/stdlib/culture-formatting/)).
- A **non-formattable** value (a `string`) ignores the specifier and coerces as usual — matching C#'s
  `string.Format`.
- The formatted conversion is also available directly as the 2-arg `Convert.ToString(value, "F2")`.

### Which other interpolated forms are there?   {#forms}
All of C#'s forms: `$@"…"` (or `@$"…"`) keeps backslashes as written and may span lines; `$"""…"""` is a raw
interpolated string (no escapes, quotes written plainly); `{x,5}` right-aligns the formatted value in five characters
and `{x,-5}` left-aligns it, combining with a format as `{x,6:D4}`. A hole may break across lines.

```osy title="path, raw text, a padded column" test app=interpolation-forms
string Row(int qty, string sku) => $@"C:\stock\{sku}" + $"""  "{sku}" """ + $"[{qty,5}][{qty,-5}][{qty,6:D4}]";
```

## Examples       {#examples}
```osy title="format specifiers" test app=interpolation-format
string Money(decimal amount) { return $"Total: {amount:F2}"; }   // "Total: 1234.50"
string Thousands(int n) { return $"{n:N0}"; }                    // "1,234,567"
string Percent(decimal ratio) { return $"{ratio:P1}"; }          // "12.3 %"
string Plain(int n) { return $"n = {n}"; }                       // "n = 5" (no specifier)
string Direct(decimal d) { return Convert.ToString(d, "F3"); }   // "3.142"
```

## See also       {#see-also}
- [Numeric types & literal suffixes](https://osysharp.com/reference/function/numeric-literals/) — the numeric values you format
- [Convert](https://osysharp.com/reference/function/convert/) — `Convert.ToString` and the coercion builtins
