globalization-and-culturelisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# Globalization and Culture
## The default is a landmine
`double.Parse`, `decimal.ToString`, `DateTime.Parse`, `string.Format`, and interpolation all default to `CultureInfo.CurrentCulture` - the culture of the thread, which in a server means "whatever the OS image or a stray configuration set". Code that works on the developer's en-US machine and corrupts data on a de-DE server:
```csharp
// WRONG: on a German-culture server, "1.5" parses as 15 (dot is a thousands separator)
var price = decimal.Parse(priceText);
// WRONG: emits "1,5" into a CSV/JSON/SQL string that expects "1.5"
var s = price.ToString();
// RIGHT: machine-to-machine data is invariant, always
var price = decimal.Parse(priceText, CultureInfo.InvariantCulture);
var s = price.ToString(CultureInfo.InvariantCulture);
```
The dividing rule: **data crossing a machine boundary** (files, protocols, URLs, database strings, config, logs meant for parsing) is `InvariantCulture`; **text rendered for a human eye** is the user's culture - explicitly resolved, not whatever the thread has. Any Parse/ToString/Format of numbers or dates with no `IFormatProvider` argument is a review question; enable CA1305 (specify IFormatProvider) as at least a warning to make the omissions visible.
Interpolated strings crossing machine boundaries: `FormattableString.Invariant($"page={page}&price={price}")` or `string.Create(CultureInfo.InvariantCulture, $"...")` - a bare `$"..."` building a URL formats the decimal with the thread culture