보간 문자열을 그대로 문자열로 바꾸지 말고 FormattableString으로 넘기면, 문화권(Culture)에 맞게 숫자/날짜/통화 형식을 안전하게 적용하고 리소스 기반 번역도 자연스럽게 처리할 수 있습니다.
1. FormattableString이란?
$"..." 보간 문자열을 FormattableString 타입으로 받으면, 내부의 원본 서식 문자열과 인자 목록을 분리해 가지고 있습니다. 따라서 나중에 원하는 Culture로 포맷팅하거나, 리소스 템플릿에 동일한 인자를 적용할 수 있습니다.
using System;
using System.Globalization;
decimal price = 12345.67m;
DateTime date = new DateTime(2026, 5, 1);
FormattableString fs = $"총액 {price:C} - 날짜 {date:D}"; // 포맷 지시자 유지
var ko = new CultureInfo("ko-KR");
var en = new CultureInfo("en-US");
string koText = fs.ToString(ko); // "총액 ₩12,345.67 - 날짜 2026년 5월 1일 금요일"
string enText = fs.ToString(en); // "총액 $12,345.67 - 날짜 Friday, May 1, 2026"2. CurrentCulture / Invariant 간편 사용
정밀한 숫자 로그 등은 InvariantCulture로, 사용자 노출 텍스트는 CurrentCulture로 처리하는 것이 안전합니다.
using System;
using System.Globalization;
var lat = 37.5665;
var lng = 126.9780;
// 현재 스레드 Culture로 포맷
string userMessage = FormattableString.CurrentCulture($"위도 {lat:F4}, 경도 {lng:F4}");
// 불변(파싱/로그/키) 용도
string logLine = FormattableString.Invariant($"lat={lat:F6}; lng={lng:F6}");3. 리소스 기반 다국어: 템플릿 키 + 인자 재사용
FormattableString.Format은 "Hello {0}, total {1:C}" 형태의 템플릿을 제공합니다. 이 값을 리소스 키로 쓰고, 문화권별 템플릿을 찾아 동일한 인자에 적용하면 번역과 포맷이 동시에 처리됩니다.
using System;
using System.Collections.Generic;
using System.Globalization;
public sealed class SimpleLocalizer
{
private readonly IDictionary<string, string> _resources;
private readonly CultureInfo _culture;
public SimpleLocalizer(IDictionary<string, string> resources, CultureInfo culture)
{
_resources = resources;
_culture = culture;
}
// IStringLocalizer 패턴과 유사한 인덱서
public string this[FormattableString fs]
{
get
{
string key = fs.Format; // 예: "Total {0:C} on {1:D}"
object[] args = fs.GetArguments(); // 인자 배열
if (!_resources.TryGetValue(key, out var template))
template = key; // 리소스 없으면 원문 키 사용
return string.Format(_culture, template, args);
}
}
}
// 사용 예시
var fr = new CultureInfo("fr-FR");
var resourcesFr = new Dictionary<string, string>
{
{ "Total {0:C} on {1:D}", "Total {0:C} le {1:D}" }
};
var locFr = new SimpleLocalizer(resourcesFr, fr);
var total = 1234.5m;
var when = new DateTime(2026, 5, 1);
string text = locFr[$"Total {total:C} on {when:D}"]; // "Total 1 234,50 € le vendredi 1 mai 2026"핵심은 보간 문자열을 즉시 문자열로 만들지 않고, FormattableString 형태로 전달하는 것입니다. 이렇게 하면 템플릿 번역과 인자 포맷팅을 문화권에 맞게 한 번에 처리합니다. ASP.NET Core의 IStringLocalizer도 동일한 패턴을 지원합니다.
4. 숫자/날짜/통화 예제
using System;
using System.Globalization;
var de = new CultureInfo("de-DE");
var jp = new CultureInfo("ja-JP");
int count = 12000;
decimal money = 98765.43m;
DateTime dt = new DateTime(2026, 12, 24, 18, 30, 0);
FormattableString fs = $"개수 {count:N0}, 금액 {money:C}, 시간 {dt:F}";
Console.WriteLine(fs.ToString(de)); // "개수 12.000, 금액 98.765,43 €, 시간 Donnerstag, 24. Dezember 2026 18:30:00"
Console.WriteLine(fs.ToString(jp)); // "개수 12,000, 金額 ¥98,765, 時間 2026年12月24日 18:30:00"5. 실전 팁/주의사항
- 인자를 미리 ToString()으로 포맷하지 마세요. 원본 타입(DateTime, decimal 등) 그대로 넘겨야 문화권별 포맷이 반영됩니다.
- 통화는 C, 숫자는 N/P, 날짜는 d/D/f/F 등 서식 지정자를 적극 활용하세요.
- 리소스 키로 영어 문장을 그대로 쓰는 방식은 간편하지만, 대형 프로젝트에서는 의미 키(예: Errors.Required)로 운영하는 것을 고려하세요. 이때도 값은 "{0} is required"처럼 자리표시자를 유지합니다.
- 로그/식별자/직렬화는 InvariantCulture, 사용자 화면은 CurrentUICulture/CurrentCulture를 사용하세요.
- ASP.NET Core에서는 IStringLocalizer[$"..."] 형태로 FormattableString을 바로 넘기면 리소스 템플릿과 인자 포맷이 자동 처리됩니다.
6. 미니 유틸리티(확장 메서드)
using System;
using System.Globalization;
public static class FormattableStringExtensions
{
public static string Localize(this FormattableString fs, IFormatProvider provider)
=> fs.ToString(provider);
public static string Invariant(this FormattableString fs)
=> FormattableString.Invariant(fs);
}
// 사용
var ko = new CultureInfo("ko-KR");
string s = $"가격 {1234.56m:C}".Localize(ko);
string inv = $"x={Math.PI:F6}".Invariant();정리: 보간 문자열을 FormattableString으로 다루면, 번역(리소스 템플릿)과 지역화 포맷팅을 분리·자동화할 수 있습니다. UI는 CurrentCulture, 데이터/로그는 Invariant로 구분해 안전하게 다국어를 지원하세요.
'C#' 카테고리의 다른 글
| C# 암호화 해시(Checksum) 생성과 비교 (0) | 2026.06.01 |
|---|---|
| C# 종속성 주입(DI) 패턴과 IServiceProvider 활용 (0) | 2026.05.30 |
| C# AttributeTargets로 애트리뷰트 적용 범위 제어 (0) | 2026.05.29 |
| C# BinarySearch와 검색 알고리즘 성능 비교 (0) | 2026.05.29 |
| C# yield return을 이용한 지연 실행(Deferred Execution) (0) | 2026.05.29 |