Global Formatting Made Easy with IFormattable in .NET 8 for Enterprise Applications
In today’s interconnected world, enterprise applications must cater to diverse user bases spanning multiple regions and cultures. One critical aspect often overlooked is the presentation of financial data, where nuances in formatting can greatly impact user experience and data accuracy.
In this blog post, we’ll explore how the Money class, implemented in .NET 8 and leveraging the IFormattable interface, revolutionizes global financial data presentation, ensuring consistency and cultural relevance across various locales.
Understanding the Money Class
The Money class serves as a cornerstone for representing monetary values within enterprise applications. It encapsulates both the amount and currency code, providing a robust and immutable data structure for financial transactions.
By utilizing the record feature introduced in .NET 8, the Money class benefits from built-in immutability, value-based equality, and straightforward syntax for object creation and manipulation.
public record Money(decimal Amount, string CurrencyCode) : IFormattable
{
private static MoneyFormatter Formatter { get; } = new();
public string ToString(string? format, IFormatProvider? formatProvider) =>
MoneyFormatter.Format(this, format, formatProvider);
}
Cultural Compatibility with IFormattable
At the heart of the Money class lies the implementation of the IFormattable interface. This interface allows the Money objects to be formatted into string representations tailored to the cultural preferences of users worldwide.
Whether it's displaying currency symbols, decimal separators, or thousands grouping, the IFormattable interface ensures that monetary values are presented accurately and intelligibly across different regions.
public sealed class MoneyFormatter
{
private static ConcurrentDictionary<string, string> CurrencySymbolsCache { get; } = new();
public static string Format(Money money, string? format, IFormatProvider? formatProvider)
{
formatProvider ??= CultureInfo.CurrentCulture;
format = EnsureValidFormat(format);
return format.ToUpperInvariant() switch
{
"N" => FormatAsNumber(money, formatProvider),
"C" => FormatAsCurrency(money, formatProvider),
_ => throw new FormatException($"The {format} format string is not supported.")
};
}
// ...
}
Testing with xUnit and FluentAssertions
To validate the functionality of the Money class and MoneyFormatter, comprehensive unit tests are imperative. Leveraging the xUnit testing framework along with FluentAssertions, we rigorously test the formatting logic across various cultural scenarios.
using System.Globalization;
using Xunit;
using FluentAssertions;
public class MoneyTests
{
[Theory]
[InlineData(1234.56, "USD", "en-US", "1,234.56 USD")]
[InlineData(1234.56, "EUR", "de-DE", "1.234,56 EUR")]
public void Money_ToString_FormatsCorrectlyForGivenCulture(decimal amount, string currencyCode, string culture, string expected)
{
// Arrange
var money = new Money(amount, currencyCode);
var cultureInfo = new CultureInfo(culture);
// Act
var result = money.ToString("N", cultureInfo);
// Assert
result.Should().Be(expected);
}
}
Conclusion
In an increasingly globalized business landscape, the ability to present financial data accurately and culturally relevantly is paramount. Through the implementation of the Money class and MoneyFormatter utility, our enterprise applications can now seamlessly accommodate diverse user bases worldwide.
By embracing the principles of cultural compatibility and meticulous testing, we pave the way for enhanced user experiences and empowered decision-making in the realm of global finance.
If you looking for source code, please check next link: https://gist.github.com/admir-live/63ed3a6e6b7fca101a3f5df4b053884b
Cheers! 👋
Follow me on LinkedIn: www.linkedin.com/comm/mynetwork/discovery-see-all?usecase=PEOPLE_FOLLOWS&followMember=admir-live